Skip to content

Kev-0.8B on Core ML: fused decisions + Guess Who demo - #19

Merged
Alex-Wengg merged 9 commits into
mainfrom
feat/kev-qwen35-demo
Sep 26, 2026
Merged

Alex-Wengg merged 9 commits into
mainfrom
feat/kev-qwen35-demo

Conversation

@Alex-Wengg

Copy link
Copy Markdown
Member

Adds Kev-0.8B (Qwen3.5 backbone) on the GPU through Core ML.

  • QwenBPETokenizer: Qwen byte-level BPE in Swift, identical to HF tokenizers on 3,694 texts / 522k tokens.
  • KevManager: Kev's row form, one question per call (fallback path).
  • KevFastManager: one fused call per request. The state and all questions run together, questions packed end to end under a segment mask so each restarts from the state. Buckets are read from the package's function names.
  • KevModelStore: pinned, checksummed snapshot of FluidInference/kev-0.8b-coreml @ 8fa70891.
  • KevGuessWhoDemo: self-playing Guess Who over 80 Wikipedia people (DBpedia-14), 12 yes/no answers per bio in one call; demo.sh adds a terminal with asitop and the model log.
  • KevCheck: tokenizer / parity / serving checks. DBpediaSample gains a class filter and pages at the 100-row API limit.

Fidelity: fused fp16 functions vs the fp16 row packages, 191 questions, 0 top-answer flips (max |Δp| 0.004). The row packages reproduce Kev's published transfer-v4 / decision-v7 numbers (3 flips in 2,232 questions vs Kev fp32).

Speed, M5 Pro, Kev's serving_bench.py shapes (new state), Core ML vs Kev's MLX server: 2 q short 18.1 vs 26.6 ms, 6 q short 37.8 vs 51.5 ms, 5 q / 370-token state 64.1 vs 69.6 ms. Conversion: FluidInference/mobius models/computer-use/kev-0.8b/coreml.

🤖 Generated with Claude Code

…n path

QwenBPETokenizer: Qwen byte-level BPE in Swift (NFC, added-token split, Qwen
pre-tokenizer regex, GPT-2 byte map, merge ranks); matches HF tokenizers on
3,694 texts / 522,084 tokens.

KevManager: Kev's row form (state + one question branch per Core ML call)
over the L<length>_K<options> buckets; questions formatted like
kev.api.to_record, <|x|> escaped, state capped at Kev's MAX_STATE 384 for
evaluation. Swift == Python Core ML on 191 questions, 0 top-answer flips.

KevFastManager (macOS 15 / iOS 18): one fused_S*_P* function call per request
from the weight-shared multifunction KevFused package. The state and every
question run together; questions are packed end to end in 128-token lanes and
each restarts from the state (segment mask). Questions over a lane or with
more than 16 options fall back to rows. 191 questions, 0 flips, max |dp| 0.004.

M5 Pro, warmed, Kev's serving_bench cases vs Kev's MLX (new state):
  2 questions, short state        18.1 ms  (MLX 26.6)
  6 questions, short state        37.8 ms  (MLX 51.5)
  5 questions, 370-token state    64.1 ms  (MLX 69.6)

KevCheck: tokenizer, parity, serving, fast-parity, fast-serving modes.
KevGuessWhoDemo: a self-playing Guess Who over 80 real Wikipedia people
(DBpedia-14 test split: artists, athletes, politicians). Kev reads each bio
once and answers all 12 yes/no questions in one fused Core ML call per card,
then plays turns (question closest to a 50/50 split of the cards still up)
until the hidden person, chosen among cards with a unique answer signature,
is left. Header metrics (last / median call, decisions per second, total),
Pause (Space) / Reset (Cmd-R). Inference runs back to back off the main
actor and streams into the UI; rendering the grid no longer delays calls
(118 -> ~40 ms per card wall). demo.sh opens a Terminal with asitop above
and the colored model log below.

M5 Pro: ~38 ms median per card (12 decisions), 960 decisions in 3.0-3.5 s.

KevFastManager: buckets are discovered from the package's function names
(MLModelAsset.functionNames), so a package with any subset of fused
functions works; warm() now runs each function once. A function left idle
pays a 0.3-0.8 s re-setup on its next call (not input dependent), so the
demo re-warms before each scan.

DBpediaSample: optional class filter; pages requests at the dataset
viewer's 100-row limit.
Reset crashed the app: cancelling a game ends its result stream without an
error, so the old game went on into its turns reading the new wall's
unscanned cards (nil unwrap in split). The game now stops on cancellation
after re-warming and after the scan, and turn logic no longer force-unwraps.
Buttons are not keyboard-focusable, so Space only toggles Pause.

Header: controls and fixed-width metrics on the first row, the question on
a full-width second row, so a changing status never moves anything. The app
opens idle ("Ready — press Play"); Play starts the first game.
The demo looped games forever. Play now deals one wall, plays it to the
solve and stops on the result; Play or Reset deals the next. Reset also
clears a pause. The game counter is gone from the sidebar.
Play runs up to 4 games (3 s apart) and stops on the last result; Reset
starts a fresh set. The sidebar shows the game's place in the set.
KevModelStore fetches the pinned FluidInference/kev-0.8b-coreml snapshot
(8fa70891: fused package, L512_K16 + L1024_K80 row buckets, embedding table,
tokenizer), checksummed once per revision. The Guess Who demo uses it unless
a local model directory is given. The embedding table ships once, so both
managers look for the bucket folder that has it instead of the first one.

KevPackingTests: lane placement, grouping by packed length and readouts,
yes/no rendering, and the pinned asset list.
KevFastManager no longer loads the row fallback models up front; each row
bucket loads on its first question (KevManager.load(eager:), default eager).
A workload that never falls back holds only the fused package: peak memory
footprint 0.67 GB on the 80-bio Guess Who scan.

Packages are compiled once and the .mlmodelc kept beside them, so launches
after the first skip the compile (HF snapshot: warm 24 s -> 11 s).

KevCheck bios: timed yes/no workload through the fused path, for comparison
with Kev's PyTorch model (mobius bench_original_bios.py).
@Alex-Wengg
Alex-Wengg merged commit 5a91acf into main Sep 26, 2026
1 check passed
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