Skip to content

Kev: Kev-0.8B and Kev-4B, each question its own row read by a pointer head (KitKevDecider, kind rowDecision) - #67

Merged
john-rocky merged 2 commits into
mainfrom
kev
Oct 4, 2026
Merged

john-rocky merged 2 commits into
mainfrom
kev

Conversation

@john-rocky

Copy link
Copy Markdown
Owner

Adds Jared Palmer's decision models Kev-0.8B and Kev-4B (Apache-2.0) to the kit: the catalog entries kev-0.8b and kev-4b under a new kind, rowDecision; KitKevDecider (Sources/CoreAIKit/Kev/); and the routes that load it, so systemone serve --model kev-0.8b answers SystemOne-compatible requests.

The model. A state (text or JSON) and typed questions (noul, choice, score) go in, and every option of every question comes back with a probability. Nothing is generated. Each question is its own row: the state, then the question and its options between the author's delimiter tokens. The author's pointer head reads the decoder's hidden state at the row's last token and at each option's closing token; each question is a softmax over its own options at the author's temperature. The weights are a rank-16 LoRA adapter and the head on Qwen3.5-0.8B-Base and Qwen3.5-4B-Base.

The host. The model zoo's apps/Kev library (9e06b5a), ported with every numeric path unchanged: across its nine files, only names (prefixed Kev; the zoo's KevDecider is KevPipeline), public, @available(macOS 27, iOS 27, *), the Intel-Mac guard and a provenance line differ. The decoder runs 128 tokens per call from zeroed states and has no vocabulary head. The head runs on the host in float64, and each p is rounded to float32 once. The shared prefix is on by default: a request's state runs its whole 128-token calls once, and each question continues from a copy of the decoder's states. On this graph that gives the same hidden rows as running each row whole, bit for bit. prepare(state:) keeps a state for questions that come later. A bundle whose graph takes a dynamic query length is refused at load: the zoo measured that form and does not ship it, because its memory grows. systemone serve, the MCP server and SystemOne.backend(catalog:) load KitKevDecider for the kind; decide-cli routes ask, serve and parity on it. Kev reads no images: a request carrying images gets a 422 that names the model.

Catalog. mlboydaisuke/Kev-0.8B-CoreAI @ 3368b4e9 and mlboydaisuke/Kev-4B-CoreAI @ dad3eb2b, each repo's main. systemone models (built-in catalog) lists them at 1.5 GB and 8.4 GB. Kev-0.8B ships one .aimodel to macOS and iOS; Kev-4B is macOS only. The head and the tokenizer are inside the bundle, so the entries carry no assets; format: pointerHead says how they are read. With an empty runtime cache, a load specializes the graph: 3.3 s for 0.8B, and 16.6 s with a 15.55 GB cache entry for 4B, in the zoo's runs.

Shipped kits. Kits 0.1.0–0.7.3 decode rowDecision as unknown and leave both entries out of available(_:), so no list in those kits shows them. TypedDecisions(catalog: "kev-0.8b") refuses it at its kind check, before any download. With kind: decision, 0.5.0–0.7.3 would list the entries, and loading one would download the bundle for a TypedDecisions that cannot read it. Measured on all twelve tags: each tag's own catalog code, compiled with a small main, decodes all 75 entries of this branch's catalog.json and reports both Kev entries as unknown, in neither available() nor available(.decision).

Gates (M4 Max, macOS 27.0 26A428, Xcode 27.0 RC). The kit resolves swift-transformers 1.3.3; the zoo gated its host on 1.3.4. With the kit's version, the rows of the zoo's whole fixture and held-out set equal the author's: 434/434 and 130/130 (the ids, the decide and option indices, the keys, the packed request).

decide-cli parity (Release) on the zoo's fixture, on its 155 records that carry their text, with the model downloaded from the catalog pin, against the author's fp32 read-out:

model questions row ids = author argmax (top-2 margin > 0.02) near-ties agreeing max |Δp| mean of row means
kev-0.8b 186 186/186 181/181 4/5 0.0124 0.00107
kev-4b 186 186/186 182/182 3/4 0.0153 0.00081

