Skip to content

Commit b8aa9ea

Browse files
dfa1claude
andcommitted
Split modules into contract (core) and implementations (client-*), align names
core keeps the TypeSafeClient interface, its decorators, TypeSafeException, the model and the JsonCodec SPI. DefaultTypeSafeClient, ApiKey and the HttpTransport SPI move to the new client-http module, so the compiler now enforces that core never sees the HTTP client; TypeSafeClient.builder() becomes DefaultTypeSafeClient.builder(). Every module's directory, artifact and package share one name: client-http-jdk, client-http-okhttp, codec-jackson2, codec-jackson3, client-local, client-mapping, client-testkit (io.github.dfa1.typesafe.<module>, dashes as dots). No client-* module depends on another in production. ADR 0003 records the layout (closes #12). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent b1a86bb commit b8aa9ea

118 files changed

Lines changed: 592 additions & 427 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/local.yml‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
name: Local engines
22

3-
# Agreement with Jev and throughput of typesafe-java-local's engines, as a table in the job summary.
3+
# Agreement with Jev and throughput of typesafe-java-client-local's engines, as a table in the job summary.
44
# Model directories are cached: Laya fp32 (+ fp16 on macOS, for the GPU) and Qwen 4-bit, all onnx-community exports.
55
on:
66
workflow_dispatch:
77
push:
88
branches: [main]
9-
paths: ['local/**', '.github/workflows/local.yml', '.github/scripts/local_summary.py']
9+
paths: ['client-local/**', '.github/workflows/local.yml', '.github/scripts/local_summary.py']
1010
pull_request:
11-
paths: ['local/**', '.github/workflows/local.yml', '.github/scripts/local_summary.py']
11+
paths: ['client-local/**', '.github/workflows/local.yml', '.github/scripts/local_summary.py']
1212

1313
jobs:
1414
numbers:
@@ -48,23 +48,23 @@ jobs:
4848
uvx --from huggingface_hub hf download Cloudflare/clef-flash --revision 17f0b0ad64efb65d273590632833508766b2aae6 tokenizer.json --local-dir $M/clef-flash
4949
5050
- name: Build
51-
run: ./mvnw -B -q -pl local -am test-compile dependency:build-classpath -Dmdep.outputFile=$PWD/cp.txt -Dmdep.includeScope=test
51+
run: ./mvnw -B -q -pl client-local -am test-compile dependency:build-classpath -Dmdep.outputFile=$PWD/cp.txt -Dmdep.includeScope=test
5252

5353
- name: Real-model tests
54-
run: ./mvnw -B -pl local -am test -DexcludedGroups=acceptance -Dengine=laya -Dsurefire.failIfNoSpecifiedTests=false
54+
run: ./mvnw -B -pl client-local -am test -DexcludedGroups=acceptance -Dengine=laya -Dsurefire.failIfNoSpecifiedTests=false
5555

5656
- name: Agreement with Jev
57-
working-directory: local
57+
working-directory: client-local
5858
run: |
5959
CP="$(cat ../cp.txt):target/classes:target/test-classes"
6060
ENGINES="laya qwen"
6161
if [ "$RUNNER_OS" = macOS ]; then ENGINES="$ENGINES laya-fp16-gpu"; fi
6262
for e in $ENGINES; do
63-
java -cp "$CP" io.github.dfa1.typesafe.local.JevComparison $e | tee -a ../jev.txt
63+
java -cp "$CP" io.github.dfa1.typesafe.client.local.JevComparison $e | tee -a ../jev.txt
6464
done
6565
6666
- name: Throughput
67-
working-directory: local
67+
working-directory: client-local
6868
run: |
6969
CP="$(cat ../cp.txt):target/classes:target/test-classes"
7070
ENGINES=LAYA,QWEN

