Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
785 commits
Select commit Hold shift + click to select a range
e1fcf5a
feat(whisper): decoder self-attention KV cache
Snider Jul 19, 2026
8702a2b
feat(metal): sdpa_vector head-dim-32 runtime-dim fallback (#28)
Snider Jul 19, 2026
5567acc
feat(engine/metal): wire rwkv7 + mamba2 through lem serve/generate (#…
Snider Jul 19, 2026
5349993
merge: #28 runtime-dim sdpa_vector fallback (lane/hd32)
Snider Jul 19, 2026
23679aa
merge: #36 serve seam — rwkv7 + mamba2 through lem serve/generate (la…
Snider Jul 19, 2026
2c3165e
feat(serve): POST /v1/audio/transcriptions for Whisper checkpoints
Snider Jul 19, 2026
cddddae
merge: #37 whisper serve route + decoder KV cache (lane/whisperserve)
Snider Jul 19, 2026
f135a7a
feat(metal): TurboQuant prefill goes BATCHED — kill the sequential fa…
Snider Jul 19, 2026
03657df
merge: #48 batched TurboQuant prefill — 65x (lane/tqprefill)
Snider Jul 19, 2026
8600143
fix(metal): rtdim resolvers cache success only — error-latching poiso…
Snider Jul 19, 2026
9de06e6
test(metal): wrong-lib coverage matches the #28 fallback contract
Snider Jul 19, 2026
db22b40
test(metal): finish the #28 contract in wrong-lib coverage — rtdim ca…
Snider Jul 19, 2026
0cbf20a
test(metal): rwkv7 device-GEMM parity — the device hook is clean, the…
Snider Jul 19, 2026
09b371c
merge: #36 rwkv7 device-hook parity — exonerated (lane/rwkvparity)
Snider Jul 19, 2026
43ebad4
feat(metal): fuse TurboQuant decode SDPA's q-rotation + output-unrota…
Snider Jul 19, 2026
b3cb1d9
docs(dflash): #37 survey — real external family, engine forward built…
Snider Jul 19, 2026
de5c831
feat(decode): dflash.ParseConfig recognises both real DFlash conventions
Snider Jul 19, 2026
67ce924
feat(decode): real z-lab DFlash architecture — host-f32 forward, orac…
Snider Jul 19, 2026
ed481a7
merge: #48 TQ decode fusion — rotation folded into single-pass SDPA (…
Snider Jul 19, 2026
081b13a
merge: #37 dflash survey + real z-lab host forward (lane/dflash)
Snider Jul 19, 2026
62e9102
test(metal/train): #49 sublayer instrument — station captures + bf16 …
Snider Jul 19, 2026
c9c733a
fix(metal/train): #49 root-caused — bf16-chaos envelope, not an engin…
Snider Jul 19, 2026
5353d20
merge: #49 NO engine defect — bf16 chaos through E2B layer-8 cliffs, …
Snider Jul 19, 2026
3d8c9b0
feat(dotsocr): working OCR — NaViT vision tower + Qwen2 decoder, host…
Snider Jul 19, 2026
a18069b
perf(metal): O(delta) paged-KV sync/reload at the MTP verify seam (#53)
Snider Jul 19, 2026
d850c51
merge: #37 DOTS-OCR host-correct — reference-verbatim OCR (lane/dotsocr)
Snider Jul 19, 2026
95b5dfb
perf(metal): incremental linearSnapshot — the drafter export stops re…
Snider Jul 19, 2026
bd5e9f9
feat(glmocr): host-f32 GLM-OCR forward — image in, text out (#37)
Snider Jul 19, 2026
174f657
merge: #37 GLM-OCR host-correct — byte-exact E2E (lane/glmocr)
Snider Jul 19, 2026
282dacc
perf(metal): plain tied-embedding drafter head rides the fused draft …
Snider Jul 19, 2026
f352dc4
perf(metal): fan the delta KV sync/reload across layers (#53)
Snider Jul 19, 2026
8b8e5d4
perf(metal): quantise the plain drafter head — 537MB bf16 scan become…
Snider Jul 19, 2026
1800a2a
docs(models): 26B MTP pair row — −73% closed to ~−7%, mechanism + nex…
Snider Jul 19, 2026
542a78e
merge: #53 gemma4 26B MTP — the -73% loss closed to ~-7%, mechanism i…
Snider Jul 19, 2026
2834d9d
fix(deepseekvl2): read decoder geometry from config.json's top level,…
Snider Jul 19, 2026
10ba2eb
feat(deepseekvl2): DeepSeek-OCR forward — SAM+CLIP vision encoder, Mo…
Snider Jul 19, 2026
ed87682
feat: add lem ocr verb, OCRModel capability interface, pkg/ocr example
Snider Jul 19, 2026
cd016ad
merge: #37 DeepSeek-OCR host-correct + the lem ocr verb (lane/dsocr)
Snider Jul 19, 2026
7681c18
feat(cli): lem ocr dispatches every OCR arch by model_type
Snider Jul 19, 2026
9d45e05
docs(tq): quiet-box receipts — default stays native; TQ TTFT 52x at 1…
Snider Jul 19, 2026
bc12148
perf(metal): device-chained MTP draft block — one CB, one readback pe…
Snider Jul 19, 2026
886de3d
perf(metal): wide M=5-8 halved-tile qmv rows — byte-exact, live-refut…
Snider Jul 19, 2026
e219945
docs(mtp): boundary-greedy carry design — invalidation mapped, own-la…
Snider Jul 19, 2026
dd8971c
merge lane/mtp26b2: MTP lever pack 2 — device-chained draft block (on…
Snider Jul 19, 2026
39620b9
fix(metal): #54 native E2B long-context prefill — opt out of the resi…
Snider Jul 19, 2026
18dcaf1
merge lane/prefill54: #54 native long-context prefill — DisablePrompt…
Snider Jul 19, 2026
faa8993
docs(receipts): #54 addendum — native 12k prefill 9585 tok/s, the 52x…
Snider Jul 19, 2026
0477897
feat(metal): fuse TurboQuant 2-pass decode's output unrotation into p…
Snider Jul 20, 2026
02e1e61
merge lane/tq2pass: TQ 2-pass decode — fold output unrotation into a …
Snider Jul 20, 2026
3df3627
feat(model): #50 archzoo — mixtral factory port + qwen3_6/qwen3_next …
Snider Jul 20, 2026
ffe3feb
merge lane/archzoo: #50 blocker — qwen3_6/qwen3_next/mixtral factory-…
Snider Jul 20, 2026
2729446
fix(metal): #55 MTP greedy pair-vs-plain byte equality — same prompt …
Snider Jul 20, 2026
cc6f53c
merge lane/mtpdiverge: #55 MTP greedy divergence — two stacked defect…
Snider Jul 20, 2026
45415f5
fix(metal): #56 concrete param for chargeGPUBusy — the probe's anonym…
Snider Jul 20, 2026
e2bcfb6
docs(tq): design for TQ KV on MoE + hybrid arches (#48 follow-on)
Snider Jul 20, 2026
e980446
feat(tq): state-lane TurboQuant carrier — TQ KV on MoE (stepToken) se…
Snider Jul 20, 2026
3b87833
feat(model): #50 archzoo — dbrx + olmoe factory-load without composed
Snider Jul 20, 2026
3e353bc
merge lane/zoodbrx: #50 archzoo — dbrx + olmoe factory-load without c…
Snider Jul 20, 2026
0204175
feat(model/arch): factory-native load routes for granitemoe and qwenm…
Snider Jul 20, 2026
9843bbf
feat(engine/metal): extend isQwen35FactoryType to qwen3_6/qwen3_6_moe…
Snider Jul 20, 2026
3b93bd0
feat(tq): mixed-kind snapshot block codec + shared-owner TQ read on t…
Snider Jul 20, 2026
e0180ef
feat(mtp): grouped-by-expert MoE row batching primitive (#53)
Snider Jul 20, 2026
ee585bc
docs(tq): align design with landed reality — shared-owner TQ read, tu…
Snider Jul 20, 2026
99808ae
merge lane/zooqwen: #50 archzoo — granitemoe (zero-copy packed alias)…
Snider Jul 20, 2026
bae9bdb
merge lane/mtprows: #53 grouped-by-expert MoE row-batching primitive …
Snider Jul 20, 2026
adc7624
merge lane/tqmoe: #48 TQ on MoE + hybrids — per-layer-KIND cache matr…
Snider Jul 20, 2026
52a3826
feat(model/llama4): give llama4 the factory route (#50 archzoo)
Snider Jul 20, 2026
5323bce
merge lane/llama4: #50 archzoo — llama4 factory route (chains Normali…
Snider Jul 20, 2026
237aa8e
refactor(hip,mtp-probe): sever model/composed — #50 demolition, two l…
Snider Jul 20, 2026
21f0039
merge lane/hipmisc: #50 — engine/hip composed side-door severed (regi…
Snider Jul 20, 2026
7a1c825
docs(#50): composed-strip inventory — classification of every model/c…
Snider Jul 20, 2026
9782dac
refactor(metal)!: sever every model/composed edge — the factory is th…
Snider Jul 20, 2026
606d5d1
feat(model)!: delete model/composed — one engine, one route (#50)
Snider Jul 20, 2026
7bd6da3
test(qwen35): one-test-per-symbol coverage for the ported family pred…
Snider Jul 20, 2026
7821b3d
fix(examples)!: retire pkg/packed-moe with the composed API it demons…
Snider Jul 20, 2026
e25e3d1
docs(examples): hybrid-quant — factory-route truth incl. the sub-2-bi…
Snider Jul 20, 2026
064d731
test(dotsocr): #58 opt-in gate for the ~10-min real-checkpoint E2E — …
Snider Jul 20, 2026
fc31e8c
merge lane/composedrip: #50 — model/composed DELETED (+867/−20,566, 1…
Snider Jul 20, 2026
308af2e
fix(hip): make composed-runner capability reporting tell the truth
Snider Jul 20, 2026
0ca34af
fix(metal): #50 correct the sub-2-bit decline's story and flip the Bo…
Snider Jul 20, 2026
745d995
merge lane/hiplabels: #50 — hip capability reporting tells the truth …
Snider Jul 20, 2026
4723837
fix(mtp): mtpRowsMoEBatched supports fused ExpGateUp experts (#53)
Snider Jul 20, 2026
15f663a
feat(mtp): layer-major verify driver wires mtpRowsMoEBatched into the…
Snider Jul 20, 2026
fc8afb9
docs(#60): owned-weight device binding design — registered-owned-rang…
Snider Jul 20, 2026
8110131
merge lane/mtpwire: #53 layer-major verify driver — mtpRowsMoEBatched…
Snider Jul 20, 2026
0327541
feat(model): owned-tensor adoption + the b1->b2 repack re-hook at Loa…
Snider Jul 20, 2026
26642de
feat(mtp): #53 the batched exact lane IS the exact lane — armed by de…
Snider Jul 20, 2026
fe76876
fix(hip): retire qwen3_6 from the dense-quick-win whitelist, give pro…
Snider Jul 20, 2026
29f60c0
fix(model): #57 give the shared expert its own FF width (Arch.SharedE…
Snider Jul 20, 2026
ab3cf99
merge lane/sharedff: #57 shared-expert FF width — model.Arch.SharedEx…
Snider Jul 20, 2026
9826d12
merge lane/hiptruth2: #50 — last stale hip capability tables tell the…
Snider Jul 20, 2026
7523462
feat(metal): bind registered owned weights resident in bufForAligned;…
Snider Jul 20, 2026
434d21a
fix(hip): post-hiptruth2 fixups — native_contract_test drops the reti…
Snider Jul 20, 2026
086cfdb
test(metal): #48 hybrid fixture — plain-attn + gated-delta qualifies …
Snider Jul 20, 2026
7c8627d
fix(hip): #50 recognised-vs-runnable split — inspection Supported key…
Snider Jul 20, 2026
b3efcb0
test(metal): #48 hybrid mixed-kind snapshot round trip + gated-delta …
Snider Jul 20, 2026
37e2948
fix(hip): RecognisedArchitecture resolves through LookupArchitectureP…
Snider Jul 20, 2026
87e33ad
merge lane/tqfixture: #48 hybrid band test landed — synthetic plain-a…
Snider Jul 20, 2026
862cf23
fix(model): alias wrapper names into dm.Tensors once — the repack wri…
Snider Jul 20, 2026
aa74f4a
feat(metal)!: delete subTwoBitQuantDecline — Bonsai 1-bit serves; fli…
Snider Jul 20, 2026
b3284d9
merge lane/ownedbind: #60 owned-weight device binding — owned-tensor …
Snider Jul 20, 2026
48e2d72
fix(mtp): #53 complete eligibility — qmvByteExactServable probes ever…
Snider Jul 20, 2026
64102fb
docs: design for the qwen35 factory-native vision tower (#59 item 1)
Snider Jul 20, 2026
43e5a56
fix(metal): #61 size encQwenMoEHalf's shared-expert dispatch off its …
Snider Jul 20, 2026
c08a624
feat(qwen35): factory-native vision tower payload + loader (#59 item 1)
Snider Jul 20, 2026
a6b3983
merge lane/sharedwidth: #61 shared-expert decode width — encQwenMoEHa…
Snider Jul 20, 2026
67cefcd
feat(metal): qwen vision tower forward — the ported composed encoder …
Snider Jul 20, 2026
6481bc9
feat(metal): #62 gated-delta recurrent state rides the block codec
Snider Jul 20, 2026
cb1715b
merge lane/gdstate: #62 gated-delta recurrent state rides the block c…
Snider Jul 20, 2026
a7b5671
feat(metal): serve the qwen35 vision tower — image turns on the facto…
Snider Jul 20, 2026
5e5f7c0
docs(metal): retire the stale qwen-vision-gap comments the port closed
Snider Jul 20, 2026
61e0259
merge lane/qvision: #59 qwen35 factory-native vision tower — arch-own…
Snider Jul 20, 2026
66c00a2
feat(engine/metal): serve zoo MoE arches — nil-tolerant decode shape …
Snider Jul 20, 2026
94b5fa2
merge lane/zooserve: #59 zoo MoE arches SERVE — llama-family layer sh…
Snider Jul 20, 2026
9f08d7c
docs(dflash): #52 forward design — real z-lab maths is in-tree and im…
Snider Jul 20, 2026
ae1dec1
fix(metal): #59 wire moeLoadedToBF16 into loadedToBF16 — the bf16 fac…
Snider Jul 20, 2026
44ac5d0
feat(model/arch): z-lab DFlash drafter arch package — config + typed …
Snider Jul 20, 2026
b564cd2
feat(engine/metal): real z-lab DFlash block forward — oracle-gated on…
Snider Jul 20, 2026
0649a02
feat(engine/metal): typed decline when a DFlash drafter dir is loaded…
Snider Jul 20, 2026
a55bc42
merge lane/dflash: #52 the real z-lab DFlash forward — arch package (…
Snider Jul 20, 2026
5584147
docs(qwen-mtp): design the factory-native MTP pair, v1 scope (#59 ite…
Snider Jul 20, 2026
ab69557
feat(qwen35): real config/Arch/weight-name route for the MTP drafter …
Snider Jul 20, 2026
f0bb8a9
docs(purge-plan): #51 measured git-history purge analysis and recomme…
Snider Jul 20, 2026
70bb598
docs: #51 purge plan — measured options (688 MiB -> 114 MiB safe / 32…
Snider Jul 20, 2026
7de02b8
chore: #51 drop examples/bin from HEAD — 783MiB of committed build ou…
Snider Jul 20, 2026
f3dd599
fix(engine/metal): narrow the qwen hybrid MTP-pair decline message (#…
Snider Jul 20, 2026
00d82b2
merge lane/hybridpair: #59 qwen MTP drafter scaffolding — real Parse/…
Snider Jul 20, 2026
06e0cbb
refactor(engine/metal): rename #18 factory chain files off the mislea…
Snider Jul 20, 2026
0db97bf
merge lane/chainrename: #59 honest names for the #18 machinery — fuse…
Snider Jul 20, 2026
22e5e09
chore(metal): #59 rename the three test companions to match their ren…
Snider Jul 20, 2026
80c0ec6
test(engine/metal): real-checkpoint parity receipt for the MoE zoo — …
Snider Jul 20, 2026
326dcac
merge lane/zooparity: #59 the first real-checkpoint zoo receipt — OLM…
Snider Jul 20, 2026
f3c1b5d
feat(examples): rebuild pkg/packed-moe for the factory route (#59 ite…
Snider Jul 20, 2026
a7c9b0c
merge lane/moeexample: #59 the factory packed-MoE example restored — …
Snider Jul 20, 2026
7a3e2be
refactor(engine/metal): #59 pass 2 — rename the last nine composed_ f…
Snider Jul 20, 2026
f13315b
merge lane/chainrename2: #59 renames pass 2 — zero composed_* files r…
Snider Jul 20, 2026
78e28ae
fix(engine/metal): bf16 MoE shared-expert + fused ExpGateUp (#59)
Snider Jul 20, 2026
cfece5d
merge lane/bf16moe: #59 bf16 MoE block parity — shared-expert fields …
Snider Jul 20, 2026
3fbb1e3
fix(jetmoe): restore the named MoA refusal at the factory route (#59 …
Snider Jul 20, 2026
0578bca
merge lane/jetmoe: #59 named MoA refusal restored — jetmoe Config.Arc…
Snider Jul 20, 2026
91eb096
fix(engine): gate tokenizer EOS arming on HasEOSToken (#64)
Snider Jul 20, 2026
9db0203
merge lane/eosfix: #64 undeclared EOS no longer arms token id 0 — EOS…
Snider Jul 20, 2026
361a23a
feat(qwen35): #59 normalisation follow-up — HF processor-parity visio…
Snider Jul 20, 2026
4faae07
merge lane/visionnorm: #59 qwen vision preprocessing matches the decl…
Snider Jul 20, 2026
d1d287d
docs(dflash): reconfirm spec_generate round policy from the real chec…
Snider Jul 20, 2026
4662f5e
fix(engine/metal): per-arch SiLU/GELU selection for the MoE expert co…
Snider Jul 20, 2026
6d50343
docs(metal): design note — qmv_rows general (qmv_impl M-variant) for …
Snider Jul 20, 2026
72ef9f1
merge lane/moeact: #63 per-arch MoE activation — hidden_act parses (o…
Snider Jul 20, 2026
b05f49d
feat(metal): lthn_qmv_rows_general — qmv_impl's M-variant for non-fas…
Snider Jul 20, 2026
dc17232
feat(metal): route unaligned dims onto the general tiled qmv_rows tier
Snider Jul 20, 2026
942276c
test(metal): gate the general qmv_rows tier — byte sweep, key rule, r…
Snider Jul 20, 2026
196a62a
test(metal): laneSet off-envelope pin follows the twin boundary — fol…
Snider Jul 20, 2026
74a1e9e
merge lane/qmvwide: #53 the unaligned-dims multi-row qmv — lthn_qmv_r…
Snider Jul 20, 2026
1db0211
feat(dflash): arm the real z-lab drafter for live serving (#52 §7 ite…
Snider Jul 20, 2026
71c8df6
feat(engine/metal): load-time cache-plan instrument for -kv-cache (#48)
Snider Jul 20, 2026
e9867a1
merge lane/tqinstrument: #48 cache-plan instrument — one always-on lo…
Snider Jul 20, 2026
74b0b94
feat(engine/metal): #59 device-resident bf16 qwen vision tower
Snider Jul 20, 2026
3ba6765
merge lane/vistower: #59 device-resident bf16 qwen vision tower — ~87…
Snider Jul 20, 2026
30382be
feat(dflash): real paired-generate receipt lands; probe withheld with…
Snider Jul 20, 2026
32110a4
merge lane/dflasharm: #52 z-lab pair armed — round policy reconfirmed…
Snider Jul 20, 2026
32fb991
feat(mtp): #53 instrument the rows-moe verify driver + real-26B receipt
Snider Jul 20, 2026
527d3e0
merge lane/mtpdiag: #53 the engagement receipt — driver engages 6/6 l…
Snider Jul 20, 2026
af5232a
test(dflash): #66 capture-vs-oracle receipt — root cause is qwen3 Emb…
Snider Jul 20, 2026
0b42eed
merge lane/capturefix: #66 the capture tap is INNOCENT — root cause i…
Snider Jul 20, 2026
6db2699
fix(qwen3): #66 EmbedScale: 1 — the sole tree-wide omission; the gemm…
Snider Jul 20, 2026
567b697
test(qwen3): #67 all-layers oracle instrument finds layer 35, not con…
Snider Jul 20, 2026
03d5e41
merge lane/qwen3resid: #67 all-layers oracle instrument — config exon…
Snider Jul 20, 2026
5a9387a
fix(engine/metal): q8 ICB batched-dense verify serves K-row blocks by…
Snider Jul 20, 2026
32a1b3a
merge lane/densefold: #53 q8-safe per-row interleave (owner + the pre…
Snider Jul 20, 2026
630cc73
docs(engine/metal): #67 layer-35 divergence - clear decode_forward_ar…
Snider Jul 20, 2026
f63dea0
feat(mtp): the batched fold is the verify for every lane — per-row by…
Snider Jul 20, 2026
7365f0a
merge lane/layer35: #67 hypothesis refuted by trace — no last-layer s…
Snider Jul 20, 2026
14d86b1
feat(mtp): the fused K-row qmm_t head is the verify head — one batche…
Snider Jul 20, 2026
0d45ee5
chore(comments): strip dates/names/narrative from code comments
Snider Jul 20, 2026
19593c3
chore: strip dated and attributed comments from Go code — constraints…
Snider Jul 20, 2026
7279a0b
test(engine/metal): #65 OLMoE oracle instrument names two compounding…
Snider Jul 20, 2026
34deaab
test(metal): #65 OLMoE oracle instrument — per-layer + attn/MLP bisec…
Snider Jul 20, 2026
5a99fcf
test(metal): #65 oracle diagnostics opt-in via LTHN_OLMOE_ORACLE — th…
Snider Jul 20, 2026
18479e3
perf(metal): #53 content-keyed boundary-greedy cache — the greedy ver…
Snider Jul 21, 2026
cdd9a74
docs(metal): verify-fold comments state the current default — both ti…
Snider Jul 21, 2026
b2e55f8
docs(perf): #53/#48 receipts — verify round decomposition, the 210 cl…
Snider Jul 21, 2026
3448b99
docs(perf): #53 the 230 is real — reproduced at 248 (+42%) on the ori…
Snider Jul 21, 2026
a4c6fab
feat(serving): draft ladder resolves the family assistant from the HF…
Snider Jul 21, 2026
694f460
fix(metal): #65 select QK-norm granularity by loaded weight length
Snider Jul 21, 2026
99a5f68
fix(metal): #65 wire NormaliseMoETopK through the router combine-weig…
Snider Jul 21, 2026
eab0da5
test(metal): #65 flip the OLMoE oracle to always-on; docs: update the…
Snider Jul 21, 2026
8414ffb
Merge lane/olmoe — #65 OLMoE QK-norm granularity + router combine-wei…
Snider Jul 21, 2026
6c1976b
test(metal): #65 bisection oracle re-extracted residual-inclusive — a…
Snider Jul 21, 2026
2dac9cd
fix(gemma4): declare NormaliseMoETopK — softmax-over-selected ≡ the r…
Snider Jul 21, 2026
8a88547
fix(qwen3): forward hidden_act into Arch.Activation — undeclared it r…
Snider Jul 21, 2026
b3fd6f7
perf(metal): fused fp32-internal silu(gate)·up kernel — one bf16 roun…
Snider Jul 21, 2026
3ed0fb9
test(metal): #67 resolved — layer-selectable MLP op probe, layer-35 o…
Snider Jul 21, 2026
b4a92c4
fix(metal): silu upgrade reaches the chain target and the host refere…
Snider Jul 21, 2026
6ff520d
fix(metal): dflash z-lab accept-rate re-measured post-#67; anchor pha…
Snider Jul 21, 2026
9052daa
Merge lane/dflash — #52 accept-rate measured post-#67; anchor-context…
Snider Jul 21, 2026
bf284ae
feat(metal): whole-layer-stack MTP verify ICB — interior [1,L-2] reco…
Snider Jul 21, 2026
d0041d1
Merge lane/verifyicb — whole-stack verify ICB: interior layers replay…
Snider Jul 21, 2026
c554f19
fix(metal): verify-stack ICB opt-in until its replay race is fixed — …
Snider Jul 21, 2026
01f0be3
fix(metal): verify-stack replay exonerated — the parity flake is the …
Snider Jul 21, 2026
f5f6c75
docs(perf): #48 KV-mode ladder decided — q8 default, turboquant is th…
Snider Jul 21, 2026
4ed59cb
docs(perf): #48 matrix re-certified on a quiet box — the 84.2/122.6 c…
Snider Jul 21, 2026
723c28f
feat(hip): qwen3_moe native forward pass — attention, router, SwiGLU …
Snider Jul 21, 2026
20487ae
fix(metal): restore the #55 byte-exact greedy verify routing — the MT…
Snider Jul 21, 2026
bf6b87e
feat(metal): whole-stack verify ICB default-on — LTHN_VERIFY_STACK_IC…
Snider Jul 21, 2026
ef39227
fix(hip): qwen3_moe NeoX rope convention + full test suite (CPU + gfx…
Snider Jul 21, 2026
03c73fa
fix(metal): TQ prefill scratch capacity-plan to actual need, not maxLen
Snider Jul 21, 2026
f65c28d
merge(hip): qwen3_moe native forward pass — lane/hipmoe
Snider Jul 21, 2026
f87ad2e
Merge branch 'lane/tqalloc' into dev
Snider Jul 21, 2026
5daa587
docs(perf): bistability root-cause corrected + TQ-only allocation ret…
Snider Jul 21, 2026
0959e9e
feat(engine): ChatRetained — a Chat-equivalent turn over a caller-ret…
Snider Jul 21, 2026
2867865
fix(generate): kill the double arch decode-state build in the warm+ti…
Snider Jul 21, 2026
df4f380
test(generate): pin the retained-session build count and its fallbacks
Snider Jul 21, 2026
cf869c0
Merge branch 'lane/genfix' into dev
Snider Jul 21, 2026
7c27d00
fix(model): refuse a wrong-shape q_norm/k_norm at Assemble load time
Snider Jul 21, 2026
150473c
fix(metal): ICB recorder refuses whole-vector QK-norm, not just docum…
Snider Jul 21, 2026
823b531
test(metal): router normalise=false unit coverage + gemma4 byte-guard
Snider Jul 21, 2026
3cd80b8
docs(metal): retire the stale #59 GELU/SiLU skip message; note the #6…
Snider Jul 21, 2026
7ebcd54
Merge branch 'lane/olmoefix' into dev
Snider Jul 21, 2026
8106e2a
feat(metal): q8 KV on sliding owners — the cache is uniform, not a pe…
Snider Jul 21, 2026
38fd9af
test(metal): sliding-ring q8 fixtures — wrap, sharers, chunk seams, d…
Snider Jul 21, 2026
a478456
feat(metal): q8 KV for headDim-128 — the Qwen3/OLMoE-class geometry arms
Snider Jul 21, 2026
c22d978
test(metal): hd-128 q8 fixture — geometry-parameterised sliding fixture
Snider Jul 21, 2026
9e2a61d
test(metal): TQ residency receipt pins a genuinely bf16 twin
Snider Jul 21, 2026
b2974a8
perf(metal): rows-moe verify — one submission per stage, not per expe…
Snider Jul 21, 2026
3fd9a81
perf(metal): device-routed rows-moe verify — one command buffer per M…
Snider Jul 21, 2026
89c19f4
perf(metal): chained rows-moe verify round — one submission per round
Snider Jul 21, 2026
dd11bfa
perf(metal): fold K-row attention into the chained verify round
Snider Jul 21, 2026
d8c2bba
perf(metal): overlap the chained verify round — chunked commits, batc…
Snider Jul 21, 2026
2be9f9f
perf(metal): arch-aware MTP draft block default — quant-MoE 6, dense 5
Snider Jul 21, 2026
c9f691f
docs(dflash): accept-rate gap bisected to numeric regime — instrument…
Snider Jul 21, 2026
75701cf
perf(metal): rearm the verify fold for the greedy lane — one determin…
Snider Jul 21, 2026
7e4f1f5
test(metal): verify-stack ICB NaN bisect instruments — uniform-4-bit …
Snider Jul 21, 2026
a35c395
test(metal): #71 replay-parity pin + record-arm debug log
Snider Jul 21, 2026
4179ccb
test(metal): #71 bisect instruments + two green parity micros — q8 la…
Snider Jul 22, 2026
980681a
test(metal): #71 skip lever -> CSV + overlap-consume + per-axis rms t…
Snider Jul 22, 2026
c0cb5b8
test(metal): #71 bind-level trace + own-encoder probe + 2-layer rms/a…
Snider Jul 22, 2026
ebdc8e7
fix(metal): #71 ROOT CAUSE — AGX ICB commands mis-encode buffer offse…
Snider Jul 22, 2026
a69948e
feat(metal): #72 re-enable whole-stack verify replay on >4GiB shards …
Snider Jul 22, 2026
2ef2cf4
fix(metal): #72 audit — every ICB record sink guards >=2^32 bind offsets
Snider Jul 22, 2026
16b2c56
test(metal): #73 12B pair greedy-parity stage bisect — divergence is …
Snider Jul 22, 2026
dc2ab6c
fix(model): accept index-less sharded checkpoints in both shard spell…
Snider Jul 22, 2026
a775d22
fix(model): alias the model.language_model wrapper layout so Qwen3.5 …
Snider Jul 22, 2026
0b33e3f
merge: qwen shard discovery + multimodal wrapper nesting (lane/qwen-s…
Snider Jul 22, 2026
2df5d3c
test(metal): #73 resolved — 12B bisect becomes a characterisation pin
Snider Jul 22, 2026
a9ed22b
docs: keep the manual, move working state out
Snider Jul 22, 2026
63deceb
docs: drop references to relocated working-state files
Snider Jul 22, 2026
0362a0b
build(cli): require dappco.re/go/html v0.11.0 — the .ctml TUI surface
Snider Jul 22, 2026
8457b2c
feat(tui): render the tab strip from .ctml with teabox mouse switching
Snider Jul 22, 2026
0618a42
merge: tab strip on .ctml — slice 1 of the TUI migration (lane/ctml-t…
Snider Jul 22, 2026
33b907c
merge: absorb main's line post-purge — history unification, tree unch…
Snider Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ go/lem

# task build:embed output
/bin/
bench/last-run.json

# generated at build:embed time — never commit compiled metallibs
cli/*.metallib.gz

# built example binaries — never commit build output (the 783MiB HEAD ballast, #51)
examples/bin/
77 changes: 77 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# CI for go-inference on the git.lthn.sh homelab runner (shell executor, AMD box,
# concurrent = 1 → one job at a time). The junctures come from the shared gates in
# dappcore/devops/build — the gate owns WHEN, this file owns WHAT:
#
# push (gate/push.yml) → build: compile bin/lem for linux/amd64 (engine/hip cgo).
# pr (gate/pr.yml) → lint → test: gofmt+vet, then the portable suite. Test is
# skipped if lint fails; "Pipelines must succeed" gates the merge.
# release (gate/release.yml) → per-backend CLI binaries, on a git tag only.
# graphify (gitlab/graphify.yml) → auxiliary code-graph refresh (dev/main, allow_failure).
include:
- { project: 'dappcore/devops/build', file: '/gate/push.yml', ref: dev }
- { project: 'dappcore/devops/build', file: '/gate/pr.yml', ref: dev }
- { project: 'dappcore/devops/build', file: '/gate/release.yml', ref: dev }
- { project: 'dappcore/devops/build', file: '/gitlab/graphify.yml', ref: dev }

# Run one pipeline per change: MR pipelines for MRs, tag pipelines for releases,
# branch pipelines otherwise — and never a redundant branch pipeline while an MR
# is open for that branch.
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_TAG'
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never
- if: '$CI_COMMIT_BRANCH'

variables:
GOTOOLCHAIN: "auto" # box go is 1.22; go.mod's 1.26 directive auto-fetches
GIT_SUBMODULE_STRATEGY: none # a plain Go compile/test needs no vendored C sources

# --- PUSH: standard flow — does it still build? ----------------------------
build:
extends: .gate-push
script:
- go version
- make build

# --- PR gate: lint THEN test (the test stage is skipped if lint fails) ------
lint:
extends: .gate-lint
script:
- cd go
- test -z "$(gofmt -l .)"
- go vet ./...

test:
extends: .gate-test
script:
- cd go
- go test -count=1 ./...

# --- RELEASE: per-backend CLI binaries, on a tag ---------------------------
.release-cli:
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA"
paths: [ build/bin/ ]
expire_in: 1 week

lem-cpu-x86:
extends: [.gate-release, .release-cli]
script: [ make lem-cpu-x86 ]

lem-cpu-aarch64:
extends: [.gate-release, .release-cli]
script: [ make lem-cpu-aarch64 ]

lem-amd:
extends: [.gate-release, .release-cli]
variables:
GIT_SUBMODULE_STRATEGY: normal # GitLab inits the ROCm sources for the static archives
script: [ make lem-amd ]

lem-cuda:
extends: [.gate-release, .release-cli]
variables:
GIT_SUBMODULE_STRATEGY: normal
script: [ make lem-cuda ]
13 changes: 13 additions & 0 deletions .graphifyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# .graphifyignore — scope the go-inference knowledge graph to OUR code.
#
# Same syntax as .gitignore (incl. ! negation). graphify already auto-respects
# .gitignore, so /build/, /bin/, .core/, *.test etc. are skipped. This file
# only ADDS exclusions.
#
# external/ is vendored mlx + ROCm C/C++ (git submodules — therefore NOT in
# .gitignore). It's a dependency, not go-inference's own code; indexing it
# roughly doubled the graph with headers like amd_hip_fp6.h / OCLBuffer*.h.
external/

# JS deps under the GUI (normally gitignored already; explicit for safety).
gui/node_modules/
16 changes: 14 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The sovereign inference engine for the Core Go ecosystem. Module: `dappco.re/go/
1. **The shared contract** (`go/*.go`) — `TextModel`/`Backend` interfaces, options, registry, discovery. Backends import this; never the reverse.
2. **The engines** (`go/engine/`) — `engine/metal` (package `native`): the **no-cgo Apple GPU engine** (darwin/arm64, objc bridge via `github.com/tmc/apple`, ICB-replayed decode, MTP speculative pairs, paged SDPA, q8 KV); `engine/hip`: AMD ROCm (linux/amd64). Plus `cmd/lem` (the serving/bench binary), OpenAI/Anthropic/Ollama compat handlers, and conversation state (`-state`, no-prompt-replay).

`docs/` at repo root is the manual: `architecture.md`, `backends.md` (registry + **engine runtime levers**), `cmd-lem.md`, `build.md`, and **`handover.md` — read that one first; it is the working handover from the engine-perf campaigns.**
`docs/` at repo root is the manual: `architecture.md`, `backends.md` (registry + **engine runtime levers**), `cmd-lem.md`, `build.md`, `history.md`. Working state (handovers, design drafts, receipts, campaign perf notes) lives with the orchestrating agent, not here.

## Commands

Expand All @@ -22,7 +22,8 @@ go test -count=1 ... # ALWAYS -count=1 for benchmarks — go test caches

# kernel changes (from repo root)
task metallib:kernels # 31 .metal files -> build/dist/lib/lthn_kernels.metallib
gzip -9 -c build/dist/lib/lthn_kernels.metallib > go/cmd/lem/lthn_kernels.metallib.gz # then rebuild lem
# metallibs are NEVER committed — cli/*.metallib.gz are generated by `task build:embed`
# (gitignored); the plain build resolves them externally via MLX_METALLIB_PATH

# bench shape (model path is POSITIONAL, last)
../bin/lem generate -draft <assistant-path> -temp 0 -max-tokens 400 [-context 20480 -prompt-file <f>] <model-path>
Expand All @@ -38,6 +39,17 @@ gzip -9 -c build/dist/lib/lthn_kernels.metallib > go/cmd/lem/lthn_kernels.metall
- **No bulk perl/sed refactors** — one site at a time, vet after each.
- Branch **dev**; origin `github.com/dAppCore/go-inference` (push non-force). Task board via the session task tools; git log is the history of record.

## Qwen hybrid — the FACTORY is the ONLY route (#18/#50)

Since 2026-07-19 (`ae095829`) **qwen3_5 / qwen3_5_moe load through the factory** (`model.Assemble` + `arch_session`) with the fused whole-token chain decode (`engine/metal/arch_qwen_fused.go`): every layer (gated-delta / gated-attention, dense or MoE tail) encodes into ONE command buffer with resident state, CB-recorded and replayed per token where servable. It **beat the composed lane on both local hybrids** — 0.8B 217 vs 195 tok/s greedy, 35B-A3B 30 vs 19 — and #50 (2026-07-20) deleted `model/composed` outright: the factory is no longer the default, it is the only route, for every arch.

- **Routing** (`engine/metal/load.go`): every registered arch through `model.Load`; mamba2/rwkv7 by name to their own loaders. Typed declines name the retired-composed capability gaps: sub-2-bit packs (Bonsai 1-bit — no factory qmv width, #24), the qwen MTP `-draft` pairing (`LoadSpeculativePair` declines hybrids; factory pair route pending), and image turns on the vision-towered 35B (factory serves the TEXT stack; the engine's not-a-vision-model refusal answers). `LTHN_QWEN_FUSED=0` forces the factory's host halves (the correctness reference). `LTHN_QWEN_COMPOSED` is gone with the lane.
- **The submit-ahead decode tails decline recurrent sessions** (`hasRecurrentLayers`): a speculative link past a stop cannot be unwound from a gated-delta recurrence. Serial tail only.
- **Replay bookkeeping**: `composedChainReplay` (historic name — it is the factory chain's recorder) advances the `attnKVDeviceState.n` counters ITSELF — bumping them again host-side double-advances the position and silently corrupts KV slot addressing (coherent-but-wrong text; caught by the live-walk A/B).
- **Activation is per-arch, not per-kernel.** `MoEExpertsQuant` is gemma's **GELU**; the qwen lane is **SiLU** (`MoEExpertsQuantSiLU`). Wrong binding = *coherent-but-wrong* text below the greedy argmax threshold. Gate on the byte-level test (`TestMoEExpertsQuantSiLU`), never on "the output reads fine".
- **A/B recipe** (from a worktree, GPU free): `task build` → `bin/lem`; `MLX_METALLIB_PATH=build/dist/lib/mlx.metallib bin/lem generate -temp 0 -max-tokens N <snapshot>`; greedy output is the correctness oracle. Run default vs `LTHN_QWEN_FUSED=0`.
- **#50 residue** (see `go/docs/composed-strip-inventory.md` for the full classification): the `composed_*`-named files in engine/metal ARE the factory chain machinery (rename = follow-up); `examples/pkg/packed-moe` retired with the composed API it demonstrated (a factory packed-MoE example is the follow-up); `examples/pkg/hybrid-quant` serves through the factory unchanged. Next gap to close: mlx-lm ~110 tok/s on the 35B vs our 30 — a unified-engine campaign.

## Stability Rules (root contract)

Changes to `go/*.go` interfaces affect every consumer simultaneously.
Expand Down
51 changes: 44 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ AMD_GOARCH ?= amd64
CUDA_GOARCH ?= amd64
CPU_X86_GOARCH ?= amd64
CPU_AARCH64_GOARCH ?= arm64
CPU_AARCH64_CC ?= aarch64-linux-gnu-gcc
CPU_AARCH64_CXX ?= aarch64-linux-gnu-g++
AMD_CGO_ENABLED ?= 1
CUDA_CGO_ENABLED ?= 1
CPU_CGO_ENABLED ?= 0
# DuckDB (the go-store/chathistory driver) requires cgo — like engine/hip,
# the linux lanes build with cgo on. Only the Apple metal engine is no-cgo.
CPU_CGO_ENABLED ?= 1
RELEASE_BINS := $(CLI_NAME)-amd $(CLI_NAME)-cuda $(CLI_NAME)-cpu-x86 $(CLI_NAME)-cpu-aarch64
RELEASE_ARCHIVES := $(addsuffix -linux.tar.gz,$(RELEASE_BINS))
RELEASE_SIDECARS = $(AMD_KERNEL_MODULE_NAME) $(CUDA_KERNEL_MODULE_NAME) $(CPU_X86_KERNEL_MODULE_NAME) $(CPU_AARCH64_KERNEL_MODULE_NAME)
Expand Down Expand Up @@ -112,10 +116,13 @@ HIP_CPU_INCLUDE ?= /opt/hip-cpu/include
HIP_CPU_CXX ?= g++
HIP_CPU_AARCH64_CXX ?= aarch64-linux-gnu-g++
HIP_CPU_STD ?= c++20
HIP_PRODUCTION_GGUF ?=
HIP_PRODUCTION_HSACO ?= $(AMD_KERNEL_MODULE)
HIP_PRODUCTION_MOE ?= 0

.PHONY: all help build build-cli lem-rocm lthn-rocm named-binaries release-binaries release-dependency-guard release-artifacts dist static-hip-binaries rocr-cmake-shims hsa-static-archive hip-static-archive require-static-hip-archive hip-link-info lem-amd lem-cuda lem-cpu-x86 lem-cpu-aarch64 lthn-amd lthn-cuda lthn-cpu-x86 lthn-cpu-aarch64 test test-cli test-all clean \
hip hip-amd hip-nvidia hip-cpu hip-cpu-x86_64 hip-cpu-aarch64 \
test-hip-amd test-hip-nvidia test-hip-cpu test-hip-cpu-runtime test-hip-cpu-kernel-runtime test-zluda-cuda \
test-hip-amd test-hip-nvidia test-hip-cpu test-hip-cpu-runtime test-hip-cpu-kernel-runtime test-zluda-cuda test-hip-production \
compile-matrix test-matrix

all: build
Expand All @@ -131,6 +138,7 @@ help:
' named-binaries build all named release binaries' \
' release-artifacts build archives and checksums under $(DIST_DIR)' \
' test run the Go module test suite' \
' test-hip-production run the AMD Gemma4 release receipt (set HIP_PRODUCTION_GGUF)' \
' test-matrix run the host suite plus every hip toolchain smoke, skipping what is absent' \
' clean remove $(BUILD_DIR)'

Expand Down Expand Up @@ -230,11 +238,12 @@ rocr-cmake-shims:
' set_target_properties(llvm-objcopy PROPERTIES IMPORTED_LOCATION "$(ROCR_LLVM_OBJCOPY)")' \
'endif()' > "$(ROCR_CMAKE_SHIM_DIR_ABS)/llvm/LLVMConfig.cmake"

hsa-static-archive: rocr-cmake-shims
@test -d "$(ROCR_RUNTIME_SOURCE_DIR_ABS)" || { echo "missing ROCr runtime source submodule: $(ROCR_RUNTIME_SOURCE_DIR)"; exit 1; }
hsa-static-archive:
@test -f "$(ROCR_RUNTIME_SOURCE_DIR_ABS)/CMakeLists.txt" || { echo "ROCr runtime source submodule is not initialized: $(ROCR_RUNTIME_SOURCE_DIR)"; exit 1; }
@test -n "$(DRM_AMDGPU_STATIC_ARCHIVE)" || { echo "missing static libdrm_amdgpu.a; install libdrm-amdgpu-dev"; exit 1; }
@test -n "$(DRM_STATIC_ARCHIVE)" || { echo "missing static libdrm.a; install libdrm-dev"; exit 1; }
@test -n "$(ELF_STATIC_ARCHIVE)" || { echo "missing static libelf.a; install libelf-dev"; exit 1; }
$(MAKE) --no-print-directory rocr-cmake-shims
$(CMAKE) -S "$(ROCR_RUNTIME_SOURCE_DIR_ABS)" -B "$(ROCR_RUNTIME_BUILD_DIR_ABS)" -G "$(CMAKE_GENERATOR)" \
-DBUILD_SHARED_LIBS=OFF \
-DClang_DIR="$(ROCR_CMAKE_SHIM_DIR_ABS)/clang" \
Expand All @@ -247,8 +256,8 @@ hsa-static-archive: rocr-cmake-shims
@test -s "$(ROCR_HSAKMT_STATIC_ARCHIVE)" || { echo "expected static HSAKMT archive was not produced: $(ROCR_HSAKMT_STATIC_ARCHIVE)"; exit 1; }

hip-static-archive:
@test -d "$(HIP_API_SOURCE_DIR_ABS)" || { echo "missing HIP API source submodule: $(HIP_API_SOURCE_DIR)"; exit 1; }
@test -d "$(HIP_RUNTIME_SOURCE_DIR_ABS)" || { echo "missing HIP runtime source submodule: $(HIP_RUNTIME_SOURCE_DIR)"; exit 1; }
@test -d "$(HIP_API_SOURCE_DIR_ABS)/include" || { echo "HIP API source submodule not checked out: $(HIP_API_SOURCE_DIR) (ROCm/HIP is the HIP_COMMON_DIR headers repo — it has no CMakeLists.txt)"; exit 1; }
@test -f "$(HIP_RUNTIME_SOURCE_DIR_ABS)/CMakeLists.txt" || { echo "HIP runtime source submodule is not initialized: $(HIP_RUNTIME_SOURCE_DIR)"; exit 1; }
$(CMAKE) -S "$(HIP_RUNTIME_SOURCE_DIR_ABS)" -B "$(HIP_RUNTIME_BUILD_DIR_ABS)" -G "$(CMAKE_GENERATOR)" \
-DCLR_BUILD_HIP=ON \
-DCLR_BUILD_OCL=OFF \
Expand Down Expand Up @@ -294,7 +303,7 @@ lem-cpu-x86: hip-cpu-x86_64

lem-cpu-aarch64: hip-cpu-aarch64
mkdir -p "$(BIN_DIR_ABS)"
CGO_ENABLED=$(CPU_CGO_ENABLED) GOOS=$(TARGET_GOOS) GOARCH=$(CPU_AARCH64_GOARCH) $(GO) -C "$(CLI_SUBTREE)" build -ldflags "$(CLI_GO_LDFLAGS)" -o "$(BIN_DIR_ABS)/$(CLI_NAME)-cpu-aarch64" "$(CLI_CMD)"
CGO_ENABLED=$(CPU_CGO_ENABLED) CC=$(CPU_AARCH64_CC) CXX=$(CPU_AARCH64_CXX) GOOS=$(TARGET_GOOS) GOARCH=$(CPU_AARCH64_GOARCH) $(GO) -C "$(CLI_SUBTREE)" build -ldflags "$(CLI_GO_LDFLAGS)" -o "$(BIN_DIR_ABS)/$(CLI_NAME)-cpu-aarch64" "$(CLI_CMD)"

test:
$(GO) -C "$(GO_SUBTREE)" test ./... -count=1
Expand Down Expand Up @@ -344,6 +353,34 @@ test-hip-cpu-kernel-runtime:
test-zluda-cuda:
GO_ROCM_RUN_ZLUDA_CUDA_TESTS=1 CUDA_PATH="$(CUDA_PATH)" CUDA_HOME="$(CUDA_HOME)" $(GO) -C "$(GO_SUBTREE)" test ./engine/hip -run TestHIPKernelSource_ZLUDACUDARuntimeSmoke_Good -count=1

test-hip-production:
@test -n "$(strip $(HIP_PRODUCTION_GGUF))" || { echo "HIP_PRODUCTION_GGUF must name a local Gemma4 GGUF"; exit 1; }
@test -f "$(HIP_PRODUCTION_GGUF)" || { echo "HIP production GGUF not found: $(HIP_PRODUCTION_GGUF)"; exit 1; }
$(MAKE) --no-print-directory hip-amd
@test -f "$(HIP_PRODUCTION_HSACO)" || { echo "HIP production HSACO not found: $(HIP_PRODUCTION_HSACO)"; exit 1; }
$(GO) -C "$(GO_SUBTREE)" test ./engine/hip -count=1 -run '^TestScheduler_'
@set -o pipefail; \
gguf="$$(realpath "$(HIP_PRODUCTION_GGUF)")"; \
hsaco="$$(realpath "$(HIP_PRODUCTION_HSACO)")"; \
tests='TestNativeDecodeSmokeKernelStatus_Good|TestHIPGemma4ExactStateContinuityHardware_Good|TestHIPLaneSetE2BHardwareMatchesSingleLanes_Good'; \
if [ "$(HIP_PRODUCTION_MOE)" = "1" ]; then \
tests="$$tests|TestHIPLaneSet26BMoEHardwareMatchesSingleLanes_Good"; \
fi; \
log="$$(mktemp)"; \
trap 'rm -f "$$log"' EXIT; \
HIP_VISIBLE_DEVICES="$${HIP_VISIBLE_DEVICES:-0}" \
GO_ROCM_RUN_MODEL_TESTS=1 \
GO_ROCM_RUN_MOE_LANE_TESTS="$(HIP_PRODUCTION_MOE)" \
GO_ROCM_MODEL_PATH="$$gguf" \
GO_ROCM_KERNEL_HSACO="$$hsaco" \
$(GO) -C "$(GO_SUBTREE)" test ./engine/hip -count=1 -v -run "^($$tests)$$" | tee "$$log"; \
status="$${PIPESTATUS[0]}"; \
if grep -q -- '--- SKIP:' "$$log"; then \
echo "HIP production receipt skipped a required hardware test"; \
exit 1; \
fi; \
exit "$$status"

# test-matrix aggregates the host suite plus every hip toolchain smoke behind
# one command. Each leg probes its own toolchain first and skips with a
# one-line reason when it is absent, so the verb runs unattended on any box
Expand Down
6 changes: 6 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ tasks:
cmds:
- go test -tags metal_runtime -count=1 ./...

bench:grid:
desc: "Run the locked gemma4 benchmark grid (bench/gemma4.json) via the lem bench verb. Needs build/dist/lib/*.metallib — run task metallib first."
dir: cli
cmds:
- MLX_METALLIB_PATH={{.ROOT_DIR}}/build/dist/lib/mlx.metallib go run -tags metal_runtime . bench --config {{.ROOT_DIR}}/bench/gemma4.json --json {{.ROOT_DIR}}/bench/last-run.json

cover:
desc: "Portable coverage -> coverage.out (the profile codecov.yml reads; 95% target). Prints the total on the last line."
dir: go
Expand Down
34 changes: 34 additions & 0 deletions bench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!-- SPDX-Licence-Identifier: EUPL-1.2 -->

# bench/ — the reproducible benchmark grids

`lem bench --config bench/gemma4.json --json out.json` runs the locked house
grid: per model × lane (plain, mtp), one untimed warmup then one timed greedy
tg-512, decode tok/s from the engine's own metrics. Model refs are HF cache
repo names, so the same config reproduces the grid on any box sharing the
cache convention (the metal Mac, the hip AMD box) — edit the JSON to shift
focus, never the code.

Notes baked into gemma4.json:
- Drafters are EXPLICIT (auto-detection ambiguity broke the shell sweep this
verb replaces) and quant-matched where possible — the 2026-07-16 matrix
measured E4B's matched QAT draft at +47% vs +11% for the same draft
cross-paired onto the non-QAT base. e4b-4bit therefore runs plain-only.
- The 26B rows keep their mtp lane deliberately: the −70% MoE-verify loss is
#372's open tracking number, and the grid should show it until it's fixed.
- The default prompt (counting to 800) is the house tg-N workload: maximally
predictable output, so MTP acceptance reads at its CEILING — right for
comparability with the reference tables, wrong for estimating real-workload
gains (creative text measured 8-20% acceptance on the same pair, i.e. MTP
roughly break-even there). Bench a workload by shifting the prompt in a
config copy.
- QAT rows decode SLOWER than plain 4bit by design, not by engine defect: the
mlx-community `qat-4bit` checkpoints are mixed-precision — every
`mlp.{gate,up,down}_proj` carries a per-tensor 8-bit override (config.json
`quantization`), attention/embed/head stay 4-bit, group size 64 throughout.
Decode is weight-bandwidth-bound, so the extra bytes ARE the slowdown:
E2B 4.33 vs 3.55 GB (+22%) → measured −16%, E4B 6.80 vs 5.15 GB (+32%) →
−21%, 31B 28.8 vs 18.4 GB (+57%) → −32% — each at or slightly better than
the pure byte-ratio prediction (−18/−24/−36%), which is the receipt that the
q8 tensors ride the fast qmv path. Pick QAT for quality, plain 4bit for
speed; a grid row comparing them measures checkpoint mass, not the engine.
16 changes: 16 additions & 0 deletions bench/gemma4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"prompt": "Write the integers from 1 to 800 separated by single spaces. Output only the numbers, nothing else.",
"tokens": 512,
"warmup": 16,
"runs": [
{"name": "e2b-4bit", "model": "mlx-community/gemma-4-e2b-it-4bit", "draft": "mlx-community/gemma-4-E2B-it-assistant-bf16"},
{"name": "e2b-qat4", "model": "mlx-community/gemma-4-E2B-it-qat-4bit", "draft": "mlx-community/gemma-4-E2B-it-assistant-bf16"},
{"name": "e4b-4bit", "model": "mlx-community/gemma-4-e4b-it-4bit"},
{"name": "e4b-qat4", "model": "mlx-community/gemma-4-E4B-it-qat-4bit", "draft": "mlx-community/gemma-4-E4B-it-qat-assistant-4bit"},
{"name": "12b-4bit", "model": "mlx-community/gemma-4-12B-it-4bit", "draft": "mlx-community/gemma-4-12B-it-assistant-bf16"},
{"name": "26b-4bit", "model": "mlx-community/gemma-4-26b-a4b-it-4bit", "draft": "mlx-community/gemma-4-26B-A4B-it-qat-assistant-4bit"},
{"name": "26b-qat4", "model": "mlx-community/gemma-4-26B-A4B-it-qat-4bit","draft": "mlx-community/gemma-4-26B-A4B-it-qat-assistant-4bit"},
{"name": "31b-4bit", "model": "mlx-community/gemma-4-31B-it-4bit", "draft": "mlx-community/gemma-4-31B-it-assistant-bf16"},
{"name": "31b-qat4", "model": "mlx-community/gemma-4-31B-it-qat-4bit", "draft": "mlx-community/gemma-4-31B-it-assistant-bf16"}
]
}
Loading