Against the zoo's own Swift host on the same bundle (kev fixture --shared, the .aimodel specialized by the runtime, as the kit loads it), the hidden rows and the probabilities are bit-equal on all 186 questions of each model, and the answers on all 155 records. Kev-0.8B was compared with the shared prefix on and off, Kev-4B with it on. Each maximum is the card's value, on the same row.

  • KIT_KEV_GATE=1 swift test --filter Kev: 10 Kev-0.8B records bit-equal to the zoo's Swift run, direct and shared: the row ids against the author's, the hidden-row digests, the p bits, the answers and the usage. KIT_KEV_GATE_4B=1: 3 Kev-4B records, the same.
  • systemone serve --model kev-0.8b: conformance/check.py 25/25 (22 requests and the three routes). The 255-option choice makes a row longer than 3,968 tokens and gets the 422 that says so. A request with images gets a 422.
  • systemone mcp --model kev-0.8b: one decide call answered.
  • The whole suite: XCTest 100 (23 skipped, 0 failures), swift-testing 285 passed.

Not in this PR. No release and no pin bump: no existing catalog pin moves, and Package.resolved is unchanged. The 0.8B entry ships the same .aimodel to iOS, which the zoo gated on an iPhone 18 Pro; this branch was not compiled for iOS, and the kit has not run on a phone. Speed was not measured. Kev-4B with the shared prefix off ran only on the test's 3 records. CoreAI.decide and CoreAI.systemOne load through TypedDecisions(catalog:), so they refuse Kev by its kind; the op route is not added. Use KitKevDecider or systemone serve.

On merge. The live catalog gains the two entries. A kit released before this one (0.7.3 and earlier) reads them as unknown and shows them nowhere. Until the merge, a build of this branch finds each entry by id in its built-in catalog, so KitKevDecider(catalog:) and systemone serve --model kev-0.8b work; systemone models reads the live list ahead of the built-in one and shows them once the merge is in.

Check it yourself. The tests and the server each download the model from the catalog pin unless it is cached (1.5 GB and 8.4 GB).

KIT_KEV_GATE=1 swift test --filter Kev
KIT_KEV_GATE_4B=1 swift test --filter Kev

# the model zoo's models/kev-0.8b/fixtures-kev-0.8b.json (or models/kev-4b/fixtures-kev-4b.json)
(cd Examples/Decide && swift run -c release decide-cli parity --fixture <fixtures-kev-0.8b.json>)

swift build -c release --product systemone
.build/release/systemone serve --model kev-0.8b --port 8091 &
python3 Examples/Decide/conformance/check.py http://127.0.0.1:8091 --timeout 600

Docs. CHANGELOG (Unreleased / Added), README (the Decide line, 75 entries), AGENTS.md (75 entries), docs/SYSTEM_ONE.md (a new section, "One row per question: Kev", and a row in the options table).

… head (KitKevDecider, kind rowDecision)

Adds Jared Palmer's decision models to the kit: catalog entries kev-0.8b (macOS and iOS, 1,520 MB) and kev-4b (macOS,
8,431 MB) under a new kind, rowDecision (format pointerHead), pinned to mlboydaisuke/Kev-0.8B-CoreAI @ 3368b4e9 and
mlboydaisuke/Kev-4B-CoreAI @ dad3eb2b. KitKevDecider (Sources/CoreAIKit/Kev/) is a DecisionBackend over the model
zoo's apps/Kev host (9e06b5a), ported with names prefixed Kev and every numeric path unchanged.
SystemOne.backend(catalog:) and SystemOne.supports route the kind to it, so systemone serve and mcp answer with it;
decide-cli ask, serve and parity (a coreai-kev-fixtures/1 fixture) load it.

Kits 0.1.0-0.7.3 decode rowDecision as unknown and list neither entry (each tag's catalog code run on this
catalog.json). TypedDecisions(catalog:) refuses the kind before any download. A bundle whose graph takes a dynamic
query length is refused at load.