‎CHANGELOG.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- **Breaking: modules split into contract (`core`) and implementations (`client-*`), names aligned** — `core` keeps the `TypeSafeClient` interface, its decorators, the model and `JsonCodec`; `DefaultTypeSafeClient`/`ApiKey`/`HttpTransport` move to the new `client-http`, `TypeSafeClient.builder()` becomes `DefaultTypeSafeClient.builder()`, and every module's artifact and package now match its directory (`client-http-jdk`, `client-http-okhttp`, `codec-jackson2`, `codec-jackson3`, `client-local`, `client-mapping`, `client-testkit`); see ADR 0003.
1011
- **docs: local-model setup as three steps** — dependency, Laya download, code, then a model comparison; the optional Clef 4-bit script runs from its GitHub URL with `uv run`, and load errors link to the how-to.
11-
- **`local`: hardening** — special tokens in caller text stay plain text (no prompt injection via `<|im_end|>`), the tokenizer cache is bounded, and Clef checks its weights' sha256 on first load.
12-
- **`local`: shared request validation, `evaluateAsync` off virtual threads** — every engine rejects no questions or empty criteria with `BadRequest` (Laya used to NPE or answer a null choice), and `evaluateAsync` runs on one platform thread so native inference can't pin the JVM's virtual-thread carriers.
13-
- **`local`: `LocalClefTypeSafeClient.loadOnGpu(...)`** — Clef-flash on ONNX Runtime's WebGPU backend (macOS on Apple Silicon), same answers as the CPU; Laya gets no GPU option, since its WebGPU answers drift between launches. (#19)
12+
- **`client-local`: hardening** — special tokens in caller text stay plain text (no prompt injection via `<|im_end|>`), the tokenizer cache is bounded, and Clef checks its weights' sha256 on first load.
13+
- **`client-local`: shared request validation, `evaluateAsync` off virtual threads** — every engine rejects no questions or empty criteria with `BadRequest` (Laya used to NPE or answer a null choice), and `evaluateAsync` runs on one platform thread so native inference can't pin the JVM's virtual-thread carriers.
14+
- **`client-local`: `LocalClefTypeSafeClient.loadOnGpu(...)`** — Clef-flash on ONNX Runtime's WebGPU backend (macOS on Apple Silicon), same answers as the CPU; Laya gets no GPU option, since its WebGPU answers drift between launches. (#19)
1415
- **docs: run Clef-flash on a Mac with MLX** — a how-to pointing the regular client at mlx-community's local Clef-flash server: about 0.55 s per request on an M5, much closer to Jev than Laya. (#14)
15-
- **`local`: `LocalClefTypeSafeClient`** — Cloudflare's Clef-flash (Qwen3.5-9B + joint schema head) on Ollaya's ONNX graph over the upstream bf16 weights; one forward pass per request; `scripts/clef/quantize_q4.py` converts it to 4-bit weights (7.7 GB, 2–4 s per request on an M5 GPU). (#14)
16-
- **`local`: `LocalLayaTypeSafeClient`, `LocalQwenTypeSafeClient`** — new module, one client class per model, evaluating in-process on ONNX Runtime from a local model directory (Laya fp32 from onnx-community, or Qwen2.5 as a prompted-LLM baseline), with a `Local engines` workflow publishing agreement-with-Jev and throughput tables. (#14)
16+
- **`client-local`: `LocalClefTypeSafeClient`** — Cloudflare's Clef-flash (Qwen3.5-9B + joint schema head) on Ollaya's ONNX graph over the upstream bf16 weights; one forward pass per request; `scripts/clef/quantize_q4.py` converts it to 4-bit weights (7.7 GB, 2–4 s per request on an M5 GPU). (#14)
17+
- **`client-local`: `LocalLayaTypeSafeClient`, `LocalQwenTypeSafeClient`** — new module, one client class per model, evaluating in-process on ONNX Runtime from a local model directory (Laya fp32 from onnx-community, or Qwen2.5 as a prompted-LLM baseline), with a `Local engines` workflow publishing agreement-with-Jev and throughput tables. (#14)
1718
- **build: checkstyle engine 14.3.0** — the plugin's default 9.3 can't parse Java 21 pattern matching for switch; same rules. (#14)
1819
- **Breaking: optional API key, `ApiKey` without a public constructor** — `TypeSafeClient.builder()` takes no argument and `.apiKey(key)` is optional (no key, no `Authorization` header, for local servers); `ApiKey` is a final class built with `of(String)`/`fromFile`/`fromDefaultFile`/`fromEnv`, and no longer exposes its value. (#18)
19-
- **`jackson2`: ignore unknown fields, like `jackson3`** — a field the API adds to a response no longer fails decoding with `ResponseDecoding`; Jackson 2's default rejected it. (#18)
20+
- **`codec-jackson2`: ignore unknown fields, like `codec-jackson3`** — a field the API adds to a response no longer fails decoding with `ResponseDecoding`; Jackson 2's default rejected it. (#18)
2021
- **`core`: `Content.fields(...)` and `EvaluateRequest.Builder` keep their order** — both copied with `Map.copyOf`, which reshuffles keys per JVM run, so the same request serialized with fields/questions in a different order each run; they now keep the caller's iteration order.
2122
- **`core`: `TokenCounter`** — running totals of input/output tokens from `EvaluateResponse#usage()`, added via `.decorateWith(tokens::decorate)`; thread-safe, shareable across clients.
2223
- **`core`: `Builder.decorateWith(...)` naming and docs** — named to read as additive and to pair with the `decorate(...)` factories; `Builder` javadoc and the how-to explain decorator ordering.

0 commit comments

Comments
 (0)