1.0 Scope
This page states which parts of the LLM4S API are safe to build on ahead of a 1.0 freeze.
Until 0.4.1 all of it shipped in one core Maven artifact of roughly 84k lines — agent runtime, RAG, MCP, speech, image, knowledge graph, and eleven provider clients together. There was no way to tell from the artifact alone which parts of that surface are meant to be a long-term contract and which are still moving fast.
The modularisation programme tracked in #1126 split it up. Rows marked carved below live in their target module; any other row still ships inside llm4s-core, and its target-module column says where it is heading, not where it lives today.
The carves, all published from 0.5.0:
| Slice | Modules carved | Status |
|---|---|---|
| 1 | llm4s-rag, llm4s-knowledgegraph |
published in 0.5.0 |
| 2 | llm4s-memory, llm4s-memory-postgres |
published in 0.5.0 |
| 3 | llm4s-mcp, llm4s-media, llm4s-image, llm4s-speech |
published in 0.5.0 |
| 4 | none - the provider registration SPI (ProviderDescriptor, discovered through META-INF/services) that slice 5’s modules register through |
published in 0.5.0 |
| 5 | llm4s-ollama, llm4s-gemini, llm4s-anthropic, llm4s-openai, llm4s-openai-compatible (with Mistral and Cohere), llm4s-voyage - llm4s-core now holds no provider client |
published in 0.5.0 |
| 6 | llm4s-observability (Langfuse, the trace collector, CostTracker); OpenTelemetryConfig joins the existing llm4s-observability-otel; llm4s-observability-prometheus (Prometheus). The tracing and metrics contracts stay in llm4s-core, which declares no observability dependency |
published in 0.5.0 |
| 7 | llm4s-agent-tools (the built-in tools and their config), llm4s-agent (agent, assistant) |
published in 0.5.0 |
0.5.0 is the first release to publish separate module artifacts (0.4.x was a single llm4s-core; 0.4.0 was the artifact rename only, #1141). It sets no MiMa baseline: that comes at a pre-1.0 release, version to be decided, once the designs are final (#1281). Until then nothing is frozen: “Frozen at 1.0” is the tier a module will hold, not a constraint on changing it now.
Maturity Legend
These definitions are shared with the Roadmap so the two pages agree.
| Status | Meaning |
|---|---|
| Frozen at 1.0 | Source and binary compatible within the 1.x series once published under its target module. This is the compatibility promise 1.0 makes. |
| Beta | Implemented and usable, but API, provider behavior, or docs still need hardening before v1.0. |
| Experimental | Useful prototype or advanced feature; expect changes. |
| Planned | Roadmap item, design, issue, or PR queue item; not a stable user contract. |
Package Map
Every top-level package under modules/core/src/main/scala/org/llm4s/, its target module, and its tier.
| Package | Target module | Tier |
|---|---|---|
types |
llm4s-core |
Frozen at 1.0 |
error |
llm4s-core |
Frozen at 1.0 |
config |
llm4s-core |
Frozen at 1.0 |
model |
llm4s-core |
Frozen at 1.0 |
toolapi — the tool API: ToolFunction, ToolRegistry, schemas, execution |
llm4s-core |
Frozen at 1.0 |
toolapi/builtin, toolapi/tools — the built-in tools (core utilities, filesystem, HTTP, shell, Brave/DuckDuckGo/Exa search) and ToolsConfigLoader — carved |
llm4s-agent-tools |
Beta |
context |
llm4s-core |
Frozen at 1.0 |
util |
llm4s-core |
Frozen at 1.0 |
syntax |
llm4s-core |
Frozen at 1.0 |
identity |
llm4s-core |
Frozen at 1.0 |
resource |
llm4s-core |
Frozen at 1.0 |
core |
llm4s-core |
Frozen at 1.0 |
http |
llm4s-core |
Frozen at 1.0 |
security |
llm4s-core |
Frozen at 1.0 |
llmconnect (API only — see provider split below) |
llm4s-core |
Frozen at 1.0 |
reliability |
llm4s-core |
Frozen at 1.0 |
agent (excludes agent/memory) — carved |
llm4s-agent |
Frozen at 1.0 |
assistant — carved |
llm4s-agent |
Beta |
trace — the contract: Tracing, TraceEvent, TracingComposer, TracingMode, the trace/spi TracingBackend SPI, NoOpTracing, ConsoleTracing, and TracingSettings (in llmconnect/config) |
llm4s-core |
Frozen at 1.0 |
metrics — the contract: MetricsCollector |
llm4s-core |
Frozen at 1.0 |
trace — Langfuse (LangfuseTracing, its batch sender and TracingBackend), TraceCollectorTracing, trace/model, trace/store; metrics — CostTracker — carved |
llm4s-observability |
Beta |
trace — OpenTelemetry (OpenTelemetryTracing, OpenTelemetryConfig) — carved |
llm4s-observability-otel (modules/trace-opentelemetry) |
Beta |
metrics — Prometheus (PrometheusMetrics, PrometheusEndpoint, MetricsConfigLoader) — carved |
llm4s-observability-prometheus |
Beta |
metrics — Micrometer (MicrometerMetrics), a MetricsCollector over a MeterRegistry — new |
llm4s-observability-micrometer |
Beta |
llmconnect/provider — OpenAI, Azure and Requesty (the providers sharing OpenAIClient) — carved |
llm4s-openai |
Frozen at 1.0 |
llmconnect/auth/azure — AzureEntraId: Microsoft Entra ID tokens (DefaultAzureCredential, any TokenCredential) for the azure provider’s authentication = "entra-id" (#2274) — new |
llm4s-azure-identity (modules/providers/azure-identity); carries Azure’s azure-identity |
Experimental |
llmconnect/provider — OpenRouter, DeepSeek, Z.ai and the generic openai-compatible provider, on one SDK-free OpenAICompatibleClient — carved |
llm4s-openai-compatible |
Frozen at 1.0 |
llmconnect/provider — Anthropic — carved |
llm4s-anthropic |
Frozen at 1.0 |
llmconnect/provider — Gemini (and Vertex AI) — carved |
llm4s-gemini |
Frozen at 1.0 |
llmconnect/provider — Ollama — carved |
llm4s-ollama |
Frozen at 1.0 |
llmconnect/provider — Mistral and Cohere, as dialects on OpenAICompatibleClient — carved |
llm4s-openai-compatible |
Beta |
llmconnect/provider — xAI (Grok), as a dialect on OpenAICompatibleClient (#1023) — new |
llm4s-openai-compatible |
Beta |
llmconnect/provider — Voyage AI embeddings — carved |
llm4s-voyage (modules/providers/voyage) |
Beta |
llmconnect/provider — AWS Bedrock (Converse, ConverseStream) — new |
llm4s-bedrock (modules/providers/bedrock) |
Beta |
llmconnect/provider — Jina AI embeddings — new |
llm4s-jina (modules/providers/jina) |
Beta |
llmconnect/provider — Cohere embeddings, on Cohere’s native /v2/embed (Cohere chat is a dialect in llm4s-openai-compatible) — new |
llm4s-cohere (modules/providers/cohere) |
Beta |
llmconnect/provider — IBM watsonx.ai, on the chat endpoints (never run against the live service: verification against it is still open in #1314) — new |
llm4s-watsonx (modules/providers/watsonx) |
Beta |
llmconnect/provider — other community providers |
llm4s-openai-compatible dialects, or modules/providers/<name> |
Beta |
jev — typed client for TypeSafe’s Jev decision model (Noul, Choice, Score); not a chat provider — new |
llm4s-jev (modules/providers/jev) |
Beta |
testkit — the checks a provider module’s Llm4s<Name>ModuleSpec makes (ProviderModuleChecks, ProviderTestConfig, CredentialsRoundTrip, LocalProviderTestServer), formerly unpublished helpers in core’s test sources — new |
llm4s-provider-testkit (modules/provider-testkit), a test-scope dependency; requires JDK 21 (virtual threads) |
Beta |
agent/graph/sqlite — SqliteCheckpointer, the first durable Checkpointer: threads, events and run claims in one SQLite file that survives a restart and that several processes on one host can share — new |
llm4s-agent-checkpoint-sqlite (modules/agent-checkpoint-sqlite); carries the SQLite JDBC driver |
Beta |
agent/graph/postgres — PostgresCheckpointer: threads, events and run claims in a PostgreSQL schema that runtimes on several machines share, claims and retention by the database’s clock — new |
llm4s-agent-checkpoint-postgres (modules/agent-checkpoint-postgres); carries the PostgreSQL JDBC driver and HikariCP |
Beta |
schema — SchemaOf, a JSON schema derived from a case class with derives, and completeStructuredOf; builds core’s ObjectSchema and adds no dependency (Map, recursive types and sealed hierarchies with fields are refused at compile time; a recursion through a hand-written given that derives its type fails on first use) — new |
llm4s-schema-derivation (modules/schema-derivation) |
Experimental |
testkit — a scriptable fake LLMClient for the tests of code that uses llm4s (ScriptedLLMClient, Reply, RecordedCall, UnscriptedCallError) — new |
llm4s-testkit (modules/testkit), a test-scope dependency |
Beta |
doctor — the setup checker: DoctorCli (its options, text report, JSON output and exit codes are a user contract), Doctor.run and its report, run on the application’s own classpath — new |
llm4s-doctor (modules/doctor), usually a test-scope dependency |
Experimental |
agent/harness/memory — SemanticMemoryMiddleware: recall before a turn, recording and consolidation after it, one store per tenant, each write a replayable effect on a TransactionalMemoryStore — new |
llm4s-agent-memory (modules/agent-memory); depends on llm4s-agent and llm4s-memory |
Experimental |
agent/testkit — CheckpointerContract, the contract every Checkpointer must pass (commits, event numbering, compaction, run-claim leases and fencing, two runtimes over one store), and ManualClock — new |
llm4s-agent-testkit (modules/agent-testkit), a test-scope dependency; requires JDK 21 (virtual threads) |
Beta |
| build — the bill of materials that pins every published artifact to one version (no code) — new | llm4s-bom (modules/bom) |
Beta |
rag, vectorstore, chunking, reranker, eval — carved |
llm4s-rag |
Beta |
extract (consolidated from rag/extract + llmconnect/extractors) and rag/embed (from llmconnect/encoding) — carved |
llm4s-rag |
Beta |
vectorstore.elasticsearch — ElasticsearchVectorStore, a VectorStore over the Elasticsearch REST API (dense_vector kNN, metadata filters, _bulk), with ElasticsearchConfig and ElasticsearchVectorStoreConfigLoader; no Elasticsearch client library — new |
llm4s-vectorstore-elasticsearch (modules/vectorstore-elasticsearch) |
Experimental |
vectorstore.opensearch — OpenSearchVectorStore, the OpenSearch dialect of the same store (knn_vector with the k-NN plugin, the knn query, efficient or post-filtering per engine), with OpenSearchConfig and OpenSearchVectorStoreConfigLoader — new |
llm4s-vectorstore-elasticsearch (modules/vectorstore-elasticsearch) |
Experimental |
vectorstore.redis — RedisVectorStore, a VectorStore over Redis Query Engine (Redis Stack, Redis 8+) or Valkey’s valkey-search (FT.CREATE vector index over hashes, KNN FT.SEARCH, metadata filters as TAG clauses), with RedisConfig and RedisVectorStoreConfigLoader; Jedis, kept out of the API — new |
llm4s-vectorstore-redis (modules/vectorstore-redis) |
Experimental |
agent/memory (excluding PostgresMemoryStore) — carved |
llm4s-memory |
Beta |
agent/memory/PostgresMemoryStore — carved |
llm4s-memory-postgres |
Beta |
mcp - carved |
llm4s-mcp |
Beta |
knowledgegraph/neo4j — the Neo4j graph store (Neo4jGraphStore) |
llm4s-knowledgegraph-neo4j (modules/knowledgegraph-neo4j) |
Experimental |
workspace, shared, codegen, agent/harness/workspace — containerised workspace execution: the client (ContainerisedWorkspace, the harness backends WorkspaceFileBackend and WorkspaceSandboxBackend, the code-generation worker) and the wire protocol it speaks to the runner image (WorkspaceAgentProtocol, WorkspaceAgentInterface) |
llm4s-workspace-client, llm4s-workspace-shared (modules/workspace) |
Experimental |
media - new |
llm4s-media |
Beta |
speech - carved |
llm4s-speech |
Experimental |
imagegeneration, imageprocessing - carved |
llm4s-image |
Experimental |
knowledgegraph — carved (knowledgegraph/graphrag ships in llm4s-rag) |
llm4s-knowledgegraph |
Experimental |
javaapi — the Java facade (Llm4s, JLlmClient, JAgent, AgentStream, AgentStreamListener, StreamEvents, Answer, PendingInterrupt, InterruptKind, BreakpointPhase, ConversationBuilder, LlmResult, LlmException) — new |
llm4s-java-api (modules/java-api) |
Beta |
javaapi.rag — the Java facade for RAG (JRag, JRagBuilder, JRagStore, JRagAnswer, JRagSource) over llm4s-rag, in a module of its own so a chat-only Java user does not receive llm4s-rag’s dependencies — new |
llm4s-java-rag (modules/java-rag) |
Experimental |
javaapi.reactor — Reactor for the Java facade (Streams.toFlux: a streamed reply as a Flux<String>), in a module of its own so llm4s-java-api does not bring reactor-core — new |
llm4s-reactor (modules/reactor) |
Experimental |
spring — Spring Boot auto-configuration (Llm4sAutoConfiguration, Llm4sProperties, LLM4STemplate, LlmHealthIndicator) — new |
llm4s-spring-boot-starter (modules/spring-boot-starter) |
Beta |
effect.cats — LLMClientIO, AgentIO (cats-effect 3, fs2) — new |
llm4s-effect (modules/llm4s-effect) |
Beta |
zio — LLMClientZ, AgentZ (ZIO 2, ZIO Streams) — new |
llm4s-zio (modules/llm4s-zio) |
Beta |
kotlin — coroutine API (LLMClientKt, AgentKt, AgentStreamItem); a separate Gradle build, not part of sbt or the MiMa baseline, published with each release at the release’s version (no _3 suffix; managed by llm4s-bom) — new |
llm4s-kotlin-api (modules/kotlin-api) |
Experimental |
pekko — LLMClientPekko, AgentPekko (Apache Pekko Streams) — new |
llm4s-pekko (modules/llm4s-pekko) |
Beta |
Notes:
llmconnect/providerinllm4s-corenow holds only the plumbing every provider module shares - cost estimation, HTTP error mapping, metrics recording, theEmbeddingProvidertrait, exchange recording - and no client. With the registration SPI inllmconnect/spifrom slice 4 and the streaming and request helpers providers build on, it is a frozen provider-author SPI, public so that a provider can be published outside this repository: a provider is aProviderDescriptorlisted in its own module’sLlm4sProviderModule, so adding one adds a dependency and edits nothing in core. See Writing a provider. Helpers specific to one wire format live with it -ResponseFormatMapperandToolCallDeserializerinllm4s-openai-compatible, OpenAI’s model-name rules inllm4s-openai- and helpers only llm4s’s own modules use, such asProviderResultOps, areprivate[llm4s]. Vendor API keys (llm4s.credentials.<provider>.apiKey) are read by core’sconfig, but each key is bound in its provider module’sreference.conf, beside the descriptor that uses it.- The tracing and metrics contracts stay in
llm4s-core, and they are what is frozen:llmconnect, the agent runtime and every provider module trace and record metrics through them alone. The integrations are separate modules that plug into that contract - a tracing backend is aTracingBackendregistered inMETA-INF/services, discovered byTRACING_MODE, the way a provider is aProviderDescriptor. They are Beta, like every integration module other than the frozen provider modules: Langfuse’s ingestion format and OpenTelemetry’s SDK move on their vendors’ schedule, not ours, and a new tracing backend should be a new module rather than a change to the frozen surface. See the migration guide. - Rows marked carved already live in their target sbt module. Their package names are unchanged, so this is a build-file change for users, not an import rewrite — with one exception,
org.llm4s.extract, described in the migration guide. org.llm4s.mediais a new package, not a carve. It consolidates the three overlapping image-format enumerationsllm4s-corehad accumulated, so thatllm4s-imageandllm4s-speechcarve as pure file moves rather than moves plus a vocabulary change. It is the one part of slice 3 with a source break, described in the migration guide; it is deliberate and taken now rather than after 1.0.org.llm4s.extractis a new package name, not a rename of an existing one — see Slice 1 for why the two extractors were consolidated rather than just moved.org.llm4s.knowledgegraph.graphragkeeps its package name but ships inllm4s-rag, notllm4s-knowledgegraph.GraphRAGandvectorstorereferenced each other, which made the two modules inseparable; moving the one file that reaches intovectorstorebroke the cycle. Package names track the API; module boundaries track the dependency graph, and here they disagree.llm4s-memorysplits in two.PostgresMemoryStorewas the only file inagent/memorythat needed a connection pool and a server-side driver, so it ships asllm4s-memory-postgres; keeping it with the rest would mean anyone using agent memory at all inherits HikariCP and the Postgres JDBC driver.llm4s-memoryitself carries only sqlite-jdbc, for the two file-backed stores.org.llm4s.vectorstore.PostgresVectorHelpersships inllm4s-core, not inllm4s-ragwith the rest oforg.llm4s.vectorstore. It is a pureArray[Float]⇄ pgvector-text codec with no JDBC types in it, and it has consumers in two modules that must not depend on each other (llm4s-ragandllm4s-memory-postgres); the module they share is core. This is the same package-versus-module disagreement asgraphrag, in the other direction.- Vertex AI ships in
llm4s-gemini. It only calls Google’s Gemini models, in the same JSON format as the Gemini API, and differs in endpoint and OAuth authentication rather than in dependencies - so bundling costs Gemini-API users nothing, whereas splitting it out after release would be a breaking move. See the migration guide.
What Frozen means
- Source and binary compatible within 1.x. Once the pre-1.0 baseline is set,
mimaPreviousArtifactsenforces binary compatibility on every Frozen module for all subsequent 1.x releases. - Deprecate before removing. A Frozen API is only removed after a deprecation cycle, never dropped outright in a minor release. See the Compatibility and Deprecation Policy.
- Beta and Experimental can move faster. They may change in a minor release, but a migration note ships with the change in the same release’s CHANGELOG.
Scala and JDK support
1.0 targets Scala 3 only (3.7.1). Scala 2.13 support is deferred to post-1.0 and, if it happens, would target the frozen spine (llm4s-core, llm4s-agent, and the frozen provider modules) rather than the full tree. The tracing and metrics contracts are part of llm4s-core; the observability integration modules are not in the spine. JDK 21 is used in CI; the JDK each artifact needs is below.
See #1126 for the reasoning behind the Scala-3-only decision.
JDK. The minimum is JDK 21 for every artifact, and CI runs only JDK 21. This is the one support statement: the README and the other pages link here, and scripts/check-doc-support.sh fails CI when a page states another floor. For llm4s-core and everything that depends on it, which is every artifact but two, JDK 21 is a hard requirement for the reasons in the table, and every artifact is compiled to JDK 21 bytecode, so an older JDK refuses it at once with UnsupportedClassVersionError.
| Artifacts | Minimum JDK | Tested on | Why |
|---|---|---|---|
llm4s-agent, and the modules that depend on it: llm4s-java-api, llm4s-java-rag, llm4s-reactor, llm4s-kotlin-api, llm4s-spring-boot-starter, llm4s-effect, llm4s-zio, llm4s-agent-testkit |
21 | 21 | llm4s-agent depends on Ox, whose jars are compiled for JDK 21 (class-file version 65), and it starts virtual threads (Thread.ofVirtual). The Java and Kotlin facades start their own as well: AgentStream in llm4s-java-api, and AgentKt’s stream completion in llm4s-kotlin-api (#1678), and llm4s-agent-testkit’s CheckpointerContract runs its cases on virtual threads |
llm4s-core and every module that depends on it |
21 | 21 | ToolRegistry starts a virtual thread (Thread.ofVirtual), a JDK 21 API, so llm4s-core does not compile against an older JDK |
llm4s-provider-testkit |
21 | 21 | its interruption checks and LocalProviderTestServer run on virtual threads (#1582) |
llm4s-media, llm4s-workspace-shared |
21 | 21 | neither depends on llm4s-core or uses a JDK 21 API, but they are compiled to JDK 21 bytecode like every other artifact, and only JDK 21 is tested |
Bytecode. The build pins the bytecode level to the minimum (Common.javaRelease in project/Dependencies.scala): the Scala compiler gets -java-output-version 21 and javac gets --release 21, so every class is class-file version 65 whichever JDK runs the build, and the build cannot use an API newer than JDK 21. The Gradle builds match it: llm4s-kotlin-api targets JvmTarget.JVM_21 with release 21 for Java, and the Gradle Java sample compiles with release 21. scripts/check-doc-support.sh fails CI when a project or a Gradle build targets another level, and JavaReleaseSpec in llm4s-core, llm4s-agent and llm4s-java-api reads the compiled classes’ version. Up to 0.5.1 nothing pinned it: the Scala classes were version 52 and only the few Java sources (@Stable, @Experimental, and llm4s-java-api’s enums and value types) were 65, built on JDK 21, so the bytecode did not show the floor.
Why not JDK 17 for some artifacts (#1493). A lower floor for a chat-only subset, with Ox made optional or JAgent split from llm4s-java-api, was considered and rejected: llm4s-core itself starts a virtual thread (ToolRegistry), the provider modules and llm4s-java-api depend on core, and the cancellation contract (interrupts that close a blocking socket read) is defined on JDK 21 virtual threads. A JDK 17 floor would mean a second code path for core and every provider, and a second JDK in CI, for a pre-1.0 library whose agent runtime needs 21 regardless.
Programme status
For current progress against this target structure, see the tracking issue #1126 and its slice sub-issues.