Gates (M4 Max, macOS 27.0 26A428): KevTests with KIT_KEV_GATE=1 / KIT_KEV_GATE_4B=1, 10 Kev-0.8B and 3 Kev-4B
records bit-equal to the zoo's Swift host on the same bundle (hidden digests, p bits, answers, usage; direct and
shared). decide-cli parity on the zoo fixture's 155 text records (186 questions): row ids 186/186; argmax 181/181 and
182/182 above a 0.02 margin; max |dp| 0.0124 and 0.0153; hidden rows and p bit-equal to the zoo's run on 186/186 for
each model. systemone serve --model kev-0.8b: conformance/check.py 25 passed (22 requests and 3 routes).
CHANGELOG (Unreleased / Added): Kev-0.8B and Kev-4B. README: the Decide line names kev-0.8b and kev-4b, and the catalog
count is 75 (AGENTS.md too; scripts/check-doc-counts.py checks both). docs/SYSTEM_ONE.md: a section "One row per
question: Kev" with the parity table, a row in the options table, and Sources/CoreAIKit/Kev/ in "Where the pieces are".
@john-rocky
john-rocky merged commit c42cf28 into main Oct 4, 2026
2 checks passed
john-rocky added a commit that referenced this pull request Oct 8, 2026
…d at its option markers (KitD1OmniDecider, kind omniDecision, audio on /v1/systemone) (#68)

Adds Liquid AI's d1-omni-600M (LFM Open License v1.0) to the kit: the catalog entry `d1-omni-600m` under a new kind, `omniDecision`; `KitD1OmniDecider` (`Sources/CoreAIKit/D1Omni/`); `audio` on `/v1/systemone`; and the routes that load it, so `systemone serve --model d1-omni-600m` answers requests about a text, an image or a voice clip.

**The model.** A state (text or JSON), images or one 16 kHz clip, and typed questions (noul, choice, score) go in. Every option of every question comes back with a probability; nothing is generated. Each question is its own row in the publisher's form, with a `<|mask|>` marker before each option. One forward pass of a static decision graph scores every position, and each option is read at its marker. A text row is divided by the publisher's temperature for its type and option count, then softmaxed in float32. The checkpoint is an LFM2.5-Encoder-350M trunk with a decision head, a SigLIP2 vision tower and a FastConformer audio tower.

**The host.** The model zoo's `apps/D1Omni` library (f9e0e09), ported with every numeric path unchanged. Across its twelve files only names (prefixed `D1`; the zoo's `D1Omni` is `D1OmniPipeline`), access, `@available(macOS 27, iOS 27, *)` and a provenance line differ (`port_d1.py`, in the lane). One change for speed: a request's state is tokenized once for all its questions, since swift-transformers takes 0.4 s on a 3.4k-token state. The decision graph has seven lengths (64 to 4,096 positions); a row runs at the smallest that holds it. A row past 4,096 positions is refused with a 422. Images go through the vision graph once per crop, a clip through the audio graph of its 5 / 10 / 20 / 30 s bucket, and their rows go in front of each question's ids. `KitD1OmniDecider` is a `DecisionBackend` that reads images and audio; `readout(requestJSON:images:audio:)` gives each row, its graph length, the marker logits and the response in the publisher's form.

**The wire.** `/v1/systemone` gains `audio`: one 16 kHz mono 16-bit WAV as a data URL, base64, or a file path (`SystemOneServer` reads a path only on 127.0.0.1). With `audio`, `state` may be left out: the model then reads the `{}` its audio questions were trained on. Every other backend refuses a request with `audio` by name (422), through a new `DecisionBackend.audioRefusal` beside `imageRefusal`. For d1-omni, `images` is the existing field (#66) and `grid` is refused. The MCP `decide` tool's schema gains `audio`.

**Catalog.** `mlboydaisuke/d1-omni-600M-CoreAI` @ `914184e5`, the repo's main. `variants.macos.path = "macos"` and `variants.ios.path = "ios"`, 6,458 MB each (`measure-catalog-sizes.py`; the same `.aimodel`s). `format: markerScores`. The driver downloads the platform folder's twelve bundles at first use, each as its own download at `<variant>/<folder>`. The store takes such a path as given, so an iPhone reads the JIT `ios/` graphs. It never takes the repo's `ios-h19p/` AOT set, which has no L4096 (its compile exceeds the phone's memory limit). A graph loads the first time a row or a medium needs it.

