Micron Document
Java (codec)

JDK 17+ binding. java.net.http.HttpClient, Iterator + Flow.Publisher streams, Jackson JSON, msgpack-java for frames. Maven-ready.

Frameworks


'codec' (Maven artifact 'io.github.wdunn001:codec') is the Java binding. Targets JDK 17+ so it has access to records, sealed types, and the modern 'java.net.http.HttpClient'. Streaming is exposed both as 'Iterator<CodecFrame>' (synchronous, the canonical shape) and as 'Flow.Publisher<CodecFrame>' (reactive callers).

Install

code (xml):
<dependency>
<groupId>io.github.wdunn001</groupId>
<artifactId>codec</artifactId>
<version>0.1.0</version>
</dependency>
For now, install from source: 'mvn install' from 'packages/java/' of the Codec repo (https://github.com/wdunn001/Codec) into your local Maven repo.

The library brings two transitive dependencies: 'com.fasterxml.jackson.core:jackson-databind' for map JSON and 'org.msgpack:msgpack-core' for frame parsing. Everything else is JDK built-in, sha256 via 'java.security.MessageDigest', regex with 'Pattern.UNICODE_CHARACTER_CLASS', HTTP via 'java.net.http.HttpClient'.

The four-step shape

code (java):
import ai.codec.*;
// 1. fetch + verify map → MapLoader.load(...)
// 2. send → HttpClient (JDK)
// 3. stream → frames → StreamDecoder.decodeMsgpackStream(...)
// 4. IDs → text → Detokenizer.render(...)
1. Load the vocab map

code (java):
TokenizerMap map = MapLoader.load(new LoadOptions.Builder()
.url("https://cdn.jsdelivr.net/gh/wdunn001/codec-maps/maps/qwen/qwen2.json")
.hash("sha256:887311099cdc09e7022001a01fa1da396750d669b7ed2c242a000b9badd09791")
.build());
The hash is verified against the bytes on the wire before the JSON parses. Mismatch throws 'TokenizerMapHashMismatchException'. For an async fetch, use 'MapLoader.loadAsync(...)', which returns a 'CompletableFuture<TokenizerMap>'.

2. Send a request

code (java):
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();

// stream_format lives in the BODY (piggybacks on OpenAI's request schema);
// Accept-Encoding + Codec-Client-Version are the v0.4.1 Codec REQUEST HEADERS.
// See /docs/protocol/#request-vs-response-where-each-codec-knob-lives.
String body = """
{"model":"Qwen/Qwen2.5-7B-Instruct",
"prompt":"Explain entropy in one paragraph.",
"stream":true,
"stream_format":"msgpack",
"max_tokens":256}
""";

HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:8000/v1/completions"))
.header("Content-Type", "application/json")
.header("Accept-Encoding", "zstd, br, gzip, identity") // full v0.4.1 stack
.header("Codec-Client-Version", "0.4") // v0.4 normative
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();

HttpResponse<java.io.InputStream> resp = http.send(req,
HttpResponse.BodyHandlers.ofInputStream());
'HttpResponse.BodyHandlers.ofInputStream()' is the JDK equivalent of "don't buffer the body", exactly what streaming needs.

3. Decode the binary stream

code (java):
import java.util.Iterator;

Iterator<CodecFrame> frames = StreamDecoder.decodeMsgpackStream(resp.body());
while (frames.hasNext()) {
CodecFrame frame = frames.next();
// frame.ids(): int[]
// frame.done(): boolean
// frame.finishReason(): String (nullable)
}
For reactive callers there's a 'Flow.Publisher<CodecFrame>' variant:

code (java):
import java.util.concurrent.Flow;

Flow.Publisher<CodecFrame> publisher =
StreamDecoder.publishMsgpack(resp.body());
publisher.subscribe(mySubscriber);
'decodeProtobufStream' / 'publishProtobuf' cover the length-prefixed protobuf wire mode.

4. Detokenize at the edge

code (java):
Detokenizer detok = new Detokenizer(map);

while (frames.hasNext()) {
CodecFrame frame = frames.next();
String text = detok.render(frame.ids(),
new DetokenizeOptions(/* partial */ !frame.done(), /* renderSpecial */ false));
System.out.print(text);
}
'Detokenizer' is stateful. Partial multi-byte UTF-8 sequences buffer across 'render()' calls when 'partial=true'. A 4-byte 🚀 split across two frames round-trips identically. Call 'detok.reset()' between unrelated streams.

Encoding (sending IDs, not text)

code (java):
BPETokenizer tok = new BPETokenizer(map);
int[] ids = tok.encode("System: be concise.\nUser: what's BPE?");

ObjectMapper json = new ObjectMapper();
String body = json.writeValueAsString(Map.of(
"model", "Qwen/Qwen2.5-7B-Instruct",
"prompt", ids, // int[], the server reads as token IDs
"stream_format", "msgpack",
"max_tokens", 256
));
Greedy by merge priority, not left-to-right. Bit-identical to the upstream tokenizer across all reference bindings.

Watching for tool calls

code (java):
ToolWatcher watcher = new ToolWatcher(map, "<tool_call>", "</tool_call>");

while (frames.hasNext()) {
CodecFrame frame = frames.next();
for (WatcherEvent ev : watcher.feed(frame.ids())) {
switch (ev.kind()) {
case PASSTHROUGH -> forward(ev.ids());
case CAPTURED -> {
int[] ids = toIntArray(ev.ids()); // long[] → int[]
String text = detok.render(ids, new DetokenizeOptions(false, false));
ToolCall call = parseToolCall(text);
dispatch(call.name(), call.args());
}
}
}
}
The watcher's IDs are 'long[]' because Java has no unsigned 32-bit primitive. A 'long' losslessly carries the full uint32 range. A single compare per token; no detokenization on the hot path. See Tool calling.

Translating across vocabularies

code (java):
TokenizerMap qwen = MapLoader.load(new LoadOptions.Builder()
.url("https://cdn.jsdelivr.net/gh/wdunn001/codec-maps/maps/qwen/qwen2.json")
.hash("sha256:887311099cdc09e7022001a01fa1da396750d669b7ed2c242a000b9badd09791")
.build());
TokenizerMap llama = MapLoader.load(new LoadOptions.Builder()
.url("https://cdn.jsdelivr.net/gh/wdunn001/codec-maps/maps/meta-llama/llama-3.json")
.hash("sha256:79b707aea8c2b41c2883ec7913b0c4a0c880044ac844d89a9a03e779eb92db04")
.build());

Translator tr = new Translator(qwen, llama);

while (frames.hasNext()) {
CodecFrame frame = frames.next();
int[] llamaIds = tr.translate(frame.ids(), !frame.done());
forwardToLlama(llamaIds);
}

Spring / reactive integration

For a Spring WebFlux endpoint that proxies a Codec stream out as SSE:

code (java):
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chat(@RequestBody ChatRequest req) {
Detokenizer detok = new Detokenizer(map);
return Flux.from(StreamDecoder.publishMsgpack(openCodecStream(req)))
.map(frame -> detok.render(frame.ids(),
new DetokenizeOptions(!frame.done(), false)));
}
'Flow.Publisher' adapts to Reactor 'Flux' via 'Flux.from(...)'. RxJava and Mutiny work the same way through their respective 'Publisher' adapters.

Production checklist

Pin the map hash in 'LoadOptions'.
'HttpResponse.BodyHandlers.ofInputStream()'. Anything else buffers the whole response.
Reuse 'HttpClient' and 'Detokenizer'. Both are designed for reuse; new 'Detokenizer' instances mid-stream drop the UTF-8 buffer. Call 'reset()' at stream boundaries instead.
Maven Central is the target. Until publish, install locally with 'mvn install' from 'packages/java/'.

See also

• packages/java/ on GitHub (https://github.com/wdunn001/Codec/tree/main/packages/java)