Read ../lm15-contract/playbooks/port.md first. This file is the map of the
Java code and the conventions every module follows so the port reads as one
family member (playbooks/api-family.md).
| Package | What |
|---|---|
dev.lm15.json |
JsonValue sealed sum with int/float fidelity (JsonInt / JsonFloat), order-keeping JsonObject, Json.parse/write/obj/arr, JsonBuilder (put, putIfPresent, putIfNonEmpty). Every opaque payload is a JsonObject. |
dev.lm15.types |
Canonical types as records; sealed Part / Delta / StreamEvent / LiveClientEvent / LiveServerEvent; vocabularies as enums with wire() / fromWire(); ValidationException (ValueError / TypeError). Absent = null. Lists are immutable copies. |
dev.lm15.serde.Canonical |
toJson(x) / xFromJson(JsonObject) per type + Canonical.kind(name) for the vet. Read helpers readString/readInt/readDouble/readBool/readObject/readArray/readStrings implement the Number rule at the boundary. |
dev.lm15.errors |
LM15Error tree with the family's class names; ErrorMeta(provider, providerCode, status, requestId, retryAfter); Errors.mapHttpError, Errors.attachMetadata. |
dev.lm15.auth |
Credential sum (ApiKey/BearerToken/AwsCredentials), CredentialProvider, AccessPolicy + the Access table (AUTH-10), Access.selectScheme / authHeader, AuthChain (shared client/doctor selection), CredentialStores (low-level stored-login helpers), Rfc3339. |
dev.lm15.wire |
WireRequest (what a dialect builds: path under the base URL, params, headers, JSON or raw body, endpoint, model), TransportRequest (what goes on the wire), BuildContext (provider, policy, settings, compat, baseUrl, wire model, accountId), Wire.emit (auth header + host rewrites + SigV4), Wire.pathId (MAP-11), Wire.normalize (the vet's request shape), Clock. |
dev.lm15.cloud |
Hosts (settings resolution, base-URL rendering, host rewrites), SigV4 (seam; the signer lands with the cloud module). |
dev.lm15.compat |
Compat marker interface; each dialect defines its own compat record + preset table; PresetAddresses (a preset name supplies its server's address). |
dev.lm15.dialects |
Dialect interface (build / parseResponse / parseStreamEvent / normalizeError + every surface hook, defaults refuse), Dialects registry (reflective lookup by key: openai-responses, openai-chat, xai, anthropic, gemini), Common (shared helpers: partsToText, mediaDataUri, checkToolResultMedia, partToOpenAIInput, toolResultOutputOpenAI, openaiTokenLogprobs, isoUtc, multipartFormBody, modelInfosFromEntries, recordUnmapped, EFFORT_THINKING_BUDGETS). |
dev.lm15.ProviderLM |
The bound adapter: buildRequest, parseResponse, replayStream, normalizeError, complete, stream, responseStream, every surface driver, ProviderLM.builder(definition). |
dev.lm15.registry |
Registry.PROVIDERS (the 31 definitions), ProviderDefinition, DialectId. |
dev.lm15.stream |
Coalescer (MAP-3/4), StreamAccumulator (MAP-9), Streams.materialize, ResponseStream. |
dev.lm15.sse |
Sse.parse(bytes) / Sse.iterate(stream) → SseEvent(event, data). |
dev.lm15.transport |
HttpResponse, Transport, HttpTransport (java.net.http). |
dev.lm15.jobs |
BatchJob, VideoJob, WaitOptions. |
dev.lm15.live |
LiveCodec (setupFrames / encode / decode), LiveSession, Turn. |
dev.lm15.vet |
The shim: Main (framing), AdapterOps (build/parse ops), AuthOps, RouterOps, SurfaceDump. |
- A dialect is a stateless class implementing
Dialect, one per package:dev.lm15.dialects.openai.OpenAIResponsesDialect,dev.lm15.dialects.openaichat.OpenAIChatDialect(+XaiDialect),dev.lm15.dialects.anthropic.AnthropicDialect,dev.lm15.dialects.gemini.GeminiDialect.Dialectsfinds them reflectively by those exact class names; a public no-arg constructor is required. build()returns aWireRequestwithpathunder the base URL (e.g./messages,/chat/completions,/responses,/models/{model}:generateContent), setsendpoint(messages,chat/completions,responses,generateContent) andmodel(the wire model) so cloud hosts can rewrite the path; policy static headers (cx.policy().headers()) are merged by the dialect's own rule. Never add the credential header —Wire.emitdoes.- Wire bodies are
JsonObjects built withJson.obj(...)/JsonBuilderin the reference's key order (a SigV4 signature covers the bytes; goldens compare parsed JSON, but keep the order anyway). - Numbers:
JsonInt.of(n)for ints,new JsonFloat(d)for floats. A canonical float field (temperature) that a provider wire wants in proto3-JSON integer form is the dialect's business. - Refusals:
throw new UnsupportedFeatureError(message, ErrorMeta.of(cx.provider()))before any wire. Provider errors:Errors.mapHttpError(status, message, ErrorMeta.of(provider).withProviderCode(code))then refine class by the provider's own code table. Messages are never pinned; class + code + provider_code are. - Unmapped response content: collect
Common.recordUnmapped(recorder, path, type)and, when non-empty, put_lm15_unmappedinto the Response'sprovider_data(the vet surfaces it; the harness fails any non-empty list). - Response
provider_datais the whole wire body (JsonObject); stream end events carry the frame that supplied usage (MAP-3 D9). Common.parseJsonObjectfor tool-call arguments on the complete path; the accumulator does the stream path.- Copy tables as data (port.md rule 2): compat presets, model-class detectors, finish-reason maps, error-code maps — from
lm15-python/lm15/compat.pyand the dialect modules underlm15-python/lm15/providers/. The Rust port (lm15-rs/src/dialects/) is the closest typed sibling; the TypeScript port is the most compact.
mvn -B package # tests + target/lm15.jar
python3 tools/check_contract.py --contract ../lm15-contract --direction all
python3 tools/check_contract.py --contract ../lm15-contract --direction request --case anthropic.basic_textReports land in harness-reports/<direction>.json; failed cases carry the first JSON diff path. The wrapper supplies a temporary Java registration without editing the corpus (port.md rule 1). See CONFORMANCE.md for the independent comparison gate and runtime boundaries.