**Shipped kits.** The bundle's metadata says `decision.head = "encoder"`, but its rows, temperatures and media prefixes are not laya's. With `kind: decision`, a shipped kit would list it and download 6.5 GB for a `TypedDecisions` that cannot read it. Kits 0.1.0–0.7.3 decode any kind they do not know as `unknown` and leave the entry out of `available(_:)`: each tag's `ModelCatalog.swift` has the `?? .unknown` decode and the filter (read, not compiled for this kind; #67 compiled the same tags for `rowDecision`). `TypedDecisions(catalog: "d1-omni-600m")` refuses it at its kind check, before any download.

**Tests** (M4 Max, macOS 27.0 26A428, Xcode 27.0 RC; the kit resolves swift-transformers 1.3.3, the version the zoo gated).

- Without files (CI): the catalog entry, the kind refusals before a download, the wire's `audio` (forms, refusals, a clip without a state, the loopback rule), the request in the publisher's form, the option order, and the readout of the fixture's 38 smoke rows from the zoo Swift host's marker logits: the probabilities bit for bit, and all 14 responses byte-equal to the publisher's `response()` text (host.py under Python 3.12, `json.dumps(ensure_ascii=False)`).
- With the repo's `tokenizer/` and `reference/` at the pin: the zoo's 360 public fixture rows rebuilt by the kit, ids, markers and graph length equal to the publisher's, 360 of 360.
- With the bundles (`KIT_D1_OMNI_GATE=1`, or a store that holds them): 14 requests through `KitD1OmniDecider(catalog:)`, 20 text rows at 64 to 4,096 positions, 3 images (one tiled) and 3 clips (10, 20 and 30 s buckets). All 38 rows give the zoo Swift host's marker logits and probabilities bit for bit, and all 14 responses equal the publisher's text. An image (`"state": ""`) and a clip (no state) sent again over the wire as base64 give the same probabilities. Only the six lengths those rows need are loaded. This ran on the zoo's staged copy of the repo, checked file by file against the Hub at the pin (sizes, LFS sha256, git blob ids).
- The whole suite: XCTest 100 (23 skipped, 0 failures), swift-testing 297 passed.

**Not in this PR.** No release and no pin bump; no other catalog entry moves, and `Package.resolved` is unchanged. The kit has not run on a phone, and the phone's memory with several decision lengths loaded in one process was not measured. The zoo's gate app ran these graphs on an iPhone 18 Pro, one combination of graphs per process. Speed was not measured through the kit; the numbers in `docs/SYSTEM_ONE.md` are the zoo's. `CoreAI.decide` and `CoreAI.systemOne` load through `TypedDecisions(catalog:)`, so they refuse d1-omni by its kind; use `KitD1OmniDecider` or `systemone serve`. Open: the store can download one folder as its own bundle (the driver already does, per folder), so downloading a length the first time a row needs it means moving those calls from init to first use. That is the next round.

**On merge.** The live catalog gains the entry, and a kit released before this one shows it nowhere. Until the merge, a build of this branch finds it by id in its built-in catalog.

**Check it yourself.** The gate downloads `macos/` (6.5 GB) and `reference/` (9.3 MB) from the catalog pin unless they are cached.

```bash
swift test --filter D1Omni                                   # without files
KIT_D1_OMNI_GATE=1 swift test --filter D1Omni                # + the 360 rows and the 14 requests through the graphs

swift build -c release --product systemone
.build/release/systemone serve --model d1-omni-600m --port 8091 &
curl -s http://127.0.0.1:8091/v1/systemone -H 'Content-Type: application/json' \
  -d '{"audio": "/path/to/clip.wav", "questions": {"refund": {"type": "noul", "instructions": "Does the caller want a refund?"}}}'
```

**Docs.** CHANGELOG (Unreleased / Added), README (the Decide line, 76 entries), AGENTS.md (76 entries), `docs/SYSTEM_ONE.md` (a section "Text, images or audio: d1-omni", a row in the options table, `audio` in the request paragraph, `Sources/CoreAIKit/D1Omni/` in "Where the pieces are").

Merge is the lane supervisor's call.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant