Compare commits

..
Author SHA1 Message Date
Frank d5a43b7192 Merge remote-tracking branch 'origin/main' into wt/20261018-204501 2026-10-18 21:28:04 -07:00
Frank d457985b8d docs(frank-zealot): measured exact-KV boot, floor rates, chat-path instrument
Deployed shape verified live: KV 16,384.00 MiB (ledger-exact 32,768 B/token x
524,288), RS 100.50 MiB (2 cells), settled nvidia-smi 21,176 used / 2,949 free,
n_slots 2, n_ctx_slot 262144, DropInPaths empty.

Rates recorded as FLOORS: another consumer drove the endpoint through the whole
window (56 prompts on the other slot in three minutes, 96% GPU-util). Single
stream 134.1-141.5 (145.8/163.2 in a lighter minute) vs the 168.7 q8_0 record;
two-stream aggregate 256.6-265.1 vs ~300 across three; prefill 3,159.5 at
106,282 beats the 2,877 record while contended; decode at 106k depth 71.5 (new
baseline, no prior). Per-stream concurrency improved: ~130 each vs 102.8.

Instrument correction: the PrismML fork build 10777 answers 500 'The model
produced output that does not match the expected Content-only format' for a
greedy ignore_eos run on the raw /completion path, so the per-request timings
must be read off /v1/chat/completions. AGENTS.md Benchmarking Policy corrected.
2026-10-18 21:28:04 -07:00
dank 5550d32a2e Merge pull request 'fix(frank-zealot): 2 × 262,144 over unquantized f16 KV' (#21) from fix/zealot-2x262144-f16-kv into main 2026-10-18 21:06:08 -07:00
Frank 71bc1735d3 docs(frank-zealot): re-key the exact-KV shape across the vault
AGENTS.md: topology row, the frank-zealot runbook section (shape, KV
accounting, MTP verdict, compaction-hub concurrency), and two Key
Lessons bullets -- the draft-context bill restated with the real knob
names, plus a new lesson that a quant's scale overhead belongs in its
byte rate (the 6.25 % invented by doubling 13,056.00 MiB).

LLM/frank-zealot-llama-prism-bonsai9-mtp.md: retitled to the live shape;
the 3-slot q8_0 boot ledger and its measurements are relabelled as the
arm this replaced (byte-identical numbers, honest attribution), the
corrected f16 rate derivation is added, the MTP census is labelled
q8_0-era, the fallback rungs are re-derived for the 524,288 pool, and the
boot-ledger section carries an explicit PROJECTION that must be replaced
with the measured ledger from the first f16 boot.

Pointers re-pointed (live-shape sentences only; every historical ledger
left byte-identical): LLM/index.md, vault index.md, Dankclaw/OpenWebUI.md,
and the retired-runbook "today" pointers in mimo-q5km, mimo-q6k,
qwen35-9b-q6k, swift15, sglang-mimo-v26. log.md gains the dated entry.
2026-10-18 21:02:42 -07:00
Frank 68afc6dbaf fix(frank-zealot): 2 x 262,144 over unquantized f16 KV
Frank: "shift frank-zealot to 2 parallels of unquantized kv".

Shape: CTX_SIZE 786432 -> 524288, PARALLEL 3 -> 2, CACHE_TYPE_K/V
q8_0 -> f16. The per-slot window is UNCHANGED at 262,144 (the native
trained window, and models.yml contextWindow), so what the exact cache
buys is fidelity and what it pays is one concurrent conversation.

The POOL is the term that had to move: exact KV bills 32,768 B/token at
this geometry (8 full-attention layers x 4 KV heads x 256 dim, 2 B,
K and V), so the old 786,432 pool asks 24,576 MiB against the 24,123 MiB
the driver leaves addressable. 524,288 positions = 16,384 MiB, projected
~21,200 used / ~2,900 free from the 13:19 boot ledger (only the KV pool
and one recurrent-state cell move).

Also corrects a repo-wide arithmetic error: "f16 would bill 26,112 MiB"
doubled the q8_0 ledger figure INCLUDING its 1/32 scale overhead. q8_0 is
1.0625 B/element, f16 exactly 2.0, so the true ratio is 1.882x. The wall
stands; the number was wrong.

And it corrects a false claim about the drafter: this build DOES expose
--spec-draft-type-k / --spec-draft-type-v (verified against
llama-server --help), so the draft cache is quantizable -- the
exact-KV-plus-MTP exclusion is a measurement gap, not a law, and the
1,088 MiB quantized-draft arm is recorded as unmeasured.

Seam: tests/frank_zealot_unit.bats re-pins the shape contract (2 slots,
524,288 pool, 262,144 per slot) and adds a VRAM fit gate that computes
dtype x pool + the measured non-KV ledger terms against 24,123 MiB with
>=1 GiB of slack -- and proves the gate REFUSES exact KV at the old
pool, so passing means something. 18/18 green.
2026-10-18 21:02:37 -07:00
dank 100b62ba22 Merge pull request 'docs(openwebui): the TTS voice rejector is in-browser Kokoro, not frank-tts' (#20) from docs/openwebui-browser-kokoro-tts into main 2026-10-18 20:38:50 -07:00
16 changed files with 594 additions and 280 deletions
+94 -56
View File
@@ -27,7 +27,7 @@ merge absorbed the former `vllmScripts` repo, history preserved).
|--------|---------|---------|-------|------|
| Dankserver | `frank-archon` | **radiance** (StillDeadcode, **pin 1.0.13** digest-pinned `stilldeadcode/radiance@sha256:4c3f484564…`, plain `podman run`, root systemd unit) | **Publisher payload** (swap 2026-10-15, MODEL_FILE-only): `StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe` → **`qwen3.8-next-flash-fp8-iq4r-moe.rad`** (121,969,901,568 B = **116,322 MiB**, Hub-LFS sha256 `0af5e962…` verified at fetch by `scripts/download-frank-archon-iq4r.sh`) — the OFFICIAL Qwen3.8-Flash-Next bf16 checkpoint rad-converted by the engine's author: routed experts as **4-bit codes into a 16-level non-uniform codebook** (128-pt Walsh–Hadamard rotation, GPTQ on a 10M-token calibration + 3 coordinate-descent sweeps, E4M3 scales per 64 weights), **10 whole bf16-protected experts/layer**, int8 W8A8 trunk/lm_head, fp8 HC mixtures, fp8 fixed-scale PLE n-gram (47.7 GiB), **MTP head + bf16 vision tower in the container**; card KL 0.0789 mean all-tokens / top-1 92.85 % vs BF16. **Base-alignment (censored) model** — the abliterated family lives on frank-dragoon. Shape carried VERBATIM from the 2026-10-13 record: **3 × 200,000 fp8-KV** (`--max-num-seqs 3`), **25,600 MiB slab + 5,120 MiB pool = 30,720 of the 30,969 MiB claimable**, 44 GiB/rank pinned host pool, PLE pinned to RAM, MTP depth 2, MNBT 2048, Froggeric v22.5 template. Decode/prefill ladders in the runbook are the **w4dn8 record** (the bar to beat; new-payload probes pending). Rollback: re-download the w4dn8 `.rad` from the HF mirror `frank8465/Swift-1.5-Qwen3.8-Flash-Next-GPTQ-W4A8-Radiance-AMD-R9700` (LFS sha256 `09dd9306…`) + revert the swap commit — the local copy was **purged 2026-10-17** (it was the last non-iq4r model on the host; the GLM-4.7 GGUF, the radiance kvcache tiers, and every stale compile cache went in the same purge, 763 GiB free). The fp8-native `.rad` + the raw Swift1.5 BF16 source were DELETED from disk 2026-10-15 (Frank's one-time rm, volume for this payload; rebuildable via the fetch/build scripts). | 7770 |
| Dankclaw | `frank-dragoon` | **halogen-flash-server 0.16.2** (`ghcr.io/peonist-ai/halogen-flash-server@sha256:0c61bf84…`, digest-pinned, plain `podman run` via `scripts/serve-frank-dragoon.sh`, root systemd unit) | Quat3rnion/halogen-qwen3.8-flash-next-v2-abliterated **`qwen38-flash-next-v2-abliterated.hgn`** (66,687,678,432 B ≈ 62.1 GiB resident, Hub LFS sha256 `b5bd925f…` verified at fetch — the **NATIVE 2026-10-11 v2 conversion** of orcarouter's pinned BF16, abliterated, NOT the writer transplant that carried this filename; the v2 `.hgn` format loads in NO other engine), **+ REQUIRED `qwen38-flash-next-ngram.hgn`** (51,200,246,144 B, peonist @5cc17cea) **+ vision tower** (897,916,416 B, @e053f488) — both **hardlinked** from the swift15 payload dir (the release publishes only the main file; the engine's own `niah` 18/18 + `ppl` 3.968 with mean KL 0.155 vs the swift15 logits certified the stock companions), **vision ON** (`image_url` parts only, ≥256 px probes), **MTP spec decode ALWAYS-ON (`HALOGEN_SPEC_ADAPT=0` — the adaptive default disables the drafter per request and HALVES decode)**, MTP head INSIDE the checkpoint (`HALOGEN_MTP_HEAD` unset), **TWO slots over ONE `HALOGEN_KV_POOL_POSITIONS=401408` pool (= 2 × ctx) at `HALOGEN_CTX=200704`** (pool RAISED from 200,704 and DEPLOYED 2026-10-18 so BOTH slots can hold a maximum-length request at once; the parity play was frank-zealot's then-401,408 total in-flight context — zealot moved to 786,432 the same evening and dragoon's pool was NOT raised again, because every position is page cache the n-gram table loses; **11.0 GiB reserved, measured on that deploy** (the preflight formula bills 11.3) where 200,704 cost 5.5, 262,144 cost 7.2 and the 2026-10-06 pool measured 14.6; the ceiling does NOT move with it — 262,144 is the native wall and the engine refuses more without `HALOGEN_ROPE_YARN`), harness window 155,648 = ceiling − 45k estimate headroom, **server-side sampler 1.0/0.95/20** (peonist engine recipe — Quat3rnion publishes none; models.yml extraBody matches), coupled cap/timeout 65536/7200, prompt cache ON by default (warm 100k turn ≈ 2 s vs ≈ 88 s cold), **n-gram table disk-backed** (mmap page cache, `Pin::None`); measured vs the swift15 boot: cold prefill +12.6 %, decode 50.0 vs 46.8 t/s, NIAH 18/18 vs 17/18, PPL +2.3 %, thinking tokens ×1.9|7770|
| Dankclaw | `frank-zealot` | **llama.cpp PrismML fork build** (vendor release `0.2.0-dev` build **10777**, commit `e3de71522`; root systemd unit, relinked — the ONLY loader for this payload, and since 2026-10-18 also the minimum build that pairs MTP with a quantized V cache) | `prism-ml/Ternary-Bonsai-2-9B-gguf` **PQ2_0 carrying the MTP head in the file** (2,675,410,720 B, Hub-LFS sha256 `c79fcdfa…` verified at fetch by `scripts/download-frank-zealot-bonsai9.sh`; `qwen35` hybrid: `block_count 33` = **32 trunk layers** (8 full-attention + 24 Gated DeltaNet at `full_attention_interval 4`) + the `blk.32` MTP block, 4 KV heads × 256 dim, native 262,144; `general.file_type 141` with **202 custom-type PQ2_0 tensors of 442** + `prism.hadamard.*` dequant metadata, which is what locks the runtime) + the publisher's own **Q8_0 mmproj** (624,229,664 B, sha256 `e0cb6f69…`; **Q8_0 is the publisher's requirement** for concurrent image requests — the BF16 tower can crash the server, and this endpoint runs three slots) for **vision**, on the RTX 3090, **THREE 262,144-token slots** (`--ctx-size 786432 --parallel 3` = every conversation at the trained window), **q8_0 K + q8_0 V** under flash attention (17 KiB/token over the 8 full layers = 13,056 MiB pool, ledger-exact; **f16 KV needs 26,112 MiB and cannot boot this card**; +151 MiB of recurrent state for the three DeltaNet cells — the SPEC-ARMED arms print 452 MiB, because MTP depth 2 bills three seqs per slot), batch 4096 / **ubatch 2048** (measured CUDA optimum), `--log-verbosity 4`, **MTP OFF — measured, not because the head is absent**: the draft context bills its own f16 KV over the WHOLE pool (3,072 MiB) and at ubatch 2048 that squeeze halves decode (167.2 → 109.2 t/s); the documented MTP arm is `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` → 213.5 t/s. Production boot 17,922 used / 6,203 free; **168.7 t/s** single-stream greedy decode (three runs, 168.4–168.7) and **~300 t/s** aggregate across three concurrent streams (102.8 each); 2,877 t/s prefill at 106,418 was the trial arm. Cutover 2026-10-18 from Ternary-Bonsai-2-**27B** PQ2_0 at 2 × 200,704 (payload + build 10754 tree on disk as rollback). | 7771 |
| Dankclaw | `frank-zealot` | **llama.cpp PrismML fork build** (vendor release `0.2.0-dev` build **10777**, commit `e3de71522`; root systemd unit, relinked — the ONLY loader for this payload; the b10776 floor that gates MTP beside a quantized V cache is moot under exact KV, but the custom tensor type alone keeps the build pinned) | `prism-ml/Ternary-Bonsai-2-9B-gguf` **PQ2_0 carrying the MTP head in the file** (2,675,410,720 B, Hub-LFS sha256 `c79fcdfa…` verified at fetch by `scripts/download-frank-zealot-bonsai9.sh`; `qwen35` hybrid: `block_count 33` = **32 trunk layers** (8 full-attention + 24 Gated DeltaNet at `full_attention_interval 4`) + the `blk.32` MTP block, 4 KV heads × 256 dim, native 262,144; `general.file_type 141` with **202 custom-type PQ2_0 tensors of 442** + `prism.hadamard.*` dequant metadata, which is what locks the runtime) + the publisher's own **Q8_0 mmproj** (624,229,664 B, sha256 `e0cb6f69…`; **Q8_0 is the publisher's requirement** for concurrent image requests — the BF16 tower can crash the server, and this endpoint runs several slots) for **vision**, on the RTX 3090, **TWO 262,144-token slots** (`--ctx-size 524288 --parallel 2` = every conversation STILL at the trained window — the per-slot contract never moved) over **unquantized f16 K + f16 V** (32 KiB/token over the 8 full layers = **16,384 MiB** pool; the POOL halved because exact KV at the old 786,432 pool is 24,576 MiB against the 24,123 MiB the driver leaves addressable — the repo's older "26,112 MiB" figure had doubled the q8_0 number including its 1/32 scale overhead; +100.5 MiB of recurrent state for the two DeltaNet cells, ~50.25 MiB a cell), batch 4096 / **ubatch 2048** (measured CUDA optimum), `--log-verbosity 4`, **MTP OFF — measured, and now doubly so**: the draft context bills its own f16 KV over the WHOLE pool (3,072 MiB at the old pool, 2,048 MiB at this one) and at ubatch 2048 that squeeze halves decode (167.2 → 109.2 t/s); the documented q8_0-era MTP arm is `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` → 213.5 t/s. Last MEASURED boot (the 3 × 262,144 q8_0 arm this replaces): 17,922 used / 6,203 free, **168.7 t/s** single-stream greedy decode (three runs, 168.4–168.7), **~300 t/s** aggregate across three concurrent streams (102.8 each), 2,877 t/s prefill at 106,418 — the f16 boot's ledger and rates land in the runbook once measured. Cutover 2026-10-18 from Ternary-Bonsai-2-**27B** PQ2_0 at 2 × 200,704 (payload + build 10754 tree on disk as rollback). | 7771 |
Retired endpoints (`frank-npu`, and every prior runtime) are in
the [runbook index](#runbook-index). The full swap history lives in
@@ -252,8 +252,12 @@ archive.
real users, and a bench beside a live service starves the GPU and corrupts
results. Use the runtime's own harness: `llama-bench` for the llama.cpp
endpoints; a live llama.cpp server (frank-zealot) also reports honest
per-request numbers in its `/completion` `timings` object (prefill/decode
rates, `draft_n`, `draft_n_accepted`), which is how frank-zealot was measured.
per-request numbers in a `timings` object (prefill/decode rates, `draft_n`,
`draft_n_accepted`) — but on the PrismML fork build 10777 that object is only
reaching you on **`/v1/chat/completions`**: a greedy `ignore_eos` run on the raw
`/completion` path there answers **500 "The model produced output that does not
match the expected Content-only format"**, so measure through the chat path
(measured 2026-10-18 on the exact-KV reslice).
halogen has NO `/completion` path at all — frank-dragoon's rates come from
wall-clock around `/v1/chat/completions` plus the boot ledger. Rolling your own
bench requires asking first.
@@ -350,9 +354,9 @@ and `frank-zealot` through
the publisher's own **Q8_0** mmproj passed as `--mmproj` — Q8_0 is the
publisher's requirement for concurrent image requests (the card states the
BF16 tower can crash `llama-server` when several image requests run at once,
and this endpoint runs three slots). `frank-zealot`'s `contextWindow` is
**262144** — the PER-SLOT window (`--ctx-size 786432 --parallel 3` since the
2026-10-18 Bonsai-2-**9B** swap), which since that swap EQUALS the model's
and this endpoint runs several slots). `frank-zealot`'s `contextWindow` is
**262144** — the PER-SLOT window (`--ctx-size 524288 --parallel 2` since the
2026-10-18 reslice to exact f16 KV), which EQUALS the model's
native window; llama.cpp answers a longer prompt with a 400 naming the limit,
never a truncation, so the harness must compact before it.
Note also
@@ -510,7 +514,8 @@ llama.cpp refuses the custom type at load, and a loader that
ignored the Hadamard metadata would dequant WRONG — same lock class as the
ROCmFPX era. On top of that lock, **the fork gates MTP beside a quantized V
cache on build `prism-b10776-d00e252` or newer**, which is why the prebuilt
moved 10754 → 10777; the 10754 tree stays on disk as the 27B rollback runtime.
moved 10754 → 10777 (exact f16 KV makes that gate moot — the custom type alone
pins the runtime); the 10754 tree stays on disk as the 27B rollback runtime.
Retargeting the payload without the fork ships an endpoint that cannot load,
so the seam pins both paths.
@@ -520,36 +525,47 @@ attention + FFN in q8_0, which is why the draft context bills real KV), and
`qwen35.full_attention_interval = 4` ⇒ **8 full-attention + 24 Gated
DeltaNet**, 4 KV heads × 256 dim, native 262,144. **KV accounting:** the pool
scales with the 8 full layers — 8 × 4 × 256 = 8,192 elements/token per cache,
so **q8_0 K + q8_0 V bills 17,408 B/token ≈ 17 KiB** = **13,056.00 MiB** at the
786,432 pool (ledger-exact) — exactly HALF the 27B's 34,816 B/token, which is
the whole reason three native windows fit where the 27B afforded only two
shortened ones. **f16 K would bill 26,112 MiB and cannot boot on a 24,123 MiB
card.** The 24 DeltaNet layers bill a fixed recurrent state per SLOT — the
production ledger prints `RS buffer size = 150.75 MiB (3 cells, 32 layers,
3 seqs)`, while the SPEC-ARMED arms print 452.25 MiB (MTP depth 2 bills three
seqs per slot, so 9 cells) — so pool arithmetic alone under-prices the shape.
so **the deployed exact pair f16 K + f16 V bills 32,768 B/token = 32 KiB** =
**16,384 MiB** at the 524,288 pool. The q8_0 pair this endpoint shipped first
billed **17,408 B/token** (1.0625 B/element: 32 codes plus one 2-byte scale per
32-block) = **13,056.00 MiB** at the old 786,432 pool, ledger-exact — exactly
HALF the 27B's 34,816 B/token. **Exact KV at that 786,432 pool is 24,576 MiB,
more than the 24,123 MiB the driver leaves addressable, so the POOL is what
pays for f16 — the window never moved.** (The "26,112 MiB" figure this repo
carried doubled the q8_0 number *including* its scale overhead; the true ratio
is 1.882×, not 2×.) The 24 DeltaNet layers bill a fixed recurrent state per
SLOT — ~50.25 MiB a cell, so the 3-slot ledger printed `RS buffer size =
150.75 MiB (3 cells, 32 layers, 3 seqs)` and two slots bill 100.50, while the
SPEC-ARMED arms print 452.25 MiB (MTP depth 2 bills three seqs per slot, so 9
cells) — so pool arithmetic alone under-prices the shape.
**The shape (2026-10-18):** `--ctx-size 786432 --parallel 3` = THREE
conversations of 262,144, every one at the model's TRAINED window (Frank: "if
you can possibly fit a third context, do it"), `--flash-attn on`, **q8_0 K +
q8_0 V**, batch 4096 / **ubatch 2048** (the measured CUDA optimum carried from
the MiMo/Swift A/Bs on this card; it also sets the compute-buffer size, which
is what decides whether MTP fits), `--log-verbosity 4`, `--metrics`,
`--jinja`, `--mmproj` for vision, **speculative decoding OFF**
(`SPEC_TYPE=none`). Boot: **17,876 MiB used / 6,249 free**; compute 1,409 MiB
CUDA + 1,201 MiB CUDA_Host, projector 595 + 248 MiB. Measured on the same
boot: **167.2 t/s** greedy decode, 158.9 t/s single-stream chat, **280.2 t/s
aggregate across three concurrent streams**, **2,877 t/s prefill at 106,418**
tokens — the 27B record (76.6 decode / 109.4 aggregate / 691.7 prefill at 166k)
beaten on every axis. The **first request after boot is a warmup artifact**
(37.7 t/s class) — never report it.
**The shape (2026-10-18, resliced the same evening):** `--ctx-size 524288
--parallel 2` = TWO conversations of 262,144, every one still at the model's
TRAINED window (the first arm was THREE — Frank: "if you can possibly fit a
third context, do it" — and the exact-KV reslice handed one back),
`--flash-attn on`, **unquantized f16 K + f16 V**, batch 4096 / **ubatch 2048**
(the measured CUDA optimum carried from the MiMo/Swift A/Bs on this card; it
also sets the compute-buffer size, which is what decides whether MTP fits),
`--log-verbosity 4`, `--metrics`, `--jinja`, `--mmproj` for vision,
**speculative decoding OFF** (`SPEC_TYPE=none`). The MEASURED record still
belongs to the arm this replaced (3 × 262,144, q8_0 K + q8_0 V): boot **17,876
MiB used / 6,249 free**; compute 1,409 MiB CUDA + 1,201 MiB CUDA_Host, projector
595 + 248 MiB; **167.2 t/s** greedy decode, 158.9 t/s single-stream chat,
**280.2 t/s aggregate across three concurrent streams**, **2,877 t/s prefill at
106,418** tokens — the 27B record (76.6 decode / 109.4 aggregate / 691.7 prefill
at 166k) beaten on every axis. The exact-KV arm's ledger and rates go in the
runbook once booted; its projection is ~21,200 MiB used / ~2,900 free, since
only the KV pool (+3,328) and one recurrent-state cell (−50) move. The **first
request after boot is a warmup artifact** (37.7 t/s class) — never report it.
**Why MTP is off even though the head is in the file.** The drafter is GOOD —
acceptance 0.798 / 0.617 per position, mean accepted length 2.41 — and the card
recommends depth 2. It still loses AT THIS SHAPE because the draft context
bills its OWN **f16** KV over the WHOLE pool (4,096 B/position × 786,432 =
**3,072.00 MiB**, 4,830 MiB with its compute buffers) and ubatch 2048 refuses to
give it up. Booted arms, same card, same traffic (2026-10-18):
recommends depth 2. It still loses because the draft context bills its OWN
**f16** KV over the WHOLE
pool (4,096 B/position — **3,072.00 MiB** at the 786,432 pool, 4,830 MiB with
its compute buffers; **2,048.00 MiB** at the shipped 524,288 pool) and ubatch
2048 refuses to give it up. Booted arms, same card, same traffic (2026-10-18,
every row q8_0 K + q8_0 V at the 786,432 pool):
| slots | spec | ubatch | used / free | decode |
|---|---|---|---|---|
@@ -563,13 +579,17 @@ give it up. Booted arms, same card, same traffic (2026-10-18):
drafter, is what halves it at three windows. The arm that WOULD run it at three
windows is `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` (213.5 t/s single-stream,
250.8 three-stream aggregate) and pays prefill −17 % and 2.7 GiB of headroom on
a card whose projector worst-case lands AFTER the engine's own fit check. This
endpoint is the fleet's compaction hub — prefill-bound, 2–3 concurrent
subagents — so the shipped arm takes the +17 % prefill (2,877 vs 2,395 t/s) and
the +12 % three-stream aggregate (280.2 vs 250.8) over single-stream speed, and
keeps 6.2 GiB free. `SPEC_TYPE=none` strips the WHOLE spec set (`--spec-type`
and every `--spec-draft-*`) — flags move as a set or the arm measures a crash
instead of an absence.
a card whose projector worst-case lands AFTER the engine's own fit check. Under
**exact KV the drafter is arithmetically out**: the trunk projects ~21,200 MiB
of the 24,123 MiB addressable before the draft context's 2,048 MiB and its
buffers are even asked for, so the doors back to speculation are ONE window per
conversation, or KV back to q8_0.
The compaction-hub load is what makes exact KV affordable: prefill is
weight-bandwidth bound and does not re-read the cache per generated token, so
the dtype is paid in decode and concurrency (two conversations now, not three),
not in the work this endpoint exists to do. `SPEC_TYPE=none` strips the WHOLE
spec set (`--spec-type` and every `--spec-draft-*`) — flags move as a set or the
arm measures a crash instead of an absence.
**Sampler**: the recipe lives in the checkpoint (`general.sampling.*` =
temperature 1.0 / top_p 0.95 / top_k 20 / min_p 0 / repeat_penalty 1.0,
@@ -581,7 +601,7 @@ would silently re-add the flag on an explicit empty value).
**Vision**: the publisher's own **Q8_0** mmproj via `--mmproj` — Q8_0 is
MANDATORY, not a preference: the card says the BF16 tower "can crash
llama-server when several image requests run at once", and `--parallel 3` is
llama-server when several image requests run at once", and `--parallel 2` is
exactly that shape. Probe traps carry over: ≥256 px probe images or the model
answers "video", only the OpenAI `image_url` content part reaches the model,
and thinking tokens count against `max_tokens` — budget ≥512 or the probe
@@ -596,10 +616,12 @@ have to be fetched first.
**Compaction hub**: `models.yml` routes `compactionModel` for
`frank-archon`, `frank-dragoon` and `frank-zealot` (the 3090 endpoint) here —
262,144 (since the 2026-10-18 reslice) is the widest window in the fleet and
the only one that comfortably fits a worst-case compaction input (~170k
tokens). With `--parallel 3` a compaction request no longer serializes behind a
live generation (the Gemma4 era's `--parallel 1` law). Retrieval through a
262,144 (still the PER-SLOT window after the exact-KV reslice) is the widest
window in the fleet and the only one that comfortably fits a worst-case
compaction input (~170k tokens). With `--parallel 2` a compaction request still
runs beside ONE live generation, and with two already busy it waits — the Gemma4
era's `--parallel 1` law at half strength, which is the concurrency price of
exact KV. Retrieval through a
25,960-token prompt was verified exact after the swap.
**Contract law learned at verbosity 4:** a raw `/completion` request prints
@@ -1040,18 +1062,34 @@ linked runbooks.
- **A draft context bills its own f16 KV over the WHOLE POOL, and on a
single-card endpoint that squeeze can cost more than the drafter wins.** The
draft context is built at the target's `n_ctx`, so with `--parallel N` the
drafter pays the POOL, not the slot: on frank-zealot's 786,432-position
qwen35 pool, `--spec-type draft-mtp` added **3,072.00 MiB of f16 draft KV**
(4,096 B/position — the draft cache is never quantized) plus its own compute
buffers, 4,830 MiB all told. Booted arms, same card, same traffic
(2026-10-18): 3 slots + ubatch 2048 → **167.2 t/s decode with spec OFF,
109.2 with spec ON**; dropping `UBATCH_SIZE` to 1024 hands the buffers back
and spec ON gives **213.5** (+28 % over the spec-OFF arm), and at 1–2 slots
the SAME card runs MTP at ubatch 2048 and still gains ~28 %. So the MTP
question is never "does the file carry the head" — it is "what do draft KV
+ buffers cost against whatever the batch knobs keep for compute buffers",
and that answer is per-shape. Measure the arm, never the flag. See
drafter pays the POOL, not the slot: on frank-zealot's qwen35 pool
`--spec-type draft-mtp` adds **4,096 B/position of draft KV at the default
f16 dtype** (`--spec-draft-type-k/-v` can quantize the draft — untested here),
**3,072.00 MiB** at the 786,432 pool it shipped with and **2,048.00 MiB** at
the 524,288 pool it runs now, plus its own compute buffers (4,830 MiB all told
at the old pool). Booted arms, same card, same traffic (2026-10-18, every row
q8_0 KV): 3 slots + ubatch 2048 → **167.2 t/s decode with spec OFF, 109.2 with
spec ON**; dropping `UBATCH_SIZE` to 1024 hands the buffers back and spec ON
gives **213.5** (+28 % over the spec-OFF arm), and at 1–2 slots the SAME card
runs MTP at ubatch 2048 and still gains ~28 %. So the MTP question is never
"does the file carry the head" — it is "what do draft KV + buffers cost
against whatever the batch knobs keep for compute buffers", and that answer is
per-shape. Measure the arm, never the flag. Production corollary: beside
exact KV an **f16-draft** arm has no room — the trunk projects ~21,200 MiB of
the 24,123 addressable before the drafter is asked for anything; the untested
door is `--spec-draft-type-k/-v q8_0` (1,088 MiB). See
[[frank-zealot-llama-prism-bonsai9-mtp]].
- **A quant's scale overhead belongs in its byte rate, and doubling a measured
pool figure to "get the f16 number" invents 6.25 %.** This repo carried "f16
KV would bill 26,112 MiB" for weeks by doubling the q8_0 ledger figure
(13,056.00 MiB). q8_0 is 1.0625 B/element (32 codes plus one 2-byte scale per
32-block), f16 is exactly 2.0 ⇒ the true ratio is **1.882×**, so exact KV over
that pool is **24,576 MiB**. The conclusion (three f16 windows cannot boot a
card that leaves 24,123 MiB addressable) survived; the number did not — and
the honest rate is what made the resize calculable, because it shows 524,288
positions is the largest exact-KV pool that fits with room for the projector.
Derive byte rates from element count × block layout, then check them against
the ledger.
- **`--ubatch-size` has an OPTIMUM on the 2x R9700 layer split, not a plateau —
and the curve is BACKEND-SPECIFIC, so never carry it across (next bullet). Its
compute buffers land about TWICE as heavy on the card that holds the output
+1 -1
View File
@@ -1459,7 +1459,7 @@ LLM_ZEALOT_RUNTIME="${LLM_ZEALOT_RUNTIME:-/home/dank/GitPackages/llama-prism-b10
# image path — a deploy that starts without it silently ships a text-only
# endpoint. Checked unconditionally, like frank-dragoon's. Q8_0 is MANDATORY,
# not a preference: the card says the BF16 tower can crash llama-server when
# several image requests run at once, and this endpoint runs --parallel 3. Its
# several image requests run at once, and this endpoint runs --parallel 2. Its
# 868.32 MiB worst-case VRAM (595.29 model + 248.10 compute in the boot ledger)
# lands AFTER the engine's fit check, so it is not inside the q8_0 budget.
LLM_ZEALOT_MMPROJ="${LLM_ZEALOT_MMPROJ:-/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf}"
+76 -57
View File
@@ -2,12 +2,14 @@
# prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 **with the MTP head in the file**
# on the RTX 3090, :7771.
#
# 2026-10-18 (Frank): swap from the Bonsai-2-**27B** PQ2_0 to the 9B MTP export
# at THREE 262,144-token conversations (Frank: "if you can possibly fit a third
# context, do it"). The 9B bills HALF the KV of the 27B per token, which is what
# funds 3 x the native window on one 24,123 MiB card. Speculative decoding ships
# OFF — measured, not assumed; see the MTP law below, which is the whole story
# and the reason the publisher's "depth 2 works best" line does not transfer.
# 2026-10-18 (Frank): swap from the Bonsai-2-**27B** PQ2_0 to the 9B MTP export,
# and the same evening the shape moved to TWO 262,144-token conversations over an
# UNQUANTIZED (f16) KV cache — "shift frank-zealot to 2 parallels of unquantized
# kv". Exact K and V is the retrieval-fidelity ask; the price is one concurrent
# conversation, because f16 owns the card. Speculative decoding ships OFF —
# measured, not assumed; see the MTP law below, which is the whole story and the
# reason the publisher's "depth 2 works best" line does not transfer (and why
# exact KV makes the drafter illegal outright).
#
# THE RUNTIME IS A LOCK, NOT A PREFERENCE. The file is general.file_type 141
# carrying custom GGML type 142 tensors plus prism.hadamard.* metadata
@@ -27,7 +29,7 @@ MODEL_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.g
# 624,229,664 B, Hub LFS sha256 e0cb6f69… — Q8_0 IS MANDATORY, not a preference:
# the publisher's card says the BF16 mmproj "can crash llama-server when several
# image requests run at once; Q8_0 is not affected", and this endpoint runs
# --parallel 3, i.e. exactly that shape. The projector's worst case lands AFTER
# --parallel 2, i.e. exactly that shape. The projector's worst case lands AFTER
# the engine's own fit check, so it is never inside a projection number.
MMPROJ_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf
# Base alignment: Bonsai-2 is the censored BASE lineage. The 27B CRACK re-export
@@ -42,16 +44,18 @@ MMPROJ_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0.ggu
ALIAS=frank-zealot
BIND_ADDR=0.0.0.0
LISTEN_PORT=7771
# CTX_SIZE is the POOL llama.cpp divides across slots: 786,432 / 3 = 262,144 per
# conversation — each slot at the model's TRAINED window, which is the point of
# the swap (the 27B era had to cut each conversation to 200,704 to fit two).
# MUST stay divisible by PARALLEL (the seam enforces it) and the harness
# contextWindow quotes the PER-SLOT number — a longer prompt is answered with a
# 400, never truncated. The ceiling cannot go past 3 x 262,144: that is the
# native wall (no YaRN in this path) and the arithmetic agrees — a 4th slot
# would push each below the compaction contract.
CTX_SIZE=786432
PARALLEL=3
# CTX_SIZE is the POOL llama.cpp divides across slots: 524,288 / 2 = 262,144 per
# conversation — each slot STILL at the model's TRAINED window; the per-slot
# contract did not move. MUST stay divisible by PARALLEL (the seam enforces it)
# and the harness contextWindow quotes the PER-SLOT number — a longer prompt is
# answered with a 400, never truncated. THE POOL CANNOT STAY AT 786,432 UNDER
# EXACT KV: f16 bills 32,768 B/token, so that pool is 24,576 MiB — more than the
# 24,123 MiB the driver leaves addressable, before a single weight loads.
# Halving the POOL to 524,288 (16,384 MiB) is what funds exact KV. The window
# cannot go past 262,144 a slot either: that is the native wall (no YaRN in this
# path), so a 3rd slot would be a KV-dtype cut, not more context.
CTX_SIZE=524288
PARALLEL=2
BATCH_SIZE=4096
# Ubatch is a prefill knob (decode never moves) with NO interior optimum on this
# CUDA card up to 2048 — 2048 is the measured optimum (+4.6 % prefill). It has a
@@ -60,38 +64,47 @@ BATCH_SIZE=4096
UBATCH_SIZE=2048
# KV law (header + boot ledger, both confirmed): qwen35 hybrid, 32 layers with
# qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated DeltaNet;
# 4 KV heads × 256 dim = 8,192 elements per token per cache. q8_0 bills 8,704 B
# per cache ⇒ 17,408 B/token = 17 KiB, so the 786,432 pool is 13,056.00 MiB —
# the ledger said exactly that. HALF the 27B's 34,816 B/token, which is the
# whole reason three native windows fit. f16 would bill 26,112 MiB at this pool
# (against a 24,123 MiB card) and dies at cudaMalloc; the 24 DeltaNet layers
# bill a fixed recurrent state per SLOT instead of per token — the PRODUCTION
# ledger prints "RS buffer size = 150.75 MiB (3 cells, 32 layers, 3 seqs)"
# (~50 MiB a cell), while the SPEC-ARMED arms print 452.25 MiB, because MTP
# depth 2 bills three seqs per slot (9 cells) — which is why pool arithmetic
# alone under-prices the shape.
CACHE_TYPE_K=q8_0
CACHE_TYPE_V=q8_0
# 4 KV heads × 256 dim (n_embd_k_gqa = n_embd_v_gqa = 1,024 per layer) = 8,192
# elements per token per cache. EXACT KV: f16 = 2 B/element ⇒ 16,384 B per cache
# ⇒ 32,768 B/token = 32 KiB, so the 524,288 pool is 16,384.00 MiB. q8_0 bills
# 1.0625 B/element (32 codes + one 2-byte scale per 32-block) ⇒ 17,408 B/token,
# which is the 13,056.00 MiB the 3-slot ledger printed at the 786,432 pool.
# THE RATIO IS 1.882×, NOT 2×: the old note here said "f16 would bill 26,112
# MiB" because it doubled the q8_0 figure with its scale overhead included;
# exact arithmetic says 24,576 MiB at that pool. The wall stands, only its
# height moves. The 24 DeltaNet layers bill a fixed recurrent state per SLOT
# instead of per token — the 3-slot ledger printed "RS buffer size = 150.75 MiB
# (3 cells, 32 layers, 3 seqs)", ~50.25 MiB a cell so two slots bill 100.50 —
# while the SPEC-ARMED arms print 452.25 MiB, because MTP depth 2 bills three
# seqs per slot (9 cells). Pool arithmetic alone under-prices the shape.
CACHE_TYPE_K=f16
CACHE_TYPE_V=f16
# SPECULATIVE DECODING: OFF, MEASURED. The head is in the file and the drafter
# is GOOD (acceptance 0.798/0.617 per position, mean accepted length 2.41) — it
# still loses, because the draft context bills its OWN f16 KV over the WHOLE
# pool: 4,096 B/position × 786,432 = 3,072.00 MiB, plus its compute buffers,
# 4,830 MiB all told. Booted arms, same card, same traffic (2026-10-18):
# pool at its default dtype. That dtype IS a knob (--spec-draft-type-k /
# --spec-draft-type-v, both default f16 — verified against this build's --help),
# but SPEC_TYPE=none never sends them. Under the
# 3-slot q8_0 shape that bill was 4,096 B/position × 786,432 = 3,072.00 MiB plus
# ~1,758 MiB of buffers. Booted arms, same card, same traffic (2026-10-18, the
# WHOLE table is q8_0 K + q8_0 V — it predates exact KV):
# 3 × 262,144 spec OFF ub 2048 → 17,876 used / 6,249 free, decode 167.2 t/s
# 3 × 262,144 spec ON ub 2048 → 22,706 used / 1,419 free, decode 109.2 t/s
# 3 × 262,144 spec ON ub 1024 → 21,400 used / 2,725 free, decode 213.5 t/s
# 2 × 262,144 spec ON ub 2048 → 17,178 used / 6,947 free, decode 213.2 t/s
# 1 × 262,144 spec ON ub 2048 → 11,652 used / 12,473 free, decode 214.2 t/s
# i.e. MTP is worth +28 % decode whenever the card has room, and at the 3-slot
# pool it costs 1.3 GiB of compute buffers that ubatch 2048 refuses to give up —
# the squeeze, not the drafter, is what halves decode. The arm that WOULD run
# MTP at three windows is SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024 (decode 213.5,
# 3-stream 250.8 aggregate) and pays prefill −17 % and 2.7 GiB of headroom on a
# card whose projector worst-case lands after the fit check. This endpoint's
# load is the fleet's COMPACTION HUB (prefill-bound, ~170k in) plus 2-3
# concurrent subagents, so the deployed arm takes the +17 % prefill
# (2,877 vs 2,395 t/s at 106,418 tokens) and the +12 % three-stream aggregate
# (280.2 vs 250.8) over single-stream speed, and keeps 6.2 GiB free.
# MTP is worth +28 % decode whenever the card has room. At the SHIPPED exact-KV
# shape an f16-draft arm has none: the draft KV alone is 4,096 × 524,288 =
# 2,048.00 MiB plus its buffers, on a trunk that already projects ~21,200 MiB of
# the 24,123 the driver leaves addressable. DOORS BACK TO THE DRAFTER: one
# window per conversation; KV back to q8_0 with UBATCH_SIZE=1024 (213.5 t/s);
# or the UNMEASURED arm --spec-draft-type-k q8_0 --spec-draft-type-v q8_0, which
# halves the draft bill to 1,088 MiB and is legal on this build (>= b10776).
# Nobody has booted that beside exact KV — measure before promising it.
# This endpoint's load is still the fleet's COMPACTION HUB (prefill-bound,
# ~170k in): prefill is weight-bandwidth bound and does not read the KV cache
# per generated token, which is why exact KV is affordable here at all; what it
# buys back is concurrency — TWO conversations, not three.
# SPEC_TYPE=none strips the WHOLE spec set (--spec-type, every --spec-draft-*):
# flags move as a set or the arm measures a crash instead of an absence.
SPEC_TYPE=none
@@ -105,27 +118,33 @@ SPEC_DRAFT_P_MIN=0
# PRESENCE_PENALTY="" is the documented opt-out.
PRESENCE_PENALTY=1.0
NGL=99
# Boot ledger (2026-10-18 PRODUCTION boot under the unit, verbosity 4):
# 17,922 MiB used / 6,203 MiB free of 24,123 (nvidia-smi settled)
# Boot ledger REFERENCE — the previous production boot (2026-10-18 13:19,
# 3 × 262,144, q8_0 K + q8_0 V, verbosity 4), i.e. the shape this file replaced:
# 17,956 MiB used / 6,169 MiB free of 24,123 addressable (nvidia-smi settled;
# its total column reads 24,576 — the driver reserves the difference, and
# older notes quoting "a 24,123 MiB card" mean this addressable figure)
# CUDA0 model 2,036.71 MiB + CPU_Mapped 257.66 MiB
# CUDA0 KV 13,056.00 MiB (q8_0 K + q8_0 V, 786,432 positions)
# CUDA0 RS 150.75 MiB (3 cells, 32 layers, 3 seqs)
# compute 1,409.02 MiB CUDA + 1,088.87 MiB CUDA_Host (+2.84 MiB output buf)
# mmproj worst-case estimate 868.32 MiB (248.10 CUDA + 24.93 CPU compute
# meta) — charged AFTER the engine's own fit check, as always
# mmproj 595 MiB weights + 248.10 MiB CUDA compute meta; estimated worst case
# 868.32 MiB — charged AFTER the engine's own fit check, as always
# no clamp line, no cudaMalloc error
# Measured on this boot (/completion timings, greedy, ignore_eos, 512 tokens):
# 168.7 t/s decode (three runs 168.4-168.7) and ~300 t/s aggregate across
# THREE concurrent streams (102.8 each). The trial arm's record (167.2 decode,
# 280.2 aggregate, 2,877 t/s prefill at 106,418) stands; the 27B it replaces
# gave 76.6 / 109.4 / 691.7 at 166k. The FIRST request after a boot is a
# warmup artifact (37.7 t/s class) and a natural generation that stops early
# makes a wall-clock aggregate read 4x low - measure with ignore_eos and read
# the engine's own timings.
# Fallback rung if the margin is ever eaten (desktop, co-tenant): q4_0 KV on the
# 27B era measured 16,392 used / 7,733 free at 2 × 200,704; the same drop here
# frees ~6.5 GiB at three windows. UBATCH_SIZE=1024 is the cheaper lever
# (−1,306 MiB measured on this shape).
# Measured on THAT boot (/completion timings, greedy, ignore_eos, 512 tokens):
# 168.7 t/s decode (three runs 168.4-168.7), ~300 t/s aggregate across THREE
# concurrent streams (102.8 each), 2,877 t/s prefill at 106,418. The 27B it
# replaced gave 76.6 / 109.4 / 691.7 at 166k. These are the numbers the exact-KV
# arm is judged against: decode reads 1.882× the KV bytes per token, so a decode
# drop is EXPECTED and must be measured, not assumed, while prefill should hold.
# Every non-KV term of that ledger survives this reslice except RS (−50.25 MiB,
# one fewer cell) and one slot's worth of scheduler state, so the f16 arm
# PROJECTS 17,956 − 13,056 + 16,384 − 50 = 21,234 MiB used / 2,889 free, and
# ~2.0 GiB free through the projector's worst case. REPLACE THIS PROJECTION WITH
# THE MEASURED PAIR after the boot: read the new ledger AND settled nvidia-smi.
# Fallback rungs if that margin gets eaten (desktop, co-tenant), cheapest first:
# CACHE_TYPE_V=q8_0 (exact K, quantized V) frees 8,192 MiB at this pool;
# CTX_SIZE=401408 (two 200,704 conversations) puts exact KV at 12,544.00 MiB and
# buys ~6.7 GiB back; UBATCH_SIZE=1024 hands 1,306 MiB of compute buffers back.
# Log verbosity threshold (llama.cpp -lv). 4 = debug: KV layout, buffer sizes
# and graph capture land in the journal, which is what the VRAM-fit forensics
# read. Levels above 4 flood the journal.
+1 -1
View File
@@ -1,5 +1,5 @@
[Unit]
Description=llama.cpp (PrismML build 10777) Ternary-Bonsai-2-9B PQ2_0-MTP GGUF (qwen35 hybrid, 3x262144 slots over one 786432 pool, q8_0 K+V, MTP off, mmproj Q8_0 vision) on the RTX 3090 (dankclaw, frank-zealot)
Description=llama.cpp (PrismML build 10777) Ternary-Bonsai-2-9B PQ2_0-MTP GGUF (qwen35 hybrid, 2x262144 slots over one 524288 pool, unquantized f16 K+V, MTP off, mmproj Q8_0 vision) on the RTX 3090 (dankclaw, frank-zealot)
After=network-online.target
Wants=network-online.target
+57 -49
View File
@@ -3,33 +3,35 @@
# in the file) as frank-zealot on Dankclaw's RTX 3090 via llama.cpp (PrismML
# vendor build 10777, CUDA sm_86).
#
# 2026-10-18 (Frank): Bonsai-2 27B PQ2_0 → the 9B MTP export at THREE
# 262,144-token conversations (--ctx-size 786432 --parallel 3), q8_0 K + q8_0 V.
# The 9B bills 17,408 B/token against the 27B's 34,816 (8 full-attention layers
# instead of 16), and that halved rate is what funds three NATIVE windows where
# the 27B could only afford two cut to 200,704. Verbosity 4 ships so the
# VRAM-fit forensics (KV layout, buffer sizes, graph capture) land in the
# journal.
# 2026-10-18 (Frank): Bonsai-2 27B PQ2_0 → the 9B MTP export, then the same
# evening TWO 262,144-token conversations (--ctx-size 524288 --parallel 2) over
# an UNQUANTIZED f16 KV cache. The 9B bills 8,192 elements per token per cache
# (8 full-attention layers instead of the 27B's 16), so exact K and V costs
# 32,768 B/token — 16,384.00 MiB at the 524,288 pool, which is what a 24,123 MiB
# addressable card can carry beside the trunk. Verbosity 4 ships so the VRAM-fit
# forensics (KV layout, buffer sizes, graph capture) land in the journal.
#
# Architecture (engine ledger, not the marketing page): qwen35 hybrid, 32
# layers with qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated
# DeltaNet, 4 KV heads × 256 dim, native 262,144. The KV pool scales with the 8
# full layers (13,056.00 MiB at the 786,432 pool, ledger-exact); the DeltaNet
# layers cost a fixed recurrent state per SLOT — 452.25 MiB for 3 cells, ~151
# MiB each. The runtime is a hard lock, not a preference: custom GGML type 142
# plus prism.hadamard.* metadata, which only the vendor build registers, and
# MTP beside quantized V needs build ≥ b10776 — so the prebuilt moved 10754 →
# 10777 with the payload. Vision comes from the publisher's Q8_0 projector (the
# BF16 one can crash the server on concurrent images, and this endpoint runs
# three slots), so --mmproj is what gives this endpoint eyes; MMPROJ_FILE="" is
# the documented text-only opt-out.
# full layers (16,384.00 MiB of f16 at the 524,288 pool); the DeltaNet layers
# cost a fixed recurrent state per SLOT — ~50.25 MiB a cell, so 100.50 MiB for
# two. The runtime is a hard lock, not a preference: custom GGML type 142 plus
# prism.hadamard.* metadata, which only the vendor build registers (the b10776
# gate that moved the prebuilt 10754 → 10777 was MTP beside quantized V, which
# exact KV now makes moot — the type-142 lock alone keeps the build pinned).
# Vision comes from the publisher's Q8_0 projector (the BF16 one can crash the
# server on concurrent images, and this endpoint runs two slots), so --mmproj is
# what gives this endpoint eyes; MMPROJ_FILE="" is the documented text-only
# opt-out.
#
# Speculative decoding ships OFF, measured: the head loads and accepts well
# (0.798/0.617 per position, mean length 2.41) but the draft context bills its
# own f16 KV over the WHOLE pool — 4,096 B/position × 786,432 = 3,072 MiB plus
# buffers — and at ubatch 2048 that squeeze HALVES decode (167.2 → 109.2 t/s).
# frank-zealot.env carries the six-arm table and the arm that turns it back on
# (--ubatch-size 1024, decode 213.5 t/s) with its price.
# own f16 KV over the WHOLE pool — 4,096 B/position × 524,288 = 2,048 MiB plus
# buffers — and the trunk already projects 21,234 MiB with exact KV. Exact KV
# and MTP cannot share this card; frank-zealot.env carries the q8_0-era arm table
# and the two doors back to the drafter (one window, or KV back to q8_0 at
# --ubatch-size 1024 for 213.5 t/s).
#
# Sampling: the checkpoint carries the publisher's thinking recipe as GGUF
# metadata (general.sampling.* = temp 1.0 / top_p 0.95 / top_k 20 / min_p 0 /
@@ -56,40 +58,46 @@ fi
: "${ALIAS:=frank-zealot}"
: "${BIND_ADDR:=0.0.0.0}"
: "${LISTEN_PORT:=7771}"
# ctx-size is the POOL llama.cpp divides across slots: 786,432 / 3 = 262,144
# per conversation — every slot at the model's trained window. The harness
# ctx-size is the POOL llama.cpp divides across slots: 524,288 / 2 = 262,144
# per conversation — every slot still at the model's trained window. The harness
# contextWindow MUST quote the PER-SLOT number — a longer prompt is answered
# with a 400, never truncated.
: "${CTX_SIZE:=786432}"
: "${PARALLEL:=3}"
# with a 400, never truncated. Under exact KV the pool cannot stay at 786,432:
# 32,768 B/token × 786,432 = 24,576 MiB, more than the card's whole addressable
# 24,123 MiB before a single weight loads.
: "${CTX_SIZE:=524288}"
: "${PARALLEL:=2}"
# Compute-buffer geometry. Ubatch is a prefill knob (decode never moves).
# 2048 is the measured CUDA optimum (MiMo era, same arch/shape: +4.6 % prefill
# at 8.9k depth, decode flat, 616 MiB). The logical batch is VRAM- and
# throughput-neutral at a fixed ubatch. It ALSO sets the compute-buffer size,
# which is the term that decides whether an MTP draft context fits beside a
# 786,432-position pool: 1024 hands back 1,306 MiB and is the arm that runs the
# drafter at three slots (frank-zealot.env carries the table).
# which is the term that decides whether an MTP draft context fits beside a KV
# pool: 1024 hands back 1,306 MiB and was the arm that ran the drafter at three
# q8_0 windows (frank-zealot.env carries the table).
: "${BATCH_SIZE:=4096}"
: "${UBATCH_SIZE:=2048}"
# Quantized KV: q8_0 K + q8_0 V — the only pair that funds 3 × 262,144 on this
# card. 17 KiB/token at this geometry (8 full-attention layers × 4 KV heads ×
# 256 dim); 13,056.00 MiB at the 786,432 pool, ledger-exact. f16 would bill
# 26,112 MiB against a 24,123 MiB card and dies at cudaMalloc; q4_0 is the
# fallback rung. Explicit either way so the contract is visible in the argv,
# and --flash-attn stays required for quantized V (no true fp8 KV dtype exists
# in this build).
: "${CACHE_TYPE_K:=q8_0}"
: "${CACHE_TYPE_V:=q8_0}"
# Exact KV: f16 K + f16 V — no quantization error anywhere in the attention
# state. 32 KiB/token at this geometry (8 full-attention layers × 4 KV heads ×
# 256 dim × 2 B × 2 caches); 16,384.00 MiB at the 524,288 pool. That is the whole
# reason the slot count is 2: the same dtype at the old 786,432 pool is 24,576
# MiB and dies at cudaMalloc. q8_0 K + q8_0 V (17,408 B/token, 13,056.00 MiB at
# 786,432) is the documented fallback rung and funds three windows; CACHE_TYPE_V
# alone is the halfway door. Explicit either way so the contract is visible in
# the argv, and --flash-attn stays pinned because quantized V — the fallback —
# is illegal without it (no true fp8 KV dtype exists in this build).
: "${CACHE_TYPE_K:=f16}"
: "${CACHE_TYPE_V:=f16}"
# Speculative decoding defaults OFF, measured on this card and shape: the head
# IS in the checkpoint (qwen35.nextn_predict_layers = 1 — MTP is a property of
# the FILE, so the plain PQ2_0 sibling could not run it at any flag value), it
# loads, and it accepts well (mean accepted length 2.41) — but the draft context
# bills its own f16 KV over the WHOLE pool (4,096 B/position, 3,072 MiB here)
# and at ubatch 2048 that squeeze halves decode: 167.2 → 109.2 t/s. There is no
# --spec-draft-cache-type knob in this build to make the draft cheap, and no
# --spec-draft-model either: the draft rides the main model's weights. Card gate:
# quantized V alongside MTP needs fork build >= prism-b10776, which is why
# LLAMA_BIN moved to the 10777 prebuilt; on an older build the pair is illegal.
# bills its own f16 KV over the WHOLE pool (4,096 B/position, 2,048 MiB at the
# 524,288 pool) and at ubatch 2048 that squeeze halves decode: 167.2 → 109.2 t/s
# on the q8_0 3-slot shape. The draft cache dtype IS a knob (--spec-draft-type-k
# / --spec-draft-type-v, both default f16 — verified in this build's --help), so
# an armed drafter can halve its own bill; there is no --spec-draft-model because
# the draft rides the main model's weights. Card gate: quantized V alongside MTP
# needs fork build >= prism-b10776, which is why LLAMA_BIN moved to the 10777
# prebuilt; exact KV removes that gate but NOT the type-142 loader lock.
# SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024 is the documented MTP arm (213.5 t/s);
# SPEC_TYPE=none strips the whole set (flags move together).
: "${SPEC_TYPE:=none}"
@@ -104,7 +112,7 @@ fi
: "${NGL:=99}"
# Log verbosity threshold (llama.cpp -lv). 3 is the engine default (info);
# 4 adds debug detail (KV layout, buffer sizes, graph capture) which is what
# the VRAM-fit forensics need — the q8_0 KV contract and the draft-KV bill are
# the VRAM-fit forensics need — the exact-KV size and the draft-KV bill are
# only provable from this log. Levels above 4 flood the journal.
: "${VERBOSITY:=4}"
# /metrics needs the flag: llama.cpp returns 501 unless the server starts with
@@ -169,11 +177,11 @@ fi
# open after boot; make sure it exists before the server needs it.
nvidia-modprobe -u -c=0 2>/dev/null || true
# Flash attention stays pinned on: it is REQUIRED for quantized V (q8_0),
# and the build carries GGML_CUDA_FA_ALL_QUANTS=ON (since 2026-09-17), so
# the q8_0 KV contract in frank-zealot.env is a real path — without this
# flag or that build option the engine silently falls back and the
# contract becomes a lie.
# Flash attention stays pinned on. Exact f16 KV runs fine with or without it,
# but the documented fallback rung (quantized V) is ILLEGAL without it, and the
# build carries GGML_CUDA_FA_ALL_QUANTS=ON (since 2026-09-17) so that rung is a
# real path — dropping the flag here would leave the retreat route silently
# falling back to something the env file does not describe.
ARGS=(
--model "$MODEL_FILE"
--alias "$ALIAS"
+4 -2
View File
@@ -43,14 +43,16 @@ As of 2026-10-07 the picker exposes **exactly one model: `frank-zealot`** *(unve
|---|---|---|---|---|---|
| 0 | `http://host.containers.internal:7770/v1` | no (2026-10-07) | `frank-dragoon` | halogen flash-server (Strix Halo iGPU) | Dankclaw |
| 1 | `http://192.168.0.235:7770/v1` | no (2026-10-07) | `frank-archon` | radiance TP=2 (2× R9700) | Dankserver |
| 2 | `http://host.containers.internal:7771/v1` | yes | `frank-zealot` | llama.cpp CUDA (RTX 3090; production shape 3×262,144 per `scripts/frank-zealot.env`) | Dankclaw |
| 2 | `http://host.containers.internal:7771/v1` | yes | `frank-zealot` | llama.cpp CUDA (RTX 3090; production shape 2×262,144 exact f16 KV per `scripts/frank-zealot.env`) | Dankclaw |
The URLs in the first column are the DB's recorded values and predate the 2026-08-22
bridge move; on this network they are wrong — see the reachability gotcha below, which
is the corrected law. Note also that the 2026-10-18 **bench window** on `:7771`
closed the same day (production unit stopped 03:38 per the repo Benchmarking
Policy while candidate arms held the port): the candidate — Ternary-Bonsai-2-**9B**
PQ2_0 with the MTP head, 3 × 262,144 — was **deployed as production**, so the
PQ2_0 with the MTP head, deployed that afternoon at 3 × 262,144 q8_0 KV and
resliced the same evening to **2 × 262,144 over exact f16 KV** — was **deployed
as production**, so the
picker row below was re-keyed to it (see the next paragraph).
The `model` table carries exactly two decoration rows, `frank-zealot` and
@@ -26,7 +26,7 @@ timestamp: 2026-10-03
> the PrismML 27B era of [[frank-zealot-llama-prism-bonsai2-pq20]] (build 10754,
> 2 × 200,704 q8_0 KV, MTP off) — retired 2026-10-17 → 10-18 as a rollback
> target; the active row today is build **10777** on
> `Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf` at 3 × 262,144 q8_0 KV, MTP off by
> `Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf` at 2 × 262,144 exact f16 KV, MTP off by
> measurement (AGENTS.md Runbook Index). The Q5_K_M
> payload is still on disk (`stat -c %s` 2026-10-18 → 6876124704).
@@ -20,13 +20,16 @@ timestamp: 2026-10-01
> [[frank-zealot-llama-mimo-q5km]], the same runtime and shape one quant rung
> down. The Q6_K file stays at `/home/dank/models/MiMo-Q6K/` as the rollback
> target (on disk 2026-10-18, `stat -c %s` → 7793710624). :7771 today runs
> [[frank-zealot-llama-prism-bonsai2-pq20]] (llama.cpp PrismML fork build 10754,
> `Ternary-Bonsai-2-27B-PQ2_0.gguf`, 2 × 200,704, q8_0 K + q8_0 V, MTP off).
> [[frank-zealot-llama-prism-bonsai9-mtp]] (llama.cpp PrismML fork build
> **10777**, `Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 2 × 262,144,
> exact f16 K + f16 V, MTP off by measurement).
>
> Original order (Frank, 2026-10-01 night): move this endpoint to the GGUF
> path for **asymmetric KV** — unquantized K + q8_0 V — which only llama.cpp
> exposes per-tensor. The per-tensor lever stayed; this pairing did not — the
> live endpoint bills q8_0 K + q8_0 V (`scripts/frank-zealot.env:55-56`).
> live endpoint bills **exact f16 K + f16 V** — 32,768 B/token = 16,384 MiB
> over the 524,288-position pool of two 262,144 windows
> (`scripts/frank-zealot.env:57-58,80-81`).
## Identity
@@ -1,7 +1,7 @@
---
type: reference
title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork 10777), Bonsai-2-9B PQ2_0 with the MTP head, 3 × 262144, q8_0 KV (ACTIVE)"
description: "The ACTIVE frank-zealot runbook (since 2026-10-18) — the PrismML fork build 10777 of llama.cpp serving prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 on the RTX 3090: THREE conversations of 262,144 tokens (every slot at the model's trained window) over q8_0 K + q8_0 V, vision through the publisher's Q8_0 Qwen3-VL projector, and the five-arm boot census that put speculative decoding OFF by measurement even though the checkpoint carries the MTP head."
title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork 10777), Bonsai-2-9B PQ2_0 with the MTP head, 2 × 262144, exact f16 KV (ACTIVE)"
description: "The ACTIVE frank-zealot runbook (since 2026-10-18) — the PrismML fork build 10777 of llama.cpp serving prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 on the RTX 3090: TWO conversations of 262,144 tokens (every slot at the model's trained window) over an UNQUANTIZED f16 K + V cache, vision through the publisher's Q8_0 Qwen3-VL projector, and the boot-arm census that put speculative decoding OFF by measurement even though the checkpoint carries the MTP head."
tags:
- llm
- frank-zealot
@@ -14,15 +14,22 @@ tags:
timestamp: 2026-10-18
---
# frank-zealot — llama.cpp (PrismML) Bonsai-2-9B PQ2_0 + MTP head, 3 × 262,144, q8_0 KV
# frank-zealot — llama.cpp (PrismML) Bonsai-2-9B PQ2_0 + MTP head, 2 × 262,144, exact f16 KV
Live since 2026-10-18, replacing the
[[frank-zealot-llama-prism-bonsai2-pq20]] era (Ternary-Bonsai-2-**27B** PQ2_0 at
2 × 200,704). The trade in one sentence: the 9B bills **half** the KV per token
of the 27B, and that is exactly what funds **three** conversations at the
model's **native 262,144** window where the 27B afforded only two shortened
ones (Frank's ask: *"if you can possibly fit a third context, do it"*). Every
axis improved — decode, prefill, three-stream aggregate, and free VRAM.
2 × 200,704). The original trade: the 9B bills **half** the KV per token of the
27B, and that is what funded **three** conversations at the model's **native
262,144** window where the 27B afforded only two shortened ones (Frank's ask:
*"if you can possibly fit a third context, do it"*).
The shape moved again the same evening (Frank: *"shift frank-zealot to 2
parallels of unquantized kv"*). The KV cache went **exact** — f16 K + f16 V, no
quantization error anywhere in the attention state — and the **slot count paid
for it: 3 → 2**. The per-slot contract never moved; every conversation is still
262,144, still the trained window. What the dtype bought is fidelity, and what
it costs is one concurrent conversation plus the bytes attention reads per
generated token.
## Payload identity
@@ -46,9 +53,12 @@ axis improved — decode, prefill, three-stream aggregate, and free VRAM.
refuses the custom type at load; a loader that ignored the Hadamard metadata
would dequant **wrong**, silently. Same lock class as the ROCmFPX era.
2. **The build floor.** The fork's card gates **MTP beside a quantized V cache**
on `prism-b10776-d00e252` or newer, so the prebuilt moved **10754 → 10777**
for this swap. The 10754 tree stays on disk (`~/GitPackages/llama-prism/`) as
the 27B rollback runtime. `tests/frank_zealot_unit.bats` pins both paths.
on `prism-b10776-d00e252` or newer, which is why the prebuilt moved
**10754 → 10777** for the swap. Exact f16 KV makes that gate moot — there is
no quantized V to pair with the drafter — but tumbler 1 pins the runtime on
its own: stock llama.cpp cannot load this file at any flag value. The 10754
tree stays on disk (`~/GitPackages/llama-prism/`) as the 27B rollback
runtime. `tests/frank_zealot_unit.bats` pins both paths.
## Header census (read from the GGUF, not the card)
@@ -71,41 +81,66 @@ axis improved — decode, prefill, three-stream aggregate, and free VRAM.
| `general.sampling.*` | temp 1.0 / top_p 0.95 / top_k 20 / min_p 0.0 / repeat 1.0 | the card recipe; llama.cpp reads it itself |
**KV accounting.** Only the 8 full-attention layers scale with context:
8 × 4 KV heads × 256 dim = **8,192 elements/token per cache**, so
**q8_0 K + q8_0 V bills 17,408 B/token** — the 786,432-position pool is
**13,056.00 MiB**, ledger-exact. That is exactly HALF the 27B's 34,816 B/token.
**f16 KV would bill 26,112 MiB against a 24,123 MiB card** and dies at
`cudaMalloc`. The 24 DeltaNet layers bill a fixed recurrent state per SLOT
instead: the PRODUCTION ledger prints `RS buffer size = 150.75 MiB (3 cells,
32 layers, 3 seqs)` ≈ 50 MiB a cell, while the SPEC-ARMED arms printed
452.25 MiB — MTP depth 2 bills three seqs per slot, so the drafter triples
even the recurrent state. Pool arithmetic alone under-prices the shape.
8 × 4 KV heads × 256 dim = **8,192 elements/token per cache** (the engine prints
`n_embd_k_gqa = n_embd_v_gqa = 1024`). **The deployed pair is exact: f16 K +
f16 V bills 32,768 B/token** (2 B/element × 8,192 elements × 2 caches), so the
524,288-position pool is **16,384 MiB**. The q8_0 pair this endpoint shipped
first billed **17,408 B/token** — 1.0625 B/element, 32 codes plus one 2-byte
scale per 32-block — the **13,056.00 MiB** its ledger printed at the 786,432
pool, exactly HALF the 27B's 34,816 B/token.
**Corrected arithmetic, and it matters.** The note that lived here and in
`frank-zealot.env` — "f16 would bill 26,112 MiB" — doubled the q8_0 figure
*including* its scale overhead, overstating exact KV by 6.25 %. Exact f16 at the
786,432 pool is **24,576 MiB** against the **24,123 MiB** the driver leaves
addressable (`nvidia-smi`'s total column reads 24,576; used + free reads 24,123
— the difference is driver reservation). The wall stands in both readings; the
honest rate is what makes the fit calculable instead of superstitious. **Halving
the POOL is what funds exact KV** — the window is already at the native ceiling
and there is no YaRN in this path.
The 24 DeltaNet layers bill a fixed recurrent state per SLOT instead: the 3-slot
ledger printed `RS buffer size = 150.75 MiB (3 cells, 32 layers, 3 seqs)` ≈
**50.25 MiB a cell**, so two slots bill 100.50. The SPEC-ARMED arms printed
452.25 MiB — MTP depth 2 bills three seqs per slot, so the drafter triples even
the recurrent state. Pool arithmetic alone under-prices the shape.
## The shape (`scripts/frank-zealot.env`)
`--ctx-size 786432 --parallel 3` (⇒ `n_ctx_slot = 262,144`), `--flash-attn on`,
`--cache-type-k q8_0 --cache-type-v q8_0`, `--batch-size 4096`,
`--ctx-size 524288 --parallel 2` (⇒ `n_ctx_slot = 262,144`), `--flash-attn on`,
`--cache-type-k f16 --cache-type-v f16`, `--batch-size 4096`,
`--ubatch-size 2048`, `--n-gpu-layers 99`, `--jinja`, `--reasoning auto`,
`--reasoning-format deepseek`, `--metrics`, `--mmproj …`, `--log-verbosity 4`,
`--presence-penalty 1.0`,
**`SPEC_TYPE=none`** (which strips `--spec-type` AND every `--spec-draft-*`).
Per-slot window is the **contract** the harness quotes: `models.yml` carries
`contextWindow: 262144`. llama.cpp refuses a prompt longer than the SLOT with a
400 naming the limit — it never truncates — and since this swap the slot window
equals the native window, so no YaRN and no cap is in play.
`contextWindow: 262144`, and it did **not** move with the slot count because
llama.cpp divides `--ctx-size` across slots. It refuses a prompt longer than the
SLOT with a 400 naming the limit — never a truncation — and since this swap the
slot window equals the native window, so no YaRN and no cap is in play.
`tests/frank_zealot_unit.bats` now gates the arithmetic: the shipped dtype ×
pool plus the measured non-KV ledger terms must fit 24,123 MiB with ≥1 GiB of
slack, and the gate is proven to REFUSE the impossible arm (exact KV at the old
786,432 pool = 24,576 MiB of cache alone).
## The MTP verdict: the head is in the file and it is still OFF
The drafter is **good**: acceptance 0.798 / 0.617 per position, mean accepted
length 2.41, and the card recommends depth 2. It loses **at this shape**
because the draft context bills its own **f16** KV over the WHOLE pool —
`blk.32` carries real attention (4 KV heads × 256 × 2 caches × 2 B =
**4,096 B/position**) × 786,432 positions = **3,072.00 MiB**, 4,830 MiB with its
compute buffers — and `--ubatch-size 2048` refuses to give that up.
length 2.41, and the card recommends depth 2. It is OFF because the draft
context bills its own **f16** KV over the WHOLE pool — `blk.32` carries real
attention (4 KV heads × 256 × 2 caches × 2 B = **4,096 B/position** at the
default dtype). The draft dtype is itself a knob — `--spec-draft-type-k` /
`--spec-draft-type-v`, both default f16, verified against this build's `--help`
(so the "no flag exists" claim that lived here is FALSE) — and quantizing the
draft to q8_0 would bill 2,176 B/position, **1,088 MiB** at the shipped pool.
**No measured arm runs a drafter beside exact KV**; every row below is f16 draft.
Booted arms, same card, same traffic, 2026-10-18 (`/completion` `timings`,
greedy 512 tokens; never the first request after a boot):
The census below is the **q8_0 era**: every row is q8_0 K + q8_0 V at the
786,432 pool, where the draft bill was 3,072.00 MiB and 4,830 MiB with its
compute buffers. Booted arms, same card, same traffic, 2026-10-18
(`/completion` `timings`, greedy 512 tokens; never the first request after a
boot):
| slots | spec | ubatch | used / free | decode | 3-stream | prefill @106k |
|---|---|---|---|---|---|---|
@@ -117,15 +152,26 @@ greedy 512 tokens; never the first request after a boot):
**MTP is worth +28 % single-stream decode whenever the card has room** (rows
4–5 prove the drafter itself is a win; row 2 proves the squeeze is what halves
it). The arm that runs MTP at three windows is `SPEC_TYPE=draft-mtp` +
`UBATCH_SIZE=1024` (row 3) and pays **prefill −17 %** and **2,725 MiB of
it). The arm that ran it at three windows was `SPEC_TYPE=draft-mtp` +
`UBATCH_SIZE=1024` (row 3), paying **prefill −17 %** and **2,725 MiB of
headroom** on a card whose mmproj worst-case lands AFTER the engine's own fit
check. This endpoint is the fleet's **compaction hub** — prefill-bound ~170k
inputs plus 2–3 concurrent subagents — so the shipped arm takes the +17 %
prefill and +12 % three-stream aggregate over single-stream speed, and keeps
6.2 GiB free. Frank confirmed this arm on 2026-10-18.
check.
## Boot ledger (production boot 2026-10-18 13:19, under the unit, verbosity 4)
**Under exact KV an f16-draft arm is priced out of this card.** The draft
context is built at the TARGET's pool, so at 524,288 positions it bills
4,096 × 524,288 = **2,048.00 MiB** plus its own buffers, on a trunk that already
projects **~21,200 MiB** of the 24,123 MiB addressable. The doors back to
speculation are **one window per conversation** (row 5's class of shape), **KV
back to q8_0** with `UBATCH_SIZE=1024`, or the **unmeasured**
`--spec-draft-type-k q8_0 --spec-draft-type-v q8_0` arm — 1,088 MiB of draft,
legal on b10777, never booted beside exact KV. Frank chose exact KV over the
drafter on 2026-10-18, and the endpoint's actual job is what made that
affordable: the fleet's **compaction hub** is prefill-bound on ~170k inputs,
prefill is weight-bandwidth bound and does not re-read the cache per generated
token — so the dtype is paid in decode and concurrency, not in the work this
service exists to do.
## Boot ledger — the q8_0 3-slot production boot (2026-10-18 13:19, under the unit, verbosity 4)
```
CUDA0 model buffer size = 2036.71 MiB # + 257.66 MiB CPU_Mapped (mmap)
@@ -141,7 +187,30 @@ No clamp line, no `cudaMalloc` error. The engine's own projection (21,439-class
numbers from the 27B era) excludes the projector — **read settled `nvidia-smi`,
never the projection**.
## Measured (production boot, `/completion` `timings`, greedy, `ignore_eos`)
**The exact-KV boot, measured** (2026-10-18 evening, PR #21, under the unit,
verbosity 4 — the live shape):
```
CUDA0 model buffer size = 2036.71 MiB # + 257.66 MiB CPU_Mapped (mmap)
CUDA0 KV buffer size = 16384.00 MiB # f16 K + f16 V, 524,288 positions
CUDA0 RS buffer size = 100.50 MiB # 2 cells, 32 layers, 2 seqs
CUDA0 compute buffer = 1408.06 MiB # + 1088.07 MiB CUDA_Host
mmproj worst-case (est.) = 868.32 MiB # 248.10 CUDA + 24.93 CPU, AFTER fit
nvidia-smi settled: 21,176 MiB used / 2,949 MiB free of 24,123
n_slots = 2, n_ctx_slot = 262144, kv_unified = false
```
The pre-flight projection was right to the MiB, because the two terms it moved
are exact arithmetic: **16,384.00 MiB of KV is precisely 32,768 B/token ×
524,288 positions** — rate derivation and ledger agree with no slop, which is
what buries the old 26,112 MiB figure for good — and the recurrent state is one
50.25 MiB cell lighter (100.50 exactly). Settled card state landed at **21,176
used / 2,949 free**, within 24 MiB of the 21,200 / 2,920 projection,
`DropInPaths` empty, no clamp line, no `cudaMalloc` error. It is a desktop
margin, though: the mmproj's 868 MiB worst case is still charged AFTER the
engine's own fit check, so ~2 GiB is what an image storm has to live inside.
## Measured — the q8_0 3-slot boot (`/completion` `timings`, greedy, `ignore_eos`)
- **168.7 t/s** decode — three runs of 512 tokens: 168.4 / 168.7 / 168.7
(the trial arm printed 167.2; same shape, same noise band).
@@ -156,15 +225,52 @@ never the projection**.
stops on its own: a 24-token countdown measured 72 t/s across three streams
while the same shape with `ignore_eos` gave 300.
## Measured — the exact-KV boot (floors, not a clean A/B)
Instrument: `/v1/chat/completions` `timings` (this fork refuses the raw
`/completion` path for greedy runs — see *Contract traps*), greedy,
`ignore_eos`, 512 tokens, first request discarded.
|Arm|q8_0 3-slot record|f16 2-slot, live co-tenancy|
|---|---|---|
|single-stream decode|168.7|**134.1 – 141.5** over five back-to-back runs; 145.8 and 163.2 in a lighter minute|
|concurrent aggregate|≈300 across 3 (102.8 each)|**256.6 / 262.4 / 265.1** across 2 (≈128–133 each)|
|prefill at ~106k|2,877 at 106,418|**3,159.5 at 106,282**|
|decode at 106k depth|never measured|**71.5**|
**These are floors, not the shape's rates.** Someone else was driving the
endpoint through the whole window — 56 new prompts on the other slot in three
minutes, `nvidia-smi` at 96 % GPU-util — which is the exact condition the
Benchmarking Policy forbids, and a contended deep arm even came back truncated
(a 512 KB body accepted as `prompt_n = 4`). What the floors already support:
**per-conversation concurrency improved** (two streams hold ~130 t/s each where
three held 102.8), endpoint aggregate sits ~12 % under the 3-slot record,
prefill beat the record (+9.8 %) while contended, and single-stream decode is
between parity and −20 % against it. The 1.882× KV byte bill is real; its true
size on this card is still unmeasured, and quoting 134 t/s or 163 t/s as "the"
number would be a guess in either direction. Re-run the same arms with the box
quiet before the figure is carried anywhere else.
## Contract traps
- **`--metrics` is mandatory.** Without it llama.cpp answers **501** on
`/metrics`, so the Prometheus scrape reads DOWN while `/health` answers 200 —
that is how this endpoint sat red across four payload generations
(`up{job="frank-zealot"}` oldest retained sample, 2026-10-11, is already 0).
- **Bare `/completion` is a dead instrument on this fork build.** A greedy
`ignore_eos` run answers **500 — "The model produced output that does not
match the expected Content-only format"**, because the fork guards the raw
path the moment reasoning markup leaks (older builds only logged `failed to
parse reasoning content` and answered anyway). The honest per-request
instrument on THIS build is `/v1/chat/completions`, whose non-stream response
carries the same `timings` object (`prompt_n`, `prompt_per_second`,
`predicted_n`, `predicted_per_second`). `AGENTS.md` still names the
`/completion` `timings` object as this endpoint's instrument — on build 10777
read it off the chat path.
- **Q8_0 mmproj, not BF16.** The publisher's card: the BF16 tower "can crash
llama-server when several image requests run at once; Q8_0 is not affected" —
and `--parallel 3` is precisely that shape.
and `--parallel 2` is still precisely that shape.
- **Only the `image_url` content part reaches the model.** The legacy top-level
`images` field bumps `usage.prompt_tokens` and the model still invents an
answer. Probe with a solid color and expect the exact word.
@@ -216,9 +322,11 @@ never the projection**.
`frank-zealot` here — 262,144 is the widest window in the fleet and the only one
that comfortably fits a worst-case compaction input (~170k tokens; the clamp is
`min(maxTokens 16384, contextWindow − est − 4096)`, compaction fires at 85 % of
the window ≈ 222,822 est, worst case ≈ 245,891 ≤ 262,144). With `--parallel 3`
a compaction request no longer serializes behind a live generation (the Gemma4
era's `--parallel 1` law).
the window ≈ 222,822 est, worst case ≈ 245,891 ≤ 262,144). What the reslice to
**two** slots does cost is here: a compaction request still runs beside ONE live
generation, but with two conversations already busy a third waits — the Gemma4
era's `--parallel 1` serialization law at half strength. The window itself is
untouched, and the window is the term the compaction contract depends on.
## Deploy ledger (2026-10-18)
@@ -263,12 +371,24 @@ era's `--parallel 1` law).
## Fallback rungs
1. `UBATCH_SIZE=1024` — frees **1,306 MiB** measured on this shape (prefill
−17 %).
2. `CACHE_TYPE_K/V=q4_0` — frees ~6.5 GiB at three windows (the 27B era measured
the q4_0 rung at 16,392 used / 7,733 free at 2 × 200,704).
3. Cut to 2 slots — but that breaks the third concurrent subagent, so it is a
last resort, not a knob.
Cheapest first; every one is a single line of `scripts/frank-zealot.env`, and
the KV figures are for the 524,288-position pool unless stated otherwise:
1. `UBATCH_SIZE=1024` — hands back **1,306 MiB** of compute buffers measured on
this card (prefill −17 %).
2. `CACHE_TYPE_V=q8_0` — exact K, quantized V: the pool goes 16,384 →
**12,544 MiB**, freeing 3,840, and it is the halfway door back.
`--flash-attn on` is what keeps quantized V legal.
3. `CTX_SIZE=401408` — two 200,704 conversations: exact KV at
**12,544.00 MiB**, same 3,840 freed, but `models.yml` `contextWindow` MUST
follow the per-slot number or long turns start 400-ing.
4. Revert to `q8_0` K + `q8_0` V at `CTX_SIZE=786432 PARALLEL=3` — the shape
this one replaced: 13,056.00 MiB of cache and the THIRD conversation back.
5. `CACHE_TYPE_K/V=q4_0` — the far rung: 10,240 B/token ⇒ **5,120 MiB** here
(the 27B era measured q4_0 at 16,392 used / 7,733 free at 2 × 200,704).
Any retune has to keep the seam's fit gate green — it is the only instrument
that can see a boot that would die at `cudaMalloc`.
## Rollback
@@ -18,7 +18,7 @@ timestamp: 2026-10-07
[[frank-zealot-llama-prism-bonsai2-pq20]] (27B PQ2_0, 2 × 200,704 q8_0 KV, build
10754), itself retired 2026-10-17 → 10-18 and now the rollback target: :7771
today is llama.cpp PrismML build **10777** on
`Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 3 × 262,144 q8_0 KV, MTP off by
`Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 2 × 262,144 exact f16 KV, MTP off by
measurement (AGENTS.md Runbook Index active row; `scripts/frank-zealot.env`).
Transient 2026-10-18: :7771 is inside a bench window — the unit is `failed`
since 03:14 and a hand-booted process answers the port; restore with
@@ -22,7 +22,7 @@ timestamp: 2026-10-06
> frank-small the same night). Superseded by
> [[frank-zealot-llama-qwen35-9b-q6k-2x200704]]; the production shape on :7771
> today is llama.cpp PrismML build **10777** on
> `Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 3 × 262,144 q8_0 KV, MTP off by
> `Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 2 × 262,144 exact f16 KV, MTP off by
> measurement (AGENTS.md Runbook Index active row); the 27B PQ2_0 / 2 × 200,704
> shape of [[frank-zealot-llama-prism-bonsai2-pq20]] is that era's rollback
> target. Transient 2026-10-18 bench window on :7771 — the unit is `failed`
@@ -20,8 +20,9 @@ timestamp: 2026-10-01
> **RETIRED 2026-10-01, hours after cutover** (AGENTS.md Runbook Index). Replaced by
> [[frank-zealot-llama-mimo-q6k]] — llama.cpp CUDA on the MiMo Q6_K GGUF,
> chosen for the asymmetric KV (f16 K + q8_0 V) SGLang cannot express. :7771
> today runs [[frank-zealot-llama-prism-bonsai2-pq20]] (llama.cpp PrismML fork
> build 10754, `Ternary-Bonsai-2-27B-PQ2_0.gguf`, 2 × 200,704 q8_0 KV, MTP off).
> today runs [[frank-zealot-llama-prism-bonsai9-mtp]] (llama.cpp PrismML fork
> build **10777**, `Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 2 × 262,144,
> exact f16 K + f16 V, MTP off by measurement).
> The venv (`/home/dank/GitPackages/sglang-cu128/`) is still on disk; the INT8
> payload was staged to
> `/home/dank/.AGENTTRASH/20261002-retired-llm-payloads/` on 2026-10-02 and
@@ -59,8 +60,8 @@ timestamp: 2026-10-01
the two-way concurrency contract. **That compaction arithmetic is
specific to a 262,144-token slot**: the shape that followed on 2026-10-02
ran three slots of 180,224, which 183,501 tokens already exceeds — see *What
three slots cost* in [[frank-zealot-llama-mimo-q5km]]; :7771 runs two 200,704
slots today.
three slots cost* in [[frank-zealot-llama-mimo-q5km]]; :7771 today runs two
262,144-token conversations over one 524,288-position pool with exact f16 KV.
- **KV:** `fp8_e4m3` — 16 KiB/token (8 attn layers x 2 x 4 heads x 256
dim x 1 B), pool 9.31 GiB.
- **The memory shape (the 4.5 GiB the naive math misses):** the DeltaNet
+25 -17
View File
@@ -26,9 +26,10 @@ MTP spec decode always-on (HALOGEN_SPEC_ADAPT=0), TWO slots over ONE
200704, vision ON)** and
**llama.cpp on the PrismML fork (frank-zealot, port 7771: prism-ml
Ternary-Bonsai-2-9B at **PQ2_0** — 9B qwen35 hybrid (8 full-attention + 24
Gated DeltaNet layers + the blk.32 MTP block), THREE 262,144-token
conversations, MTP present in the file but OFF by measurement,
q8_0 K + q8_0 V (f16 KV does not fit the RTX 3090 at this window), vision via
Gated DeltaNet layers + the blk.32 MTP block), TWO slots over ONE
524,288-position pool (2 × the 262,144 window), MTP present in the file but
OFF by measurement, **exact f16 K + f16 V** — 32,768 B/token = **16,384 MiB**
of KV (three 262,144 windows at exact KV do not fit the RTX 3090), vision via
the publisher's own Q8_0 mmproj, on the RTX 3090 — and the custom
tensor type 141 + `prism.hadamard.*` metadata mean no other loader can open the
file)**.
@@ -63,7 +64,7 @@ ONE 401408-position KV pool at ctx 200704 = 2 slots x ctx, MTP always-on, image
G[llama.cpp PrismML fork CUDA build
frank-zealot, port 7771]
H[Ternary-Bonsai-2-9B PQ2_0 GGUF + Q8_0 mmproj
3 x 262144 ctx, q8_0 K + q8_0 V, MTP off, mmproj vision]
2 slots over ONE 524288-position pool at ctx 262144, exact f16 K + f16 V, MTP off, mmproj vision]
E2 --> E --> F
E2 --> G --> H
end
@@ -132,19 +133,26 @@ frank-zealot, port 7771]
custom PQ2_0 tensor type + `prism.hadamard.*` transform, and since this swap
also the minimum build that pairs MTP with a quantized V cache) serving
prism-ml/Ternary-Bonsai-2-9B-gguf **PQ2_0 with the MTP head IN the file** on
the RTX 3090 — **THREE 262,144-token conversations**
(`--ctx-size 786432 --parallel 3`, every slot at the window the model was
trained at), **q8_0 K + q8_0 V** (17 KiB/token over the 8 full layers =
13,056 MiB ledger-exact; the f16 pair needs 26,112 MiB and dies at
`cudaMalloc`), settled **17,876 used / 6,249 free**, vision via the
publisher's **Q8_0** mmproj (Q8_0 is the publisher's requirement for
concurrent image requests, and this runs three slots), **MTP OFF by
measurement** — the draft context bills its own f16 KV over the WHOLE pool
(3,072 MiB) and at `UBATCH_SIZE=2048` that squeeze halves decode
(167.2 → 109.2 t/s); `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` is the
documented 213.5 t/s arm, declined for +17 % prefill and 6.2 GiB of headroom.
Measured: 167.2 t/s single-stream decode, 2,877 t/s prefill at 106,418,
280.2 t/s aggregate across three streams. `--metrics` rides (`METRICS=1`):
the RTX 3090 — **TWO 262,144-token conversations**
(`--ctx-size 524288 --parallel 2`, every slot at the window the model was
trained at), **unquantized f16 K + f16 V** (32,768 B/token over the 8 full
layers = **16,384 MiB** exact; the POOL halved because that rate over the
OLD 786,432 pool bills 24,576 MiB against the 24,123 the driver leaves
addressable, and the old "26,112 MiB" note was wrong — it doubled the
q8_0 figure (17,408 B/token = 13,056 MiB) *including* its 1/32 scale
overhead, so f16/q8_0 is **1.882×**, not 2×), the q8_0-era boot settled
**17,876 used / 6,249 free**, vision via the publisher's **Q8_0** mmproj
(Q8_0 is the publisher's requirement for concurrent image requests, and this
runs two slots), **MTP OFF by measurement** — the draft context bills its own
f16 KV over the WHOLE pool (4,096 B/position × 524,288 = 2,048 MiB) and at
`UBATCH_SIZE=2048` that squeeze halves decode (167.2 → 109.2 t/s);
`SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` is the documented 213.5 t/s arm,
declined for +17 % prefill and 6.2 GiB of headroom — and exact KV leaves an
f16-draft arm no room at all (a quantized draft, `--spec-draft-type-* q8_0`,
is the unmeasured door).
Measured at the q8_0-era shape: 167.2 t/s single-stream decode, 2,877 t/s
prefill at 106,418, 280.2 t/s aggregate across three streams. `--metrics`
rides (`METRICS=1`):
llama.cpp answers 501 on `/metrics` without it, which is why Prometheus
`up{job="frank-zealot"}` read 0 for its whole retained history until
2026-10-18. `contextWindow: 262144` is the **widest in the fleet**, so this
+9 -4
View File
@@ -409,10 +409,15 @@ The LLM inference fleet on the two hosts. Full index:
loader for the custom PQ2_0 tensor type / `prism.hadamard.*`, and the floor
for MTP beside a quantized V cache) serving
prism-ml/Ternary-Bonsai-2-9B-gguf **PQ2_0 with the MTP head** on the RTX 3090
— THREE 262,144-token conversations, q8_0 K + q8_0 V (f16 OOMs at this pool:
26,112 vs 13,056 MiB over 8 full-attention layers), MTP **off by
measurement** (the draft context bills 3,072 MiB of f16 KV over the whole
pool), vision through the Q8_0 mmproj; boot 17,876 used / 6,249 free
— TWO 262,144-token conversations (`--ctx-size 524288 --parallel 2`), exact
f16 K + f16 V = **16,384 MiB** over 8 full-attention layers (32,768 B/token;
the per-slot window is UNCHANGED at 262,144 — what halved is the POOL,
because exact f16 over the OLD 786,432 pool bills **24,576 MiB** against the
**24,123** the driver leaves addressable; the old "26,112 vs 13,056 MiB" note
doubled the q8_0 figure with its 1/32 scale overhead included, so f16/q8_0 is
1.882×, not 2×), MTP **off by measurement** (the draft context bills 2,048
MiB of f16 KV over the whole pool, and exact KV leaves no room for one),
vision through the Q8_0 mmproj; boot 17,876 used / 6,249 free (the q8_0 era)
- [[frank-zealot-llama-prism-bonsai2-pq20]] — RETIRED 2026-10-17 → 10-18
(rollback target, payload + build 10754 on disk): Ternary-Bonsai-2-**27B**
PQ2_0 at TWO 200,704-token conversations, q8_0 K + q8_0 V, MTP **impossible**
+61
View File
@@ -1,5 +1,32 @@
# Bundle Change Log
## 2026-10-18
- **frank-zealot resliced to two windows of exact KV (Frank's call, PR #21).**
`--ctx-size 786432 --parallel 3` at q8_0 K+V → **`524288 / 2` at f16 K+V**, so
both remaining conversations keep the native 262,144 window and the
compaction hub reads exact K and V. Halving the pool is what funds it: the
f16 pair is 32,768 B/token over the 8 full-attention layers = 16,384.00 MiB,
while the old 786,432-position pool would have needed ~24,576 MiB of KV alone
on a card that leaves 24,576 addressable. Measured boot: KV **16,384.00**,
RS **100.50** (2 cells), compute 1,408.06 + 1,088.07, settled `nvidia-smi`
**21,176 used / 2,949 free**, `n_ctx_slot = 262144`, `DropInPaths` empty. The
doc's old "f16 needs 26,112 MiB and cannot boot" figure was the 27B's
34,816 B/token applied to the 9B — corrected in the runbook.
- **Rates are floors, and that is recorded as such.** The endpoint had another
consumer for the whole measurement window (56 prompts on the other slot in
three minutes, 96 % GPU-util), so single-stream decode came back 134.1–141.5
(145.8/163.2 in a lighter minute) against the 3-slot record of 168.7,
two-stream aggregate 256.6–265.1 against ≈300 across three, and prefill
**3,159.5 at 106,282** beat the record's 2,877 while contended. Per-stream
concurrency improved (≈130 each vs 102.8); a clean re-measure needs the box
quiet.
- **Instrument correction (fleet law).** On the PrismML fork build 10777 the raw
`/completion` path answers **500 "The model produced output that does not
match the expected Content-only format"** for a greedy `ignore_eos` run, so
the honest per-request `timings` for this endpoint must be read off
`/v1/chat/completions`. `AGENTS.md` said `/completion`; it now says the chat
path.
## 2026-10-13
- **frank-archon outage: a kernel knob, not the model.** The 2026-10-12 23:17
power failure on dankserver reset `/sys/module/ttm/parameters/pages_limit`
@@ -2787,3 +2814,37 @@ every restart since the cutover.
to the player, temp dir cleaned; the audible pass through the default
`pw-play` detection confirmed by Frank. Sessions open before the merge need
a restart to pick the command up.
## 2026-10-18 — frank-zealot: 2 × 262,144 over unquantized f16 KV
- Frank's instruction: **"shift frank-zealot to 2 parallels of unquantized
kv"**. The env delta is four lines: `CTX_SIZE=524288`, `PARALLEL=2`,
`CACHE_TYPE_K=f16`, `CACHE_TYPE_V=f16`. Everything else holds — batch
**4096** / ubatch **2048**, `--flash-attn on`, `--jinja`, `--metrics`,
`--presence-penalty 1.0`, `SPEC_TYPE=none`, the Q8_0 mmproj, port 7771, and
the per-slot window **262,144** — `models.yml` `contextWindow` untouched.
- **Why the pool had to halve.** Exact KV bills **32,768 B/token** (8
full-attention layers × 4 KV heads × 256 dim × 2 B × 2 caches), so the old
786,432 pool asks **24,576 MiB** against the **24,123 MiB** the driver leaves
addressable — impossible, not tight. 524,288 positions ⇒ **16,384 MiB** and
exactly two windows of 262,144.
- Arithmetic correction to the repo's long-standing figure: "the f16 pair would
need **26,112 MiB**" doubled the q8_0 number *including* its 1/32 scale
overhead — q8_0 is 17,408 B/token (1.0625 B/element), so f16/q8_0 is
**1.882×**, not 2×, and exact f16 over 786,432 is **24,576 MiB**. The
conclusion stands (three f16 windows cannot boot this card); the number was
wrong.
- The cost: **concurrency 3 → 2 conversations**, and MTP is now **doubly** out.
The 13:16 measurement still says off, and exact KV adds a second reason — the
draft context bills its own f16 KV over the WHOLE pool (**4,096 B/position**
× 524,288 = **2,048 MiB**) on a trunk projecting ~21,200 used.
- **Projection, explicitly not a measurement:** ~**21,200 MiB** used /
~**2,920 free**, derived from the 13:19 ledger's 17,922 used because only two
terms move — the KV pool (**+3,328**) and one recurrent-state cell
(**−50.25**, 3 cells → 2). The measured boot ledger and the decode/prefill
re-measure land in the follow-up commit, after the first f16 boot.
- New gate in `tests/frank_zealot_unit.bats`: dtype × pool plus the measured
non-KV terms must fit **24,123 MiB** with **≥ 1 GiB** of slack — and the same
gate proves it refuses the impossible arm (**f16 at 786,432**), not merely
passes the shipped one. Runbook re-keyed to the new shape:
`LLM/frank-zealot-llama-prism-bonsai9-mtp.md`.
+82 -33
View File
@@ -3,15 +3,16 @@
# prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 — the ternary Bonsai-2-9B, a
# qwen35 hybrid with 32 layers (8 full-attention + 24 Gated DeltaNet) and the
# MTP draft head INSIDE the file — via the PrismML fork build of llama.cpp
# (vendor build 10777, the minimum that pairs MTP with a quantized V cache),
# the ONLY loader that registers the payload's custom tensor type 142 and
# honours its prism.hadamard.* metadata: THREE 262,144-token conversations
# (--ctx-size 786432 --parallel 3), q8_0 K + q8_0 V under flash attention (f16
# KV would bill 26,112 MiB of a 24,123 MiB card), the publisher's own Q8_0
# vision projector (the BF16 one can crash on concurrent images), and MTP OFF
# — measured, not because the head is missing: the draft context bills f16 KV
# over the whole pool and halves decode at this shape. Log verbosity 4 ships so
# the VRAM-fit forensics land in the journal.
# (vendor build 10777), the ONLY loader that registers the payload's custom
# tensor type 142 and honours its prism.hadamard.* metadata: TWO 262,144-token
# conversations (--ctx-size 524288 --parallel 2) over an UNQUANTIZED f16 KV
# cache. Exact KV bills 32,768 B/token here, so the pool is what pays for it —
# the same dtype at the old 786,432 pool is 24,576 MiB against the 24,123 MiB
# the driver leaves addressable, and the slot count is the term that moved. The
# publisher's own Q8_0 vision projector rides along (the BF16 one can crash on
# concurrent images), and MTP stays OFF: the head is present, but exact KV
# leaves no room for a draft context that bills f16 KV over the whole pool. Log
# verbosity 4 ships so the VRAM-fit forensics land in the journal.
# This seam runs the serve script against a stub engine and asserts on the
# argv it actually execs, so what is pinned is the command the operator gets.
@@ -76,6 +77,22 @@ argv_value() {
grep -A1 -xF -- "$1" <<<"$output" | tail -1
}
# kv_bytes_per_token DTYPE — bytes ONE cache costs per token at this payload's
# geometry: 8 full-attention layers × 4 KV heads × 256 dim = 8,192 elements
# (measured from the GGUF header: n_embd_k_gqa = n_embd_v_gqa = 1024). f16 is
# 2 B/element; q8_0 is 1.0625 (32 codes plus one 2-byte scale per 32-block);
# q4_0 is 0.625 (codes + scale + min per 32-block). Unknown dtype is a FAILURE,
# never a silent zero — the fit gate below is only honest if it cannot be
# satisfied by a rung it does not understand.
kv_bytes_per_token() {
case "$1" in
f16|bf16) echo 16384 ;;
q8_0) echo 8704 ;;
q4_0) echo 5120 ;;
*) return 1 ;;
esac
}
# run_stage — exec the CURRENT $STAGE/test.env as-is. serve_argv REWRITES
# that file from the shipped env, so any test that mutates it (drop a line,
# append a drafter) must exec through this, not serve_argv — which silently
@@ -126,7 +143,7 @@ run_stage() {
done
}
@test "three conversations share the pool at a 262144 window each" {
@test "two conversations share the pool at a 262144 window each" {
serve_argv
[ "$status" -eq 0 ]
[ "$(argv_value --alias)" = "frank-zealot" ]
@@ -134,15 +151,13 @@ run_stage() {
[ "$(argv_value --host)" = "0.0.0.0" ]
[ "$(argv_value --model)" = "$MODEL_DIR/$MODEL_STUB" ]
[ "$(argv_value --n-gpu-layers)" -gt 0 ]
# llama.cpp divides --ctx-size across slots, so the observable contract
# is the per-slot window. The 9B swap exists precisely so every
# conversation gets the model's TRAINED 262,144 (the 27B had to be cut to
# 200,704 to fund two), and the harness contextWindow must quote 262144 —
# the pool and the slot count are neither, and a longer prompt is a 400.
# Three is also the ceiling: a fourth slot would push each window under the
# fleet's worst-case compaction input, which is this endpoint's job.
[ "$(argv_value --parallel)" -eq 3 ]
[ "$(argv_value --ctx-size)" -eq 786432 ]
# llama.cpp divides --ctx-size across slots, so the observable contract is
# the per-slot window. Exact KV costs the SLOT COUNT, not the window: every
# conversation still gets the model's TRAINED 262,144, and the harness
# contextWindow must quote that number — the pool and the slot count are
# neither, and a longer prompt is a 400, never a truncation.
[ "$(argv_value --parallel)" -eq 2 ]
[ "$(argv_value --ctx-size)" -eq 524288 ]
local pool slots
pool="$(argv_value --ctx-size)"; slots="$(argv_value --parallel)"
[ $((pool % slots)) -eq 0 ]
@@ -151,15 +166,16 @@ run_stage() {
@test "the KV dtype pair follows the env file and flash-attn stays pinned on" {
# Asserted against the env rather than a literal so a retune moves one
# file, not two. The shipped pair is q8_0/q8_0: this payload bills
# 8 full-attention layers × 4 KV heads × 256 dim = 8,192 elements per token
# per cache, so f16/f16 would need 26,112 MiB of the 24,123 MiB card while
# q8_0 bills 17,408 B/token = 13,056.00 MiB at the 786,432 pool — the
# ledger said exactly that, and it is the referee. The 24 DeltaNet layers
# bill a fixed recurrent state per slot (452.25 MiB for three cells), which
# is why pool arithmetic alone under-prices the shape. flash-attn is not
# decoration: quantized V is only legal with it, and it must stay explicit
# so a future quant swap cannot silently fall back.
# file, not two. The shipped pair is f16/f16: exact attention state, and the
# slot count is what makes it affordable — this payload bills 8,192 elements
# per token per cache (8 full-attention layers × 4 KV heads × 256 dim), so
# exact KV is 32,768 B/token = 16,384.00 MiB at the 524,288 pool, where the
# q8_0 pair this file used to ship billed 17,408 B/token = 13,056.00 MiB at
# the old 786,432 pool (ledger-exact both times). The 24 DeltaNet layers
# bill a fixed recurrent state per slot (~50.25 MiB a cell), which is why
# pool arithmetic alone under-prices the shape. flash-attn is not
# decoration: the quantized-V fallback is only legal with it, so it stays
# explicit and a dtype retune cannot silently fall back.
local env_k env_v
env_k="$(sed -nE 's/^CACHE_TYPE_K=(.+)$/\1/p' "$ENV_FILE")"
env_v="$(sed -nE 's/^CACHE_TYPE_V=(.+)$/\1/p' "$ENV_FILE")"
@@ -170,6 +186,38 @@ run_stage() {
[ "$(argv_value --flash-attn)" = "on" ]
}
@test "the shipped KV dtype and pool actually fit the card this boots on" {
# No health gate can see this mistake: llama.cpp sizes the KV pool as
# rate × pool-positions and cudaMalloc fails at boot ON THE LIVE ENDPOINT.
# The fit is arithmetic off the payload geometry plus the non-KV terms
# measured off the boot ledger (weights 2,036.71 + compute buffers 1,409.02
# + projector 595 + 248.10 + CUDA context ≈ 460 = 4,749 MiB, dtype- and
# slot-independent at this ubatch), plus one ~51 MiB recurrent-state cell
# per slot. 24,123 MiB is what the driver leaves addressable (nvidia-smi
# reports it as used + free; its total column reads 24,576).
local env_k env_v pool slots rate_k rate_v kv total
env_k="$(sed -nE 's/^CACHE_TYPE_K=(.+)$/\1/p' "$ENV_FILE")"
env_v="$(sed -nE 's/^CACHE_TYPE_V=(.+)$/\1/p' "$ENV_FILE")"
pool="$(sed -nE 's/^CTX_SIZE=([0-9]+)$/\1/p' "$ENV_FILE")"
slots="$(sed -nE 's/^PARALLEL=([0-9]+)$/\1/p' "$ENV_FILE")"
rate_k="$(kv_bytes_per_token "$env_k")"
rate_v="$(kv_bytes_per_token "$env_v")"
kv="$(( pool * (rate_k + rate_v) / 1048576 ))"
total="$(( kv + 4749 + 51 * slots ))"
# The shipped arm has to leave real room: the projector's worst case is
# charged AFTER the engine's own fit check, so a projection that lands on
# the ceiling is a crash waiting for the first image request.
[ "$total" -le $(( 24123 - 1024 )) ]
# And the gate must be able to REFUSE: the same exact KV at the pool this
# shape left behind is 24,576 MiB of cache alone — a boot that dies before
# /health ever answers. Proving the gate bites is what makes passing one
# meaningful.
local over
over="$(( 786432 * (rate_k + rate_v) / 1048576 + 4749 + 51 * 3 ))"
[ "$over" -gt 24123 ]
}
@test "log verbosity follows the env file" {
serve_argv
[ "$status" -eq 0 ]
@@ -233,10 +281,11 @@ run_stage() {
# Frank, 2026-10-18: this file DOES carry the MTP head (the boot arms prove
# it loads and drafts, acceptance 0.798/0.617 per position, mean accepted
# length 2.41) and it is still off, because the draft context bills its own
# f16 KV over the WHOLE pool — 4,096 B/position × 786,432 = 3,072 MiB plus
# buffers — and at --ubatch-size 2048 that squeeze halves decode: 167.2 →
# 109.2 t/s. The MTP arm is SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024 (213.5
# t/s), documented in frank-zealot.env. The flags move as a set:
# f16 KV over the WHOLE pool — 4,096 B/position × 524,288 = 2,048 MiB plus
# buffers — on a trunk that already projects 21,234 MiB with exact KV. The
# MTP arms in frank-zealot.env are all q8_0-era measurements; the doors back
# to the drafter are one window per conversation, or KV back to q8_0 with
# UBATCH_SIZE=1024 (213.5 t/s). The flags move as a set:
# SPEC_TYPE=none strips --spec-type, --spec-draft-n-max AND
# --spec-draft-model together, so a no-spec boot cannot carry half a spec
# pair (the ROCmFPX-era baseline arm segfaulted at load doing that).
@@ -276,7 +325,7 @@ run_stage() {
# The Bonsai-2 text file carries no vision tensors — the publisher's Q8_0
# projector argument IS the vision capability: no flag, no eyes. Q8_0 (not
# BF16) is the publisher's own requirement for concurrent image requests,
# which is what --parallel 3 means in practice.
# which is what --parallel 2 means in practice.
serve_argv
[ "$status" -eq 0 ]
[ "$(argv_value --mmproj)" = "$MODEL_DIR/$MM_STUB" ]