feat(frank-zealot): Bonsai-2-9B PQ2_0 with the MTP head at 3 x 262,144

Swap the 3090 endpoint from Ternary-Bonsai-2-27B PQ2_0 (2 x 200,704,
fork build 10754) to Ternary-Bonsai-2-9B PQ2_0 with the MTP head IN the
file, three conversations at the trained 262,144 window
(--ctx-size 786432 --parallel 3), on vendor fork build 10777 -- the
minimum build that pairs MTP with a quantized V cache. q8_0 K + q8_0 V
is the only KV that fits: 13,056 MiB against a 26,112 MiB f16 pair.
Settled boot 17,876 used / 6,249 free.

MTP is armed and then DECLINED by measurement: the draft context bills
its own f16 KV over the whole pool (3,072 MiB) and at UBATCH_SIZE=2048
that halves decode (167.2 -> 109.2 t/s). The documented MTP arm
(SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024, 213.5 t/s) is recorded in the
env header rather than left to rediscovery.

Two bugs the trial caught: ${SPEC_PRESENCE_PENALTY:=0.0} was a
default-assignment, so the env value always won and the no-presence-penalty
MTP arm was unreachable (now ${VAR=0.0}); and the Q8_0 mmproj is the
publisher's requirement for concurrent image requests, which matters at
--parallel 3.

contextWindow is now 262144 -- the widest in the fleet -- so :7771 is the
compaction hub. 27B runbook re-banned RETIRED with its bench-window
section closed; indexes, homepage card, OpenWebUI doc and the worklog
re-keyed.
This commit is contained in:
Frank committed 2026-10-18 13:15:18 -07:00
1 parent 2b127163c8
commit 466fd477d8
14 files changed
+1001 -314

No files matched your search

+132 -70
View File
@@ -26,8 +26,8 @@ merge absorbed the former `vllmScripts` repo, history preserved).
| Target | Service | Runtime | Model | Port | | Target | Service | Runtime | Model | Port |
|--------|---------|---------|-------|------| |--------|---------|---------|-------|------|
| 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 | | 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 on 2026-10-18 so BOTH slots can hold a maximum-length request at once — the parity with frank-zealot's 401,408 pool on total in-flight context; ~10.8 GiB reserved 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`; every pool position is page cache the n-gram table loses), 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-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 on 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; ~10.8 GiB reserved 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 **10754**, commit `2459f68b5`; root systemd unit, relinked — the ONLY loader for this payload) | `prism-ml/Ternary-Bonsai-2-27B-gguf` **PQ2_0** (7,206,168,928 B, sha256 `3907dc16…`; `qwen35` hybrid: **64 blocks, 16 full-attention + 48 Gated DeltaNet**, 4 KV heads × 256 dim, vocab 248,320, native 262,144, **no MTP head in the file**; `general.file_type 141` = custom tensor type **142** + `prism.hadamard.*` dequant metadata, which is what locks the runtime) + the publisher's own **Q8_0 mmproj** (629,246,976 B; Qwen3-VL tower, 27 blocks, 768 px, `qwen3vl_merger`) for **vision**, on the RTX 3090, **TWO 200,704-token slots** (`--ctx-size 401408 --parallel 2`), **q8_0 K + q8_0 V** under flash attention (34 KiB/token over the 16 full layers = 13,328 MiB pool; **f16 KV needs 25,088 MiB and cannot boot this card**), batch 4096 / **ubatch 2048** (measured CUDA optimum), `--log-verbosity 4`, **MTP OFF** (no head in the file). Boot 22,664 used / 1,461 free; 76.6 t/s single-stream decode, 691.7 t/s prefill at 166k. Cutover 2026-10-17 from Qwen3.5-9B-Uncensored Q6_K (same shape; payload + master CUDA build on disk as rollback). | | 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**; +452 MiB of recurrent state for the three DeltaNet cells), 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. Boot 17,876 used / 6,249 free; 167.2 t/s single-stream decode, 280.2 t/s aggregate across three concurrent streams, 2,877 t/s prefill at 106,418. 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 Retired endpoints (`frank-npu`, and every prior runtime) are in
the [runbook index](#runbook-index). The full swap history lives in the [runbook index](#runbook-index). The full swap history lives in
@@ -319,12 +319,14 @@ image]`) — `frank-dragoon` returned to vision with the flash-server v2 swap
27-layer Qwen-VL tower — patching, pad-token expansion and 3-part rotary 27-layer Qwen-VL tower — patching, pad-token expansion and 3-part rotary
are the engine core; `--mm-max-patches` available), are the engine core; `--mm-max-patches` available),
and `frank-zealot` through and `frank-zealot` through
the publisher's own BF16 mmproj passed as `--mmproj` — the model is natively the publisher's own **Q8_0** mmproj passed as `--mmproj` — Q8_0 is the
multimodal but the Q6_K GGUF carries no vision tensors (same arrangement the publisher's requirement for concurrent image requests (the card states the
Gemma4 era used). `frank-zealot`'s `contextWindow` is BF16 tower can crash `llama-server` when several image requests run at once,
**200704** — the PER-SLOT window (`--ctx-size 401408 --parallel 2` since the and this endpoint runs three slots). `frank-zealot`'s `contextWindow` is
2026-10-07 Qwen3.5-9B swap), not the pool and not the model's native 262144: **262144** — the PER-SLOT window (`--ctx-size 786432 --parallel 3` since the
the harness must compact before llama.cpp answers a longer prompt with a 400. 2026-10-18 Bonsai-2-**9B** swap), which since that swap 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 Note also
that that
the reasoning channel differs by runtime: llama.cpp AND SGLang (qwen3 the reasoning channel differs by runtime: llama.cpp AND SGLang (qwen3
@@ -460,71 +462,113 @@ digest all remain on disk; the ROCmFPX door behind that
### frank-zealot — llama.cpp (PrismML fork) on the RTX 3090 (Dankclaw, ACTIVE) ### frank-zealot — llama.cpp (PrismML fork) on the RTX 3090 (Dankclaw, ACTIVE)
**The PrismML fork build of llama.cpp** (vendor release, `--version` = **The PrismML fork build of llama.cpp** (vendor release, `--version` =
`0.2.0-dev (build 10754, commit 2459f68b5)`; `0.2.0-dev (build 10777, commit e3de71522)`;
`/home/dank/GitPackages/llama-prism/llama-server`) as a **root systemd unit** `/home/dank/GitPackages/llama-prism-b10777/llama-server`) as a **root systemd
(`scripts/frank-zealot.service`, `relink_unit`ed into unit** (`scripts/frank-zealot.service`, `relink_unit`ed into
`/etc/systemd/system/`), serving `prism-ml/Ternary-Bonsai-2-27B-gguf` at `/etc/systemd/system/`), serving `prism-ml/Ternary-Bonsai-2-9B-gguf` at
**PQ2_0** (7,206,168,928 B, sha256 `3907dc16…` = the Hub LFS oid) **plus the **PQ2_0 with the MTP head IN the file** (2,675,410,720 B, Hub-LFS sha256
publisher's own Q8_0 mmproj** (629,246,976 B), both `c79fcdfa…` verified at fetch) **plus the publisher's own Q8_0 mmproj**
`/home/dank/models/Bonsai2-PQ2_0/`, on port 7771. Cutover 2026-10-17 from the (624,229,664 B, sha256 `e0cb6f69…`), both `/home/dank/models/Bonsai2-9B-MTP/`,
Qwen3.5-9B-Uncensored Q6_K era (same shape). on port 7771. Cutover 2026-10-18 from the Bonsai-2-**27B** PQ2_0 era
(2 × 200,704, same card, same fork family).
**The runtime is part of the payload identity.** The file is **The runtime is part of the payload identity — twice over.** The file is
`general.file_type 141` (PQ2_0, 2.13 bpw, group 128) with **402 tensors of the `general.file_type 141` — **202 of the 442 tensors carry the fork's PQ2_0 type**
custom GGML type 142** plus `prism.hadamard.*` metadata (normalized (the rest are f32 norms/scalars plus 8 q8_0 in the MTP block) — beside
Sylvester–Walsh transform, block 1024, 401 named weights, inverse on `prism.hadamard.*` metadata (normalized Sylvester–Walsh transform): stock
`token_embd`, `gdn_v_grouped`). Stock llama.cpp refuses type 142 at load, and a llama.cpp refuses the custom type at load, and a loader that
loader that ignored the Hadamard metadata would dequant wrongly — same lock class ignored the Hadamard metadata would dequant WRONG — same lock class as the
as the ROCmFPX era. Retargeting the payload without the fork ships an endpoint ROCmFPX era. On top of that lock, **the fork gates MTP beside a quantized V
that cannot load, so the seam pins both paths. 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.
Retargeting the payload without the fork ships an endpoint that cannot load,
so the seam pins both paths.
**Architecture: qwen35 hybrid** — read from the GGUF's own header: **64 **Architecture: qwen35 hybrid** — read from the GGUF's own header: `block_count`
blocks**, `full_attention_interval = 4` ⇒ **16 full-attention + 48 Gated **33 = 32 trunk layers + the `blk.32` MTP block** (the MTP block has its own
DeltaNet layers**, 24 attention heads / **4 KV heads** × 256 dim, embedding attention + FFN in q8_0, which is why the draft context bills real KV), and
5,120, ffn 17,408, `ssm` conv_kernel 4 / state_size 128 / group_count 16 / `qwen35.full_attention_interval = 4` ⇒ **8 full-attention + 24 Gated
time_step_rank 48 / inner_size 6,144, native 262,144, vocab 248,320. **KV DeltaNet**, 4 KV heads × 256 dim, native 262,144. **KV accounting:** the pool
accounting:** the pool scales with the 16 full layers — 16 × 4 × 256 = 16,384 scales with the 8 full layers — 8 × 4 × 256 = 8,192 elements/token per cache,
elements/token per cache, so **q8_0 K + q8_0 V bills 34 KiB/token** = 13,328 so **q8_0 K + q8_0 V bills 17,408 B/token ≈ 17 KiB** = **13,056.00 MiB** at the
MiB at the 401,408 pool (ledger-confirmed to the MiB), while **f16 KV needs 786,432 pool (ledger-exact) — exactly HALF the 27B's 34,816 B/token, which is
25,088 MiB and cannot boot on a 24,123 MiB card** (measured `cudaMalloc` OOM). the whole reason three native windows fit where the 27B afforded only two
The 48 DeltaNet layers bill a fixed recurrent state per SLOT — ~150 MiB each — shortened ones. **f16 K would bill 26,112 MiB and cannot boot on a 24,123 MiB
so pool arithmetic alone under-prices the shape. card.** The 24 DeltaNet layers bill a fixed recurrent state per SLOT —
`RS buffer size = 452.25 MiB (3 cells, 32 layers, 3 seqs)` ≈ 150 MiB each — so
pool arithmetic alone under-prices the shape.
**The shape (2026-10-17):** `--ctx-size 401408 --parallel 2` = TWO **The shape (2026-10-18):** `--ctx-size 786432 --parallel 3` = THREE
conversations of 200,704 (Frank's cap; the harness `contextWindow` quotes conversations of 262,144, every one at the model's TRAINED window (Frank: "if
the per-slot number), `--flash-attn on`, **q8_0 K + q8_0 V**, batch 4096 / you can possibly fit a third context, do it"), `--flash-attn on`, **q8_0 K +
**ubatch 2048** (the measured CUDA optimum carried from the MiMo/Swift A/Bs on q8_0 V**, batch 4096 / **ubatch 2048** (the measured CUDA optimum carried from
this card), `--log-verbosity 4`, `--mmproj` for vision, **speculative decoding the MiMo/Swift A/Bs on this card; it also sets the compute-buffer size, which
OFF** (`SPEC_TYPE=none`). Boot: **22,664 MiB used / 1,461 free**; the is what decides whether MTP fits), `--log-verbosity 4`, `--metrics`,
projection (21,439) excludes the projector's 873.10 MiB worst-case, which `--jinja`, `--mmproj` for vision, **speculative decoding OFF**
lands AFTER the fit check — read settled `nvidia-smi`, never the projection. (`SPEC_TYPE=none`). Boot: **17,876 MiB used / 6,249 free**; compute 1,409 MiB
Measured: 76.6 t/s single-stream decode (the 9B ran 94.4), 1,258 t/s prefill CUDA + 1,201 MiB CUDA_Host, projector 595 + 248 MiB. Measured on the same
at 35k, 691.7 t/s at 166k, 109.4 t/s aggregate across two concurrent streams. boot: **167.2 t/s** greedy decode, 158.9 t/s single-stream chat, **280.2 t/s
The **first request after boot is a warmup artifact** (2.6 t/s prefill / 37.7 aggregate across three concurrent streams**, **2,877 t/s prefill at 106,418**
t/s decode measured) — never report it. 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.
**Why MTP is off**: the PQ2_0 export carries no MTP head (upstream ships a **Why MTP is off even though the head is in the file.** The drafter is GOOD —
separate `…-CRACK-MTP-GGUF`, +451 MB, for that), and MTP is a property of the acceptance 0.798 / 0.617 per position, mean accepted length 2.41 — and the card
file, not of the flags. 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):
**Vision**: the publisher's own Q8_0 mmproj via `--mmproj` (Qwen3-VL tower: 27 | slots | spec | ubatch | used / free | decode |
blocks, 768 px, patch 16, merge 2, `qwen3vl_merger`, deepstack). Probe traps |---|---|---|---|---|
carry over: ≥256 px probe images or the model answers "video", and only the | 3 × 262,144 | off | 2048 | 17,876 / 6,249 | **167.2** |
OpenAI `image_url` content part reaches the model. **New trap:** thinking | 3 × 262,144 | on | 2048 | 22,706 / 1,419 | 109.2 |
tokens count against `max_tokens`, so a vision probe at 64 tokens returns | 3 × 262,144 | on | 1024 | 21,400 / 2,725 | **213.5** |
EMPTY `content` with `finish_reason: "length"` — budget ≥512 or the endpoint | 2 × 262,144 | on | 2048 | 17,178 / 6,947 | 213.2 |
looks blind while it is only mid-thought. | 1 × 262,144 | on | 2048 | 11,652 / 12,473 | 214.2 |
**Base alignment**: Bonsai-2 is the censored BASE lineage (the 9B it replaced **MTP is worth +28 % decode whenever the card has room**; the squeeze, not the
was uncensored). The CRACK re-export on disk drafter, is what halves it at three windows. The arm that WOULD run it at three
(`/home/dank/models/Bonsai2-CRACK/`, header census proves byte-identical windows is `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` (213.5 t/s single-stream,
geometry) is a one-line `MODEL_FILE` swap if a refusal ever surfaces. 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.
**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,
confirmed in the engine's own `/props`) and llama.cpp reads it itself.
**Presence penalty is the one recipe value with no GGUF metadata key**, so
`PRESENCE_PENALTY=1.0` must reach the argv; `""` is the documented opt-out, and
the serve script therefore uses `${PRESENCE_PENALTY=1.0}` (NOT `:=`, which
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
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
returns EMPTY `content` with `finish_reason: "length"` and the endpoint looks
blind while it is only mid-thought.
**Base alignment**: Bonsai-2 is the censored BASE lineage. Unlike the 27B, the
CRACK re-export on disk (`/home/dank/models/Bonsai2-CRACK/`) is byte-identical
geometry to the 27B it matched, NOT to this 9B file — if a refusal ever
surfaces there is no one-line `MODEL_FILE` swap; a 9B dealigned export would
have to be fetched first.
**Compaction hub**: `models.yml` routes `compactionModel` for **Compaction hub**: `models.yml` routes `compactionModel` for
`frank-archon`, `frank-dragoon` and `frank-zealot` (the 3090 endpoint) here — 200,704 is the `frank-archon`, `frank-dragoon` and `frank-zealot` (the 3090 endpoint) here —
only window in the fleet that fits a worst-case compaction input (~170k 262,144 (since the 2026-10-18 reslice) is the widest window in the fleet and
tokens). With `--parallel 2` a compaction request no longer serializes behind the only one that comfortably fits a worst-case compaction input (~170k
a live generation (the Gemma4 era's `--parallel 1` law). Retrieval through a tokens). With `--parallel 3` a compaction request no longer serializes behind a
live generation (the Gemma4 era's `--parallel 1` law). Retrieval through a
25,960-token prompt was verified exact after the swap. 25,960-token prompt was verified exact after the swap.
**Contract law learned at verbosity 4:** a raw `/completion` request prints **Contract law learned at verbosity 4:** a raw `/completion` request prints
@@ -533,11 +577,13 @@ model closes a bare prompt with `` — cosmetic
(`probe_request.cpp:789`); the `/v1/chat/completions` path parses (`probe_request.cpp:789`); the `/v1/chat/completions` path parses
`reasoning_content` correctly, which is the only path consumers use. Do not `reasoning_content` correctly, which is the only path consumers use. Do not
lower `VERBOSITY` to silence it: the KV/buffer ledger is what VRAM forensics lower `VERBOSITY` to silence it: the KV/buffer ledger is what VRAM forensics
read. read. `--metrics` is required or the Prometheus scrape reads 501 = DOWN.
Runbook: Runbook:
`serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai2-pq20.md`. `serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai9-mtp.md`.
Rollback: revert the swap commit — the Qwen3.5-9B Q6_K payload Rollback: revert the swap commit — the 27B payload
(`/home/dank/models/Bonsai2-PQ2_0/`) with the 10754 fork tree
(`~/GitPackages/llama-prism/`), the Qwen3.5-9B Q6_K payload
(`/home/dank/models/Qwen35-9B-HauhauCS-Q6K/`) with the master CUDA build (`/home/dank/models/Qwen35-9B-HauhauCS-Q6K/`) with the master CUDA build
(`~/GitPackages/llama-master/build-cuda/`), the Gemma4 payload (`~/GitPackages/llama-master/build-cuda/`), the Gemma4 payload
(`/home/dank/models/Gemma4-26B-A4B/`), the MiMo Q6_K payload (`/home/dank/models/Gemma4-26B-A4B/`), the MiMo Q6_K payload
@@ -960,6 +1006,21 @@ linked runbooks.
trip moved the balance point FOUR layers (~4.3 GiB of headroom, 40:25 -> trip moved the balance point FOUR layers (~4.3 GiB of headroom, 40:25 ->
36:29), which is the practical size of the effect. See 36:29), which is the practical size of the effect. See
[[frank-medium-llamacpp-dflash2]]. [[frank-medium-llamacpp-dflash2]].
- **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
[[frank-zealot-llama-prism-bonsai9-mtp]].
- **`--ubatch-size` has an OPTIMUM on the 2x R9700 layer split, not a plateau — - **`--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 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 compute buffers land about TWICE as heavy on the card that holds the output
@@ -1185,15 +1246,16 @@ Active:
| Service | Runbook | | Service | Runbook |
|---------|---------| |---------|---------|
| frank-dragoon (flash-server 0.16.2, NATIVE v2 abliterated .hgn + MTP always-on, 2 slots over ONE 401,408 pool at ctx 200704, vision ON, Dankclaw) | `serverAppConfigs-docs/LLM/frank-dragoon-flash-server-v2-native.md` | | frank-dragoon (flash-server 0.16.2, NATIVE v2 abliterated .hgn + MTP always-on, 2 slots over ONE 401,408 pool at ctx 200704, vision ON, Dankclaw) | `serverAppConfigs-docs/LLM/frank-dragoon-flash-server-v2-native.md` |
| frank-zealot (llama.cpp PrismML fork, Bonsai-2-27B PQ2_0, 2×200,704 q8_0 KV MTP-off, vision ON, Dankclaw) | `serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai2-pq20.md` | | frank-zealot (llama.cpp PrismML fork build 10777, Bonsai-2-**9B** PQ2_0 **with the MTP head**, 3×262,144 q8_0 KV, MTP off by measurement, vision ON via Q8_0 mmproj, Dankclaw) | `serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai9-mtp.md` |
| frank-archon (radiance StillDeadcode, publisher Qwen3.8-Flash-Next iq4r .rad, 2×R9700 TP=2, Dankserver) | `serverAppConfigs-docs/LLM/frank-archon-radiance-qwen38-iq4r.md` | | frank-archon (radiance StillDeadcode, publisher Qwen3.8-Flash-Next iq4r .rad, 2×R9700 TP=2, Dankserver) | `serverAppConfigs-docs/LLM/frank-archon-radiance-qwen38-iq4r.md` |
| Dankclaw kernel posture (`amd_iommu=off`) | `serverAppConfigs-docs/LLM/dankclaw-iommu-translation-off.md` | | Dankclaw kernel posture (`iommu=pt` since 2026-10-18, was `amd_iommu=off`) | `serverAppConfigs-docs/LLM/dankclaw-iommu-translation-off.md` |
| Deployment flow + incidents | `serverAppConfigs-docs/LLM/deployment.md` | | Deployment flow + incidents | `serverAppConfigs-docs/LLM/deployment.md` |
Retired (rollback targets and history): Retired (rollback targets and history):
| Runtime | Host | Window | Runbook | | Runtime | Host | Window | Runbook |
|---------|------|--------|---------| |---------|------|--------|---------|
| llama.cpp PrismML fork build 10754, Ternary-Bonsai-2-**27B** PQ2_0, 2×200,704 q8_0 K+V, MTP off (no head in the file), vision ON (`frank-zealot`) | Dankclaw | 2026-10-17 → 10-18 (rollback target — payload + 10754 fork tree on disk; owns the 34,816 B/token 16-full-layer KV law, the 25,088 MiB f16 OOM wall and the two-boot VRAM pricing) | `frank-zealot-llama-prism-bonsai2-pq20.md` |
| radiance Swift1.5-Qwen3.8-Flash-Next GPTQ w4dn8, 3×200,000 fp8-KV slab 25600 (`frank-archon`) | Dankserver | 2026-10-08 → 10-15 (rollback target — `.rad` purged 2026-10-17, byte-exact re-download `frank8465/Swift-1.5-Qwen3.8-Flash-Next-GPTQ-W4A8-Radiance-AMD-R9700`; owns the slab-slope ladder, KV dtype law, KLD campaign, and every shape law the iq4r swap carried verbatim) | `frank-archon-radiance-swift15-w4dn8.md` | | radiance Swift1.5-Qwen3.8-Flash-Next GPTQ w4dn8, 3×200,000 fp8-KV slab 25600 (`frank-archon`) | Dankserver | 2026-10-08 → 10-15 (rollback target — `.rad` purged 2026-10-17, byte-exact re-download `frank8465/Swift-1.5-Qwen3.8-Flash-Next-GPTQ-W4A8-Radiance-AMD-R9700`; owns the slab-slope ladder, KV dtype law, KLD campaign, and every shape law the iq4r swap carried verbatim) | `frank-archon-radiance-swift15-w4dn8.md` |
| llama.cpp CUDA (master) Qwen3.5-9B-Uncensored Q6_K, 2×200,704 exact f16 KV MTP-off (`frank-zealot`) | Dankclaw | 2026-10-07 → 10-17 (rollback target — payload + master CUDA build on disk; owns the 32 KiB/token 8-full-layer KV law and the 2→3-slot reslice law) | `frank-zealot-llama-qwen35-9b-q6k-2x200704.md` | | llama.cpp CUDA (master) Qwen3.5-9B-Uncensored Q6_K, 2×200,704 exact f16 KV MTP-off (`frank-zealot`) | Dankclaw | 2026-10-07 → 10-17 (rollback target — payload + master CUDA build on disk; owns the 32 KiB/token 8-full-layer KV law and the 2→3-slot reslice law) | `frank-zealot-llama-qwen35-9b-q6k-2x200704.md` |
| llama.cpp CUDA Gemma4-26B-A4B QAT Q4_K_M, 1×200,704 f16-K/q8_0-V MTP-off (`frank-zealot`) | Dankclaw | 2026-10-07 → 10-07 (same-day swap; rollback target — payload on disk; owns the `attention_k_eq_v` pair-billing KV law and the gemma4-assistant drafter-cost boots) | `frank-zealot-llama-gemma4-q4km-200704.md` | | llama.cpp CUDA Gemma4-26B-A4B QAT Q4_K_M, 1×200,704 f16-K/q8_0-V MTP-off (`frank-zealot`) | Dankclaw | 2026-10-07 → 10-07 (same-day swap; rollback target — payload on disk; owns the `attention_k_eq_v` pair-billing KV law and the gemma4-assistant drafter-cost boots) | `frank-zealot-llama-gemma4-q4km-200704.md` |
+25 -17
View File
@@ -1225,24 +1225,31 @@ deploy_llm_dragoon() {
# deploy_llm_zealot — frank-zealot is llama.cpp (PrismML fork build) on the # deploy_llm_zealot — frank-zealot is llama.cpp (PrismML fork build) on the
# RTX 3090 as a ROOT unit: the runtime is a native binary tree under # RTX 3090 as a ROOT unit: the runtime is a native binary tree under
# ~/GitPackages/llama-prism (vendor release build 10754, commit 2459f68b5 — # ~/GitPackages/llama-prism-b10777 (vendor release prism-b10777-e3de715,
# the ONLY loader that registers the payload's custom tensor type 142 and # `--version` = 0.2.0-dev build 10777 commit e3de71522 — the ONLY loader class
# reads its prism.hadamard.* metadata), so there is no container image and no # that registers the payload's custom tensor type 142 and reads its
# generated unit to avoid. relink_unit points # prism.hadamard.* metadata, and since 2026-10-18 ALSO the minimum build for
# /etc/systemd/system/frank-zealot.service at the repo file and boot # this endpoint: the card gates quantized V alongside MTP on
# persistence is that unit's [Install] section. # `prism-b10776-d00e252` or newer, which the 27B-era 10754 tree predates), so
# there is no container image and no generated unit to avoid. relink_unit
# points /etc/systemd/system/frank-zealot.service at the repo file and boot
# persistence is that unit's [Install] section. The former 10754 tree
# (~/GitPackages/llama-prism) stays on disk as the rollback runtime.
# #
# It is a SEPARATE endpoint on port 7771; frank-dragoon keeps 7770. The 3090 is # It is a SEPARATE endpoint on port 7771; frank-dragoon keeps 7770. The 3090 is
# not shared: frank-dragoon's engine runs on the gfx1151 iGPU. # not shared: frank-dragoon's engine runs on the gfx1151 iGPU.
LLM_ZEALOT_MODEL_FILE="${LLM_ZEALOT_MODEL_FILE:-/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0.gguf}" LLM_ZEALOT_MODEL_FILE="${LLM_ZEALOT_MODEL_FILE:-/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf}"
LLM_ZEALOT_RUNTIME="${LLM_ZEALOT_RUNTIME:-/home/dank/GitPackages/llama-prism/llama-server}" LLM_ZEALOT_RUNTIME="${LLM_ZEALOT_RUNTIME:-/home/dank/GitPackages/llama-prism-b10777/llama-server}"
# The projector IS the vision capability here: the Bonsai-2 text file carries # The projector IS the vision capability here: the Bonsai-2 text file carries
# no vision tensors, so the publisher's own mmproj (Qwen3-VL tower, 27 blocks, # no vision tensors, so the publisher's own mmproj (Qwen3-VL tower, 27 blocks,
# 768 px, qwen3vl_merger, 629,246,976 B) is the whole image path — a deploy # 768 px, qwen3vl_merger, projection_dim 4096, 624,229,664 B) is the whole
# that starts without it silently ships a text-only endpoint. Checked # image path — a deploy that starts without it silently ships a text-only
# unconditionally, like frank-dragoon's. Its 873.10 MiB worst-case VRAM lands # endpoint. Checked unconditionally, like frank-dragoon's. Q8_0 is MANDATORY,
# AFTER the engine's fit check, so it is not inside the q8_0 budget. # not a preference: the card says the BF16 tower can crash llama-server when
LLM_ZEALOT_MMPROJ="${LLM_ZEALOT_MMPROJ:-/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf}" # several image requests run at once, and this endpoint runs --parallel 3. 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}"
deploy_llm_zealot() { deploy_llm_zealot() {
# Refuse rather than restart-loop: a unit whose runtime or payload is # Refuse rather than restart-loop: a unit whose runtime or payload is
@@ -1250,15 +1257,16 @@ deploy_llm_zealot() {
# endpoint dark with nothing in the log to say why. # endpoint dark with nothing in the log to say why.
if [[ ! -x "$LLM_ZEALOT_RUNTIME" ]]; then if [[ ! -x "$LLM_ZEALOT_RUNTIME" ]]; then
echo "ERROR: llama.cpp runtime absent at $LLM_ZEALOT_RUNTIME" >&2 echo "ERROR: llama.cpp runtime absent at $LLM_ZEALOT_RUNTIME" >&2
echo "frank-zealot: fetch the PrismML fork release build (build 10754," >&2 echo "frank-zealot: fetch the PrismML fork release build (prism-b10777-e3de715," >&2
echo "frank-zealot: commit 2459f68b5) into ~/GitPackages/llama-prism first —" >&2 echo "frank-zealot: linux-cuda-12.4 asset) into ~/GitPackages/llama-prism-b10777 first:" >&2
echo "frank-zealot: stock llama.cpp refuses the payload's custom tensor type 142." >&2 echo "frank-zealot: stock llama.cpp refuses the payload's custom tensor type 142, and" >&2
echo "frank-zealot: builds older than b10776 cannot pair MTP with a quantized V cache." >&2
return 1 return 1
fi fi
for f in "$LLM_ZEALOT_MODEL_FILE" "$LLM_ZEALOT_MMPROJ"; do for f in "$LLM_ZEALOT_MODEL_FILE" "$LLM_ZEALOT_MMPROJ"; do
if [[ ! -f "$f" ]]; then if [[ ! -f "$f" ]]; then
echo "ERROR: frank-zealot payload file absent at $f" >&2 echo "ERROR: frank-zealot payload file absent at $f" >&2
echo "frank-zealot: run scripts/download-frank-zealot-bonsai2.sh first;" >&2 echo "frank-zealot: run scripts/download-frank-zealot-bonsai9.sh first;" >&2
echo "frank-zealot: refusing to start a unit that cannot load." >&2 echo "frank-zealot: refusing to start a unit that cannot load." >&2
return 1 return 1
fi fi
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
# Download the frank-zealot payload for llama.cpp on the RTX 3090:
# prism-ml/Ternary-Bonsai-2-9B-gguf — the **PQ2_0 packing with the MTP draft
# head built in** (the card: "load it instead of the plain PQ2_0, not alongside
# it") plus the PUBLISHER's OWN **Q8_0** vision tower. Public repo — no auth
# header, and never a token on the command line (world-readable in
# /proc/<pid>/cmdline).
#
# THE Q8_0 mmproj IS MANDATORY, NOT A PREFERENCE. The card: with the current
# fork build "the BF16 mmproj can crash llama-server when several image
# requests run at once; Q8_0 is not affected and quality is nearly the same."
# frank-zealot runs --parallel > 1, i.e. exactly that concurrent-image shape.
#
# Fresh downloads, no resume (`curl -C -` once produced a 213 MB oversized file
# on a 31 GiB payload); -f/--fail-with-body so an HTTP error is not a success,
# then byte count and sha256 against the Hub's **LFS oid** (this repo IS LFS,
# unlike the HauhauCS Xet repo the 2026-10-07 era used, so the sha values below
# are the Hub's own — verified at fetch, not computed locally).
# Idempotent: complete files are skipped.
#
# Rollback payload stays on disk: /home/dank/models/Bonsai2-PQ2_0/ (the
# Ternary-Bonsai-2-27B PQ2_0 file + its mmproj, cut over 2026-10-17), with
# Bonsai2-CRACK/ (the uncensored 27B re-export) beside it.
#
# Usage:
# scripts/download-frank-zealot-bonsai9.sh [DEST_DIR] [--verify-only]
set -euo pipefail
DEST=""
VERIFY_ONLY=0
for arg in "$@"; do
case "$arg" in
--verify-only) VERIFY_ONLY=1 ;;
*) DEST="$arg" ;;
esac
done
DEST="${DEST:-/home/dank/models/Bonsai2-9B-MTP}"
# repo | hub-name | dest-name | bytes | sha256 (Hub LFS oid)
FILES=(
"prism-ml/Ternary-Bonsai-2-9B-gguf|Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf|Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf|2675410720|c79fcdfaf6b7bf47089b50dadcfd11f9ce7d3e96adf3bbefde7912e5ef69e22b"
"prism-ml/Ternary-Bonsai-2-9B-gguf|Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf|Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf|624229664|e0cb6f6988cea323ef798cd77d019bbb819af75582f034eaab0e7232314953db"
)
mkdir -p "$DEST"
status=0
for entry in "${FILES[@]}"; do
IFS='|' read -r repo hubname destname size sha <<<"$entry"
url="https://huggingface.co/${repo}/resolve/main/${hubname}"
f="$DEST/$destname"
have=0
if [[ -f "$f" ]] && [[ "$(stat -c%s "$f")" == "$size" ]]; then
if sha256sum -c --quiet <(printf '%s %s\n' "$sha" "$f"); then
have=1
fi
fi
if (( have )); then
echo "OK $destname"
continue
fi
if (( VERIFY_ONLY )); then
echo "MISSING $destname"
status=1
continue
fi
echo "FETCHING $destname (from $repo)"
tmp="$f.part"
curl -fsSL --fail-with-body -o "$tmp" "$url"
mv "$tmp" "$f"
if [[ "$(stat -c%s "$f")" != "$size" ]]; then
echo "SIZE FAIL $destname ($(stat -c%s "$f") != $size)"
status=1
continue
fi
if ! sha256sum -c --quiet <(printf '%s %s\n' "$sha" "$f"); then
echo "SHA FAIL $destname"
status=1
continue
fi
echo "VERIFIED $destname"
done
if (( status == 0 )); then
echo "DOWNLOAD_DONE $DEST"
else
echo "DOWNLOAD_FAILED $DEST" >&2
fi
exit "$status"
+104 -51
View File
@@ -1,29 +1,39 @@
# frank-zealot — llama.cpp (PrismML fork build, CUDA) serving # frank-zealot — llama.cpp (PrismML fork build 10777, CUDA) serving
# prism-ml/Ternary-Bonsai-2-27B-gguf at PQ2_0 on the RTX 3090, :7771. # prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 **with the MTP head in the file**
# on the RTX 3090, :7771.
# #
# 2026-10-17 (Frank): swap from Qwen3.5-9B-Uncensored Q6_K to the Bonsai-2-27B # 2026-10-18 (Frank): swap from the Bonsai-2-**27B** PQ2_0 to the 9B MTP export
# ternary checkpoint at the SAME shape Frank asked for — TWO 200,704-token # at THREE 262,144-token conversations (Frank: "if you can possibly fit a third
# conversations — with q8_0 K + q8_0 V (f16 is arithmetically impossible here, # context, do it"). The 9B bills HALF the KV of the 27B per token, which is what
# see the KV law), MTP off (the file carries no nextn/mtp tensors), verbosity 4. # 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.
# #
# THE RUNTIME IS A LOCK, NOT A PREFERENCE. The file is general.file_type 141 # THE RUNTIME IS A LOCK, NOT A PREFERENCE. The file is general.file_type 141
# with 402 tensors of the CUSTOM GGML type 142 (pq2_0) plus prism.hadamard.* # carrying custom GGML type 142 tensors plus prism.hadamard.* metadata
# metadata (normalized-Sylvester-Walsh transform, block 1024, 401 named # (normalized-Sylvester-Walsh transform); stock llama.cpp refuses type 142 at
# weights, inverse on token_embd, gdn_v_grouped). Stock llama.cpp refuses type # load and a loader that ignored the Hadamard metadata would dequant WRONG. On
# 142 at load; only the PrismML fork registers the type AND reads the Hadamard # top of that lock, MTP beside a quantized V cache needs fork build >= b10776,
# metadata. The vendor prebuilt build 10754 (commit 2459f68b5) is what every # so the vendor prebuilt moved from 10754 (commit 2459f68b5, the 27B era) to
# number in this file was measured on; sha256 12ae5baba033d8cd… # 10777 (commit e3de71522). The 10754 tree stays on disk as the rollback
LLAMA_BIN=/home/dank/GitPackages/llama-prism/llama-server # runtime for the 27B payload.
MODEL_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0.gguf LLAMA_BIN=/home/dank/GitPackages/llama-prism-b10777/llama-server
MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf # 2,675,410,720 B, Hub LFS sha256 c79fcdfaf6b7… (verified at fetch by
# The CRACK re-export (dealignai/Bonsai-2-27B-Ternary-CRACK-GGUF, uncensored, # scripts/download-frank-zealot-bonsai9.sh). This IS the MTP build: the draft
# already on disk at /home/dank/models/Bonsai2-CRACK/) is a ONE-LINE swap: the # head ships INSIDE the checkpoint, so MTP availability is a property of the
# header census proves byte-identical geometry (851 tensors, same # FILE, never of a flag. The plain PQ2_0 sibling could not run it at any flag
# {pq2_0:402, f32:353, bf16:96} histogram, 64 blocks, vocab 248,320, same # value; loading this file instead of that one is the only change that matters.
# 8,952-char chat template) and the official file's sha256 is the Hub LFS oid. MODEL_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf
# Bonsai-2 is the BASE (censored) Qwen3.8 family; if a compaction or subagent # 624,229,664 B, Hub LFS sha256 e0cb6f69… — Q8_0 IS MANDATORY, not a preference:
# request ever comes back with a refusal, point MODEL_FILE at the CRACK file # the publisher's card says the BF16 mmproj "can crash llama-server when several
# and restart — the VRAM ledger below does not move. # 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
# 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
# (/home/dank/models/Bonsai2-CRACK/) is byte-identical geometry to the 27B it
# matched, NOT to this 9B file — if a refusal ever surfaces there is no
# one-line swap here; the 9B dealigned export would have to be fetched first.
# #
# Served model id — the harness/family name. The SERVICE, unit, scripts and # Served model id — the harness/family name. The SERVICE, unit, scripts and
# this env file keep the frank-zealot name; only the alias on the wire is the # this env file keep the frank-zealot name; only the alias on the wire is the
@@ -32,40 +42,83 @@ MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.ggu
ALIAS=frank-zealot ALIAS=frank-zealot
BIND_ADDR=0.0.0.0 BIND_ADDR=0.0.0.0
LISTEN_PORT=7771 LISTEN_PORT=7771
# CTX_SIZE is the POOL llama.cpp divides across slots: 401,408 / 2 = 200,704 # CTX_SIZE is the POOL llama.cpp divides across slots: 786,432 / 3 = 262,144 per
# per conversation. MUST stay divisible by PARALLEL (the seam enforces it) and # conversation — each slot at the model's TRAINED window, which is the point of
# the harness contextWindow quotes the PER-SLOT number — a longer prompt is # the swap (the 27B era had to cut each conversation to 200,704 to fit two).
# answered with a 400, never truncated. The model's native window is 262,144; # MUST stay divisible by PARALLEL (the seam enforces it) and the harness
# 2 × 262,144 at q8_0 projects 25,758 MiB and does NOT fit this card (measured # contextWindow quotes the PER-SLOT number — a longer prompt is answered with a
# f16 analogue: 41,118). One 262,144 slot fits with 5,995 MiB free but # 400, never truncated. The ceiling cannot go past 3 x 262,144: that is the
# serializes compaction, so the pair stays. # native wall (no YaRN in this path) and the arithmetic agrees — a 4th slot
CTX_SIZE=401408 # would push each below the compaction contract.
PARALLEL=2 CTX_SIZE=786432
PARALLEL=3
BATCH_SIZE=4096 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
# second job here: it sets the compute-buffer size, and the compute buffers are
# what decide whether MTP fits (see the MTP law). 1024 is the documented MTP arm.
UBATCH_SIZE=2048 UBATCH_SIZE=2048
# KV law (from the GGUF header, then confirmed by the boot ledger): 16 # KV law (header + boot ledger, both confirmed): qwen35 hybrid, 32 layers with
# full-attention layers (full_attention_interval 4 over 64 blocks) × 4 KV heads # qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated DeltaNet;
# × 256 dim = 16,384 elements per token per cache. q8_0 bills 17,408 B per # 4 KV heads × 256 dim = 8,192 elements per token per cache. q8_0 bills 8,704 B
# cache ⇒ 34,816 B/token = 34 KiB, so the 401,408 pool is 13,328.00 MiB — the # per cache ⇒ 17,408 B/token = 17 KiB, so the 786,432 pool is 13,056.00 MiB —
# ledger said exactly that. f16 would bill 25,088 MiB and the boot DIES: # the ledger said exactly that. HALF the 27B's 34,816 B/token, which is the
# `cudaMalloc failed: out of memory` on the 25,088.00 MiB request, engine fit # whole reason three native windows fit. f16 would bill 26,112 MiB at this pool
# projection 33,198 vs 23,558 free. The 48 DeltaNet layers bill a fixed # (against a 24,123 MiB card) and dies at cudaMalloc; the 24 DeltaNet layers
# recurrent state per SLOT — ~150 MiB each (context 13,627 − KV 13,328 = 299 # bill a fixed recurrent state per SLOT instead of per token — the ledger's
# for two slots), which is why pool arithmetic alone under-prices the shape. # "RS buffer size = 452.25 MiB (3 cells, 32 layers, 3 seqs)" = 150.75 MiB a
# slot — which is why pool arithmetic alone under-prices the shape.
CACHE_TYPE_K=q8_0 CACHE_TYPE_K=q8_0
CACHE_TYPE_V=q8_0 CACHE_TYPE_V=q8_0
# Boot ledger (2026-10-17, this exact shape, nvidia-smi settled): # SPECULATIVE DECODING: OFF, MEASURED. The head is in the file and the drafter
# 22,664 MiB used / 1,461 MiB free of 24,123 # is GOOD (acceptance 0.798/0.617 per position, mean accepted length 2.41) — it
# fit projection 21,439 = 6,539 model + 13,627 context + 1,272 compute # still loses, because the draft context bills its OWN f16 KV over the WHOLE
# the mtmd projector's 873.10 MiB worst-case is NOT inside that projection — # pool: 4,096 B/position × 786,432 = 3,072.00 MiB, plus its compute buffers,
# it lands after the fit check, which is the whole difference between # 4,830 MiB all told. Booted arms, same card, same traffic (2026-10-18):
# "2.6 GiB free on paper" and 1.4 GiB free in reality. Watch it. # 3 × 262,144 spec OFF ub 2048 → 17,876 used / 6,249 free, decode 167.2 t/s
# Fallback rung if the margin is ever eaten (desktop, co-tenant): q4_0 KV # 3 × 262,144 spec ON ub 2048 → 22,706 used / 1,419 free, decode 109.2 t/s
# measured 16,392 used / 7,733 free at the same 2 × 200,704. # 3 × 262,144 spec ON ub 1024 → 21,400 used / 2,725 free, decode 213.5 t/s
# 1024 is the ubatch fallback (−616 MiB, measured on the 9B predecessor at # 2 × 262,144 spec ON ub 2048 → 17,178 used / 6,947 free, decode 213.2 t/s
# this card/arch; 2048 was the measured CUDA optimum, +4.6 % prefill). # 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.
# 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 SPEC_TYPE=none
SPEC_DRAFT_N_MAX=2
SPEC_DRAFT_N_MIN=0
SPEC_DRAFT_P_MIN=0
# The card's sampler recipe lives in the checkpoint (general.sampling.* = temp
# 1.0 / top_p 0.95 / top_k 20 / min_p 0 / repeat_penalty 1.0 — confirmed in the
# engine's own /props defaults) and llama.cpp reads it itself. Presence penalty
# is the ONE recipe value with no GGUF metadata key, so it must reach the argv.
# PRESENCE_PENALTY="" is the documented opt-out.
PRESENCE_PENALTY=1.0
NGL=99 NGL=99
# Boot ledger (2026-10-18, this exact shape, nvidia-smi settled):
# 17,876 MiB used / 6,249 MiB free of 24,123
# CUDA0 KV 13,056.00 MiB (q8_0 K + q8_0 V, 786,432 positions)
# CUDA0 RS 452.25 MiB (3 recurrent-state cells)
# compute 1,409 MiB CUDA + 1,201 MiB CUDA_Host, projector 595 + 248 MiB
# no clamp line, no cudaMalloc error
# Measured on the same boot: 167.2 t/s greedy decode (/completion, 512 tokens),
# 158.9 t/s single-stream chat, 280.2 t/s aggregate across THREE concurrent
# streams (93.8 each), 2,877 t/s prefill at 106,418 tokens. The 27B record it
# replaces: 76.6 decode, 109.4 aggregate across two streams, 691.7 prefill at
# 166k — beaten on every axis. The FIRST request after a boot is a warmup
# artifact (37.7 t/s class); never report it.
# 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).
# Log verbosity threshold (llama.cpp -lv). 4 = debug: KV layout, buffer sizes # 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 # and graph capture land in the journal, which is what the VRAM-fit forensics
# read. Levels above 4 flood the journal. # read. Levels above 4 flood the journal.
+8 -1
View File
@@ -1,5 +1,5 @@
[Unit] [Unit]
Description=llama.cpp (PrismML build 10754) Ternary-Bonsai-2-27B PQ2_0 GGUF (qwen35 hybrid, 2x200704 slots, q8_0 K+V, MTP off, mmproj vision) on the RTX 3090 (dankclaw, frank-zealot) 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)
After=network-online.target After=network-online.target
Wants=network-online.target Wants=network-online.target
@@ -12,6 +12,13 @@ Group=dank
LimitMEMLOCK=infinity LimitMEMLOCK=infinity
Environment="FRANK_ZEALOT_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-zealot.env" Environment="FRANK_ZEALOT_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-zealot.env"
ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-zealot.sh ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-zealot.sh
# llama.cpp can take longer than systemd's 90 s default to drain slots and
# release the card: a real stop on 2026-10-18 hit `State 'stop-sigterm' timed
# out. Killing.` and left the unit in `failed` (result 'timeout') even though
# the process had served health correctly. The engine is SIGKILL-safe (nothing
# on disk), but a unit that always ends `failed` is a false alarm for the next
# operator, so give the drain room.
TimeoutStopSec=180
Restart=on-failure Restart=on-failure
RestartSec=10 RestartSec=10
+98 -53
View File
@@ -1,30 +1,42 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Serve prism-ml/Ternary-Bonsai-2-27B (PQ2_0 ternary quant) as frank-zealot on # Serve prism-ml/Ternary-Bonsai-2-9B-gguf (PQ2_0 ternary quant WITH the MTP head
# Dankclaw's RTX 3090 via llama.cpp (PrismML vendor build 10754, CUDA sm_86). # in the file) as frank-zealot on Dankclaw's RTX 3090 via llama.cpp (PrismML
# vendor build 10777, CUDA sm_86).
# #
# 2026-10-17 (Frank): Bonsai-2 PQ2_0 swap at TWO 200,704-token conversations # 2026-10-18 (Frank): Bonsai-2 27B PQ2_0 → the 9B MTP export at THREE
# (--ctx-size 401408 --parallel 2) with q8_0 K + q8_0 V — f16 KV does not fit # 262,144-token conversations (--ctx-size 786432 --parallel 3), q8_0 K + q8_0 V.
# this geometry: the engine's own fit projected 33,198 MiB against 23,558 free # The 9B bills 17,408 B/token against the 27B's 34,816 (8 full-attention layers
# and cudaMalloc failed on the 25,088 MiB pool. MTP stays off: this GGUF # instead of 16), and that halved rate is what funds three NATIVE windows where
# carries no MTP tensors (verified from its tensor-name list). Verbosity 4 # the 27B could only afford two cut to 200,704. Verbosity 4 ships so the
# ships so the VRAM-fit forensics (KV layout, buffer sizes, graph capture) # VRAM-fit forensics (KV layout, buffer sizes, graph capture) land in the
# land in the journal. # journal.
# #
# Architecture: qwen35 — 27B, 64 blocks with full_attention_interval 4 (16 # Architecture (engine ledger, not the marketing page): qwen35 hybrid, 32
# full-attention layers + 48 Gated DeltaNet linear layers), 24 heads / 4 KV # layers with qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated
# heads × 256 dim, native 262,144, vocab 248,320. The KV pool scales with the # DeltaNet, 4 KV heads × 256 dim, native 262,144. The KV pool scales with the 8
# 16 full layers: 34 KiB/token at q8_0 K + q8_0 V (13,328 MiB at the 401,408 # full layers (13,056.00 MiB at the 786,432 pool, ledger-exact); the DeltaNet
# pool); the DeltaNet layers cost a fixed recurrent state per SLOT (~150 MiB — # layers cost a fixed recurrent state per SLOT — 452.25 MiB for 3 cells, ~151
# boot-ledger context minus KV). Boot ledger: 22,664 used / 1,461 free, with # MiB each. The runtime is a hard lock, not a preference: custom GGML type 142
# the Q8_0 projector's 873 MiB landing AFTER the engine's fit projection. # plus prism.hadamard.* metadata, which only the vendor build registers, and
# The runtime is a hard lock, not a preference: the file carries 402 tensors # MTP beside quantized V needs build ≥ b10776 — so the prebuilt moved 10754 →
# of custom GGML type 142 plus prism.hadamard.* metadata, which only the # 10777 with the payload. Vision comes from the publisher's Q8_0 projector (the
# vendor build registers — stock llama.cpp refuses it at load. # BF16 one can crash the server on concurrent images, and this endpoint runs
# Vision comes from the publisher's Q8_0 projector, so --mmproj is what gives # three slots), so --mmproj is what gives this endpoint eyes; MMPROJ_FILE="" is
# this endpoint eyes; MMPROJ_FILE="" is the documented text-only opt-out. # the documented text-only opt-out.
# #
# Sampling: llama.cpp takes it per request (the fleet harness sends the file's # Speculative decoding ships OFF, measured: the head loads and accepts well
# own template defaults from models.yml: thinking temp 1.0 / top_p 0.95 / top_k 20). # (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.
#
# 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 /
# repeat_penalty 1.0 — confirmed in the engine's own /props defaults) and
# llama.cpp reads it; --presence-penalty is the one recipe value with no
# metadata key, so this script passes it. A request may still override per call
# (the fleet harness sends the same values).
# #
# Tunables come from frank-zealot.env unless FRANK_ZEALOT_ENV_FILE overrides. # Tunables come from frank-zealot.env unless FRANK_ZEALOT_ENV_FILE overrides.
set -euo pipefail set -euo pipefail
@@ -39,45 +51,62 @@ if [[ -f "$ENV_FILE" ]]; then
fi fi
# --- defaults (used when the env file does not set them) ------------------- # --- defaults (used when the env file does not set them) -------------------
: "${LLAMA_BIN:=/home/dank/GitPackages/llama-prism/llama-server}" : "${LLAMA_BIN:=/home/dank/GitPackages/llama-prism-b10777/llama-server}"
: "${MODEL_FILE:=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0.gguf}" : "${MODEL_FILE:=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf}"
: "${ALIAS:=frank-zealot}" : "${ALIAS:=frank-zealot}"
: "${BIND_ADDR:=0.0.0.0}" : "${BIND_ADDR:=0.0.0.0}"
: "${LISTEN_PORT:=7771}" : "${LISTEN_PORT:=7771}"
# ctx-size is the POOL llama.cpp divides across slots: 401,408 / 2 = 200,704 # ctx-size is the POOL llama.cpp divides across slots: 786,432 / 3 = 262,144
# per conversation, inside the model's trained 262,144. The harness # per conversation — every slot at the model's trained window. The harness
# contextWindow MUST quote the PER-SLOT number — a longer prompt is answered # contextWindow MUST quote the PER-SLOT number — a longer prompt is answered
# with a 400, never truncated. # with a 400, never truncated.
: "${CTX_SIZE:=401408}" : "${CTX_SIZE:=786432}"
: "${PARALLEL:=2}" : "${PARALLEL:=3}"
# Compute-buffer geometry. Ubatch is a prefill knob (decode never moves). # 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 # 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 # at 8.9k depth, decode flat, 616 MiB). The logical batch is VRAM- and
# throughput-neutral at a fixed ubatch. # 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).
: "${BATCH_SIZE:=4096}" : "${BATCH_SIZE:=4096}"
: "${UBATCH_SIZE:=2048}" : "${UBATCH_SIZE:=2048}"
# Quantized KV: q8_0 K + q8_0 V — the only pair that funds 2 × 200,704 on this # Quantized KV: q8_0 K + q8_0 V — the only pair that funds 3 × 262,144 on this
# card. 34 KiB/token at this geometry (16 full-attention layers × 4 KV heads × # card. 17 KiB/token at this geometry (8 full-attention layers × 4 KV heads ×
# 256 dim); 13,328 MiB at the 401,408 pool. f16 measured OOM; q4_0 is the # 256 dim); 13,056.00 MiB at the 786,432 pool, ledger-exact. f16 would bill
# fallback rung (16,392 used / 7,733 free). Explicit either way so the contract # 26,112 MiB against a 24,123 MiB card and dies at cudaMalloc; q4_0 is the
# is visible in the argv, and --flash-attn stays required for quantized V # fallback rung. Explicit either way so the contract is visible in the argv,
# (no true fp8 KV dtype exists in this build). # and --flash-attn stays required for quantized V (no true fp8 KV dtype exists
# in this build).
: "${CACHE_TYPE_K:=q8_0}" : "${CACHE_TYPE_K:=q8_0}"
: "${CACHE_TYPE_V:=q8_0}" : "${CACHE_TYPE_V:=q8_0}"
# Speculative decoding is OFF: this PQ2_0 file carries NO nextn/mtp tensors, so # Speculative decoding defaults OFF, measured on this card and shape: the head
# there is no in-file head (the law: MTP is a property of the file, not of the # IS in the checkpoint (qwen35.nextn_predict_layers = 1 — MTP is a property of
# flags). SPEC_TYPE=none strips BOTH speculative flags (flags move as a set); # the FILE, so the plain PQ2_0 sibling could not run it at any flag value), it
# an MTP-bearing CRACK re-export exists upstream (ManniX-ITA, +451 MB) if # loads, and it accepts well (mean accepted length 2.41) — but the draft context
# decode ever needs it — that is a new payload with its own VRAM bill, never a # bills its own f16 KV over the WHOLE pool (4,096 B/position, 3,072 MiB here)
# flag flip on this file. # 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.
# 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}" : "${SPEC_TYPE:=none}"
: "${SPEC_DRAFT_N_MAX:=3}" : "${SPEC_DRAFT_N_MAX:=2}"
: "${SPEC_DRAFT_N_MIN:=0}"
: "${SPEC_DRAFT_P_MIN:=0}"
: "${SPEC_DRAFT_FILE:=}" : "${SPEC_DRAFT_FILE:=}"
# No colon on purpose, same convention as MMPROJ_FILE: an explicitly empty
# PRESENCE_PENALTY is the documented opt-out and must NOT be replaced by the
# default (a colon treats null as unset and would silently re-add the flag).
: "${PRESENCE_PENALTY=1.0}"
: "${NGL:=99}" : "${NGL:=99}"
# Log verbosity threshold (llama.cpp -lv). 3 is the engine default (info); # 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 # 4 adds debug detail (KV layout, buffer sizes, graph capture) which is what
# the VRAM-fit forensics need. Levels above 4 flood the journal. # the VRAM-fit forensics need — the q8_0 KV contract and the draft-KV bill are
: "${VERBOSITY:=3}" # 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 # /metrics needs the flag: llama.cpp returns 501 unless the server starts with
# --metrics, and Dankclaw's Prometheus scrapes every fleet endpoint's /metrics — # --metrics, and Dankclaw's Prometheus scrapes every fleet endpoint's /metrics —
# a 501 reads as DOWN, which is how the critical LlmEndpointDown alert ended up # a 501 reads as DOWN, which is how the critical LlmEndpointDown alert ended up
@@ -89,7 +118,7 @@ fi
: "${METRICS:=1}" : "${METRICS:=1}"
# Vision projector. No colon on purpose: an explicitly empty value means # Vision projector. No colon on purpose: an explicitly empty value means
# "text-only, on purpose" and must NOT be replaced by the default path. # "text-only, on purpose" and must NOT be replaced by the default path.
: "${MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf}" : "${MMPROJ_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf}"
# Fail loudly rather than half-loading: a missing file is a download that # Fail loudly rather than half-loading: a missing file is a download that
# has not finished, and Restart=always would turn it into a crash loop that # has not finished, and Restart=always would turn it into a crash loop that
@@ -109,15 +138,31 @@ if [[ "$METRICS" = "1" ]]; then
EXTRA_ARGS+=(--metrics) EXTRA_ARGS+=(--metrics)
fi fi
# Speculative flags move as a set: they reach the argv ONLY when a drafter # Speculative flags move as a SET: they reach the argv ONLY when a drafter is
# is named. SPEC_TYPE=none (the shipped contract) strips both — a no-spec # named, and all of them together — a no-spec baseline must not carry half a
# baseline must not carry half a spec pair. # spec pair. n-max 2 / n-min 0 / p-min 0 is the publisher's recipe ("Draft depth
# 2 works best"); the MTP head is IN the checkpoint (blk.32.nextn.*,
# qwen35.nextn_predict_layers = 1), so there is no --spec-draft-model here and
# the draft context rides the MAIN model's weights.
if [[ -n "$SPEC_TYPE" && "$SPEC_TYPE" != "none" ]]; then if [[ -n "$SPEC_TYPE" && "$SPEC_TYPE" != "none" ]]; then
EXTRA_ARGS+=(--spec-type "$SPEC_TYPE" --spec-draft-n-max "$SPEC_DRAFT_N_MAX") EXTRA_ARGS+=(--spec-type "$SPEC_TYPE"
if [[ -n "$SPEC_DRAFT_FILE" ]]; then --spec-draft-n-max "$SPEC_DRAFT_N_MAX"
[[ -e "$SPEC_DRAFT_FILE" ]] || { echo "frank-zealot: missing $SPEC_DRAFT_FILE" >&2; exit 1; } --spec-draft-n-min "$SPEC_DRAFT_N_MIN"
EXTRA_ARGS+=(--spec-draft-model "$SPEC_DRAFT_FILE") --spec-draft-p-min "$SPEC_DRAFT_P_MIN")
if [[ -n "$SPEC_DRAFT_FILE" ]]; then
[[ -e "$SPEC_DRAFT_FILE" ]] || { echo "frank-zealot: missing $SPEC_DRAFT_FILE" >&2; exit 1; }
EXTRA_ARGS+=(--spec-draft-model "$SPEC_DRAFT_FILE")
fi
fi fi
# The card's sampler recipe lives partly in the checkpoint: general.sampling.*
# (temp 1.0 / top_p 0.95 / top_k 20 / min_p 0 / repeat_penalty 1.0) is read by
# llama.cpp itself, and --repeat-last-n already defaults to 64. Presence penalty
# is the ONE value with no GGUF metadata key — the card says so explicitly — so
# it is the one knob that must reach the argv or the recipe is silently
# incomplete. PRESENCE_PENALTY="" is the documented opt-out.
if [[ -n "$PRESENCE_PENALTY" ]]; then
EXTRA_ARGS+=(--presence-penalty "$PRESENCE_PENALTY")
fi fi
# The UVM device node is created lazily by nvidia-modprobe on the first CUDA # The UVM device node is created lazily by nvidia-modprobe on the first CUDA
+55 -26
View File
@@ -21,10 +21,10 @@ https://github.com/open-webui/open-webui
| Item | Value | | Item | Value |
|------|-------| |------|-------|
| Image | `ghcr.io/open-webui/open-webui:main` (verified from the live quadlet unit) | | Image | `ghcr.io/open-webui/open-webui:main` (verified from the live quadlet unit) |
| Upstream latest | 0.11.4 (2026-09-21) — security/access-control advisory; `:main` is the rolling image, not a stable pin. Imagegen endpoint is `POST /api/v1/images/generations` (the `comfyui` engine is still upstream). | | Deployed build | `0.11.4` — `GET http://127.0.0.1:3005/api/version` → `{"version":"0.11.4"}` (2026-10-18). `:main` is the rolling image, not a stable pin, so the running build lags upstream: releases `0.12.0` (2026-10-10) through `0.12.3` (2026-10-16) exist and are not deployed (https://github.com/open-webui/open-webui/releases). The 2026-09-21 `0.11.4` security/access-control advisory is what pinned this era of the page. Imagegen endpoint is `POST /api/v1/images/generations` (the `comfyui` engine is still upstream). |
| Port | 3005 (host) → 8080 (container). Port 3000 is Gitea's HTTP, so it is not usable. | | Port | 3005 (host) → 8080 (container). Port 3000 is Gitea's HTTP, so it is not usable. |
| Data volume | `/home/dank/Containers/open-webui/data` → `/app/backend/data` | | Data volume | `/home/dank/Containers/open-webui/data` → `/app/backend/data` |
| Backends | Picker exposes exactly one model — `frank-zealot` (host.containers.internal:7771). The `frank-dragoon` and `frank-archon` connections remain configured in the DB, disabled (2026-10-07) — see [The LLM backends](#the-llm-backends) | | Backends | The picker's content is `webui.db` state, not repo state: as recorded on 2026-10-07 it exposes **one** model, `frank-zealot`, with `frank-dragoon`/`frank-archon` configured but disabled — *unverified 2026-10-18* (the DB is the authority and is not readable from the repo). What the repo does fix is the container's own OpenAI target: `OPENAI_API_BASE_URL=http://192.168.0.143:7770` (`dankclaw/open-webui/quadlet/open-webui.container:32`, same value in the live container env) — see [The LLM backends](#the-llm-backends) |
| Network | Pinned bridge `systemd-open-webui` (`10.89.3.0/24`, gateway `10.89.3.1`) — see [History](#history) | | Network | Pinned bridge `systemd-open-webui` (`10.89.3.0/24`, gateway `10.89.3.1`) — see [History](#history) |
| Persistence | Quadlet unit `open-webui.service`; `[Install] WantedBy=default.target` | | Persistence | Quadlet unit `open-webui.service`; `[Install] WantedBy=default.target` |
| Deploy | `scripts/deploy-open-webui-quadlet.sh dankclaw` | | Deploy | `scripts/deploy-open-webui-quadlet.sh dankclaw` |
@@ -34,19 +34,30 @@ https://github.com/open-webui/open-webui
The picker is built from the `model` table in `webui.db` (one row per model) The picker is built from the `model` table in `webui.db` (one row per model)
plus the `model_ids` of each **enabled** OpenAI connection, NOT from live plus the `model_ids` of each **enabled** OpenAI connection, NOT from live
connection discovery — so the picker shows exactly what is seeded there. connection discovery — so the picker shows exactly what is seeded there.
As of 2026-10-07 the picker exposes **exactly one model: `frank-zealot`**. As of 2026-10-07 the picker exposes **exactly one model: `frank-zealot`** *(unverified
2026-10-18: `webui.db` is the authority; the fleet itself serves all three ids
`frank-dragoon`/`frank-zealot`/`frank-archon`, per the cards in
`shared/homepage/services.json`)*.
| Connection (`openai.api_configs` idx) | URL | Enabled | `model_ids` | Runtime | Host | | Connection (`openai.api_configs` idx) | URL as recorded 2026-10-09 | Enabled | `model_ids` | Runtime | Host |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| 0 | `http://host.containers.internal:7770/v1` | no (2026-10-07) | `frank-dragoon` | halogen flash-server (Strix Halo iGPU) | Dankclaw | | 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 | | 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, 2×200,704) | Dankclaw | | 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 |
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
picker row below was re-keyed to it (see the next paragraph).
The `model` table carries exactly one decoration row, `frank-zealot` The `model` table carries exactly one decoration row, `frank-zealot`
(context length 200,704 = the per-slot window; description since 2026-10-17: (context length 262,144 = the per-slot window since 2026-10-18; description
Ternary-Bonsai-2-27B PQ2_0 + Q8_0 vision mmproj — the row's `description` is re-keyed the same day to Ternary-Bonsai-2-9B PQ2_0 + MTP + Q8_0 vision mmproj —
live DB state, so a payload swap must re-key it in the database too, not only the row's `description` is live DB state, so a payload swap must re-key it in
in the repo). the database too, not only in the repo).
A third gate sits *after* the table, and it is the one that bites family A third gate sits *after* the table, and it is the one that bites family
accounts (2026-10-07): a model row is only visible to a non-admin user when accounts (2026-10-07): a model row is only visible to a non-admin user when
@@ -66,15 +77,22 @@ Two gotchas make the wiring non-obvious:
OpenAI `{"data":[{"id":...}]}` shape, so connection discovery silently misses OpenAI `{"data":[{"id":...}]}` shape, so connection discovery silently misses
the llama.cpp endpoints (frank-dragoon, frank-zealot). The fix is to set the llama.cpp endpoints (frank-dragoon, frank-zealot). The fix is to set
`model_ids` in each connection's `api_config` — when non-empty, OpenWebUI `model_ids` in each connection's `api_config` — when non-empty, OpenWebUI
builds the model list directly and never fetches `/v1/models`. frank-medium builds the model list directly and never fetches `/v1/models`. The old
(vLLM) serves the OpenAI shape and would discover fine, but uses `model_ids` `frank-medium` (vLLM) endpoint — retired, no longer in the fleet — served the
too for consistency. OpenAI shape and would have discovered fine, but used `model_ids` too.
- **The host's own services are reached via `host.containers.internal`.** The - **The host's own services are reached via the host LAN IP, not
container sits on a netavark bridge; it can hairpin to a *different* host's `host.containers.internal`.** The container sits on the pinned kernel bridge
LAN IP (dankserver `192.168.0.235`) but not to its *own* host's non-published `systemd-open-webui` (10.89.3.0/24, gateway 10.89.3.1, container IP 10.89.3.2 —
ports via the host LAN IP (`192.168.0.143:7770/7771` is refused). `podman inspect open-webui` 2026-10-18), and `host.containers.internal`
`host.containers.internal` (169.254.1.2) resolves to the host on the bridge (169.254.1.2) is a **slirp4netns/pasta-only name that does not resolve on this
and reaches both llama.cpp services. bridge** (`dankclaw/open-webui/quadlet/open-webui.network:15-19`). The deployed
unit therefore points at the host LAN IP: `OPENAI_API_BASE_URL=http://192.168.0.143:7770`
and `SEARXNG_QUERY_URL=http://192.168.0.143:8081/search`
(`open-webui.container:32,78`, both present in the live container env, 2026-10-18).
Those ports are published on all interfaces, so the LAN IP answers:
`ss -ltn` shows `*:7770` and `0.0.0.0:7771`, and `curl http://192.168.0.143:7771/health`
→ 200 (2026-10-18). The Dankserver backend is a different host and is reached
through its LAN IP `192.168.0.235:7770` either way.
The Ollama connection is disabled (`ollama.enable=false`). The Ollama connection is disabled (`ollama.enable=false`).
@@ -140,8 +158,14 @@ role (`ui.default_user_role=pending`), which cannot chat. Verified
`GET /api/v1/models/list`, `GET /api/v1/models` and `GET /api/v1/models/list`, `GET /api/v1/models` and
`POST /api/chat/completions` all return **401** with no key (also with a `POST /api/chat/completions` all return **401** with no key (also with a
bogus bearer token); only `/api/config`, `/api/version` and `/health` bogus bearer token); only `/api/config`, `/api/version` and `/health`
answer publicly, and none of them expose the model list. An external `POST /api/chat/completions` all return **401** with no key (also with a bogus bearer
program needs an account + API key — there is no keyless access. token); only `/api/config`, `/api/version` and `/health` answer publicly, and none of
them expose the model list. An external program needs an account + API key — there is
no keyless access. Re-verified 2026-10-18 from loopback: `/health` → `{"status":true}`,
`/api/version` → 0.11.4, `/api/v1/models/list`, `/api/v1/models` and
`POST /api/chat/completions` → 401; `/api/config` additionally publishes
`features.enable_login_form=false`, `features.enable_signup=false` and
`oauth.providers.oidc="authentik"` — the SSO-only posture, visible from outside.
## Web search ## Web search
@@ -149,8 +173,11 @@ program needs an account + API key — there is no keyless access.
Chat web search is served by [[SearXNG]] on this host, wired in the quadlet Chat web search is served by [[SearXNG]] on this host, wired in the quadlet
(`ENABLE_WEB_SEARCH`, `WEB_SEARCH_ENGINE=searxng`, `SEARXNG_QUERY_URL`). (`ENABLE_WEB_SEARCH`, `WEB_SEARCH_ENGINE=searxng`, `SEARXNG_QUERY_URL`).
- **Access path:** the backend calls `http://192.168.0.143:8081/search?format=json` - **Access path:** the quadlet sets `SEARXNG_QUERY_URL=http://192.168.0.143:8081/search`
directly — gluetun's published port, NOT the `search.franksdank.cloud` vhost. (`dankclaw/open-webui/quadlet/open-webui.container:78`, live in the container env) —
gluetun's published port, NOT the `search.franksdank.cloud` vhost. The JSON format
parameter is appended by OpenWebUI itself *(unverified 2026-10-18: OpenWebUI-side
behaviour; the env value carries no `format=json`)*.
The Anubis→Authentik guard chain is a browser mechanism (proof-of-work + The Anubis→Authentik guard chain is a browser mechanism (proof-of-work +
session flow); a plain HTTP client cannot solve it, and the internal port session flow); a plain HTTP client cannot solve it, and the internal port
bypasses Caddy entirely. bypasses Caddy entirely.
@@ -247,9 +274,11 @@ chat LLMs only), so ComfyUI needs its own image-generation connection
configured in OpenWebUI. ComfyUI itself runs GPU-enabled on Dankclaw and configured in OpenWebUI. ComfyUI itself runs GPU-enabled on Dankclaw and
serves `:8188`. serves `:8188`.
Endpoint: `POST /api/images/generations` (image `0.11.x`: upstream moved the Endpoint: `POST /api/v1/images/generations` — measured on the running build
route to `POST /api/v1/images/generations`; the `comfyui` engine is still (`0.11.4`): the legacy `POST /api/images/generations` answers **405** while the `/v1`
upstream) with `Authorization: Bearer <token>` and body route answers **401** without a token, i.e. only the `/v1` path exists (2026-10-18).
The `comfyui` engine is still upstream. Call it with `Authorization: Bearer <token>`
and body
`{"model":"imagegen","prompt":"…","n":1,"size":"1024x1024"}`; the response `{"model":"imagegen","prompt":"…","n":1,"size":"1024x1024"}`; the response
carries base64 image data or a URL. carries base64 image data or a URL.
@@ -267,7 +296,7 @@ on large sizes ⇒ check ComfyUI resources; 401 ⇒ the token is bad.
- The image tracks upstream `main`; pin a digest in the quadlet unit if reproducible rollback matters more than tracking. (The old "CPU-only host" framing is stale: this APU runs the gfx1151 ROCm build — see [[ComfyUI]].) - The image tracks upstream `main`; pin a digest in the quadlet unit if reproducible rollback matters more than tracking. (The old "CPU-only host" framing is stale: this APU runs the gfx1151 ROCm build — see [[ComfyUI]].)
- The default admin account is created on first login; secure it immediately. - The default admin account is created on first login; secure it immediately.
- If the backend is loading (3–8 minute model load), OpenWebUI shows connection errors until the model is ready. - If the backend is loading (3–8 minute model load), OpenWebUI shows connection errors until the model is ready.
- The on-host LLM services (Dankclaw `:7770`, `:7771`) are reached via `host.containers.internal`, NOT the host LAN IP: the container's netavark bridge cannot hairpin to its own host's non-published ports via `192.168.0.143` (connection refused), and the bridge gateway (`10.89.3.1`) lives in the rootless netns while llama-server runs in the host netns, so it refuses too. `host.containers.internal` (169.254.1.2) resolves to the host on the bridge and works — verified end-to-end for both endpoints. The Dankserver backend is a different host, reached via its LAN IP `192.168.0.235:7770`. - The on-host LLM services (Dankclaw `:7770`, `:7771`) are reached via the **host LAN IP `192.168.0.143`**, which is exactly what the deployed unit does (`open-webui.container:32`). The older "use `host.containers.internal`" advice is retired for this container: it is a slirp4netns/pasta-only name and the unit has been on the pinned bridge `systemd-open-webui` since 2026-08-22, where that name does not resolve (`open-webui.network:15-19`). Both endpoints are published on all interfaces today (`ss -ltn` → `*:7770`, `0.0.0.0:7771`; `curl http://192.168.0.143:7771/health` → 200, 2026-10-18). The Dankserver backend is a different host, reached via its LAN IP `192.168.0.235:7770`.
## History ## History
@@ -1,7 +1,7 @@
--- ---
type: reference type: reference
title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork), Bonsai-2-27B PQ2_0, 2 x 200704, q8_0 KV (ACTIVE)" title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork 10754), Bonsai-2-27B PQ2_0, 2 x 200704, q8_0 KV (RETIRED, rollback target)"
description: "The ACTIVE frank-zealot runbook (since 2026-10-17) — the PrismML fork build of llama.cpp serving prism-ml/Ternary-Bonsai-2-27B-gguf at PQ2_0 on the RTX 3090: two conversations of 200,704 tokens with q8_0 K + q8_0 V (f16 KV does not fit this card at this window), vision through the publisher's Q8_0 Qwen3-VL projector, MTP impossible (no tensors in the file), and the measured four-arm VRAM census that priced the shape." description: "The frank-zealot runbook of 2026-10-17 → 2026-10-18 (RETIRED — rollback target) — the PrismML fork build 10754 of llama.cpp serving prism-ml/Ternary-Bonsai-2-27B-gguf at PQ2_0 on the RTX 3090: two conversations of 200,704 tokens with q8_0 K + q8_0 V (f16 KV does not fit this card at this window), vision through the publisher's Q8_0 Qwen3-VL projector, MTP impossible (no tensors in the file), and the measured four-arm VRAM census that priced the shape. Superseded by the 9B MTP export at 3 × 262,144."
tags: tags:
- llm - llm
- frank-zealot - frank-zealot
@@ -14,12 +14,41 @@ timestamp: 2026-10-17
# frank-zealot — llama.cpp (PrismML) Bonsai-2-27B PQ2_0, 2 × 200,704, q8_0 KV # frank-zealot — llama.cpp (PrismML) Bonsai-2-27B PQ2_0, 2 × 200,704, q8_0 KV
Live since 2026-10-17, replacing the **RETIRED 2026-10-17 → 2026-10-18** (AGENTS.md Runbook Index), superseded by
[[frank-zealot-llama-qwen35-9b-q6k-2x200704]] era (Qwen3.5-9B Q6_K, exact f16 [[frank-zealot-llama-prism-bonsai9-mtp]] — prism-ml/Ternary-Bonsai-2-**9B**
KV, same 2 × 200,704 shape). The trade: the ternary 27B costs ~3.4× the weights PQ2_0 **with the MTP head**, THREE conversations at the native 262,144 window
of the 9B and buys a 3× larger, stronger base at the SAME window contract — on fork build 10777. This era stays the **rollback target**: the payload
paid for by quantizing KV (f16 KV is arithmetically impossible for this (`/home/dank/models/Bonsai2-PQ2_0/`) and the 10754 fork tree
payload on this card, measured below). (`~/GitPackages/llama-prism/`) are both still on disk. It owns the laws below —
the 34,816 B/token KV figure over 16 full-attention layers, the 25,088 MiB f16
OOM wall, and the two-boot VRAM pricing that every later shape on this card
reused.
## 2026-10-18 bench window — production unit stopped, candidate arm on 7771
As of 2026-10-18 frank-zealot sits in a deliberate, dated **bench window**
(fleet Benchmarking Policy: stop the production unit, bench candidate boots
against the service itself). The journal shows the stop at 03:38:31 and the
known llama.cpp shutdown shape — SIGTERM ignored past `TimeoutStopSec`,
`State 'stop-sigterm' timed out. Killing.` at 03:40:01 — after which the unit
reports `failed` (`is-enabled` → enabled); self-healing is merely parked
while the window runs. What answers 7771 is a **hand-booted candidate arm**
(`llama-prism-b10777/llama-server` on the 9B MTP file
`Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, alias
`frank-zealot`), not this runbook's payload: candidate-arm flags are not a
state — the pid and shape moved between two censuses ten minutes apart (one
argv read `--ctx-size 786432 --parallel 3 --spec-type draft-mtp`; a later
`/props` read `n_ctx 262144`, `speculative.types=none`); quote neither, and
expect `/v1/models` to report the candidate until the window closes. The
window's remedy is the redeploy: `./scripts/deploy.sh dankclaw --service
frank-zealot`. The production trio below is unchanged — the last PRODUCTION
boot under the unit (03:14–03:38 the same morning) printed the contract
exactly: `n_ctx = 401408`, `n_ctx_seq = 200704`, `CUDA0 KV buffer size =
13328.00 MiB`, 2 slots (journal).
**Window CLOSED 2026-10-18:** that candidate became production. The redeploy
shipped the 9B MTP export at `3 × 262,144` on fork build 10777 — see
[[frank-zealot-llama-prism-bonsai9-mtp]]; this payload is the rollback target.
## Identity ## Identity
@@ -0,0 +1,222 @@
---
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."
tags:
- llm
- frank-zealot
- dankclaw
- llama-cpp
- gguf
- rtx3090
- mtp
- spec-decode
timestamp: 2026-10-18
---
# frank-zealot — llama.cpp (PrismML) Bonsai-2-9B PQ2_0 + MTP head, 3 × 262,144, q8_0 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.
## Payload identity
| Item | Value |
|---|---|
| Runtime | `/home/dank/GitPackages/llama-prism-b10777/llama-server` — PrismML **fork** build, `--version` → `0.2.0-dev (build 10777, commit e3de71522)` |
| Checkpoint | `/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf` — 2,675,410,720 B, Hub-LFS sha256 `c79fcdfaf6b7bf47089b50dadcfd11f9ce7d3e96adf3bbefde7912e5ef69e22b` |
| Vision | `/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf` — 624,229,664 B, Hub-LFS sha256 `e0cb6f6988cea323ef798cd77d019bbb819af75582f034eaab0e7232314953db` |
| Fetch | `scripts/download-frank-zealot-bonsai9.sh [DEST]` (`--verify-only`; `-f`, byte-count and LFS sha256 per file) |
| Alias | `frank-zealot` (`ALIAS`); the SERVICE/unit/scripts keep the StarCraft name, only the wire id is the model id |
| Port | 7771, root systemd unit `frank-zealot.service` (relinked), `TimeoutStopSec=180` |
### The runtime lock has two tumblers now
1. **The tensor type.** `general.file_type 141` with **202 tensors of the fork's
PQ2_0 type** (of 442 total; the rest are f32 norms/scalars and 8 q8_0 in the
MTP block) plus `prism.hadamard.*` metadata
(`normalized-sylvester-walsh-hadamard`, `block_size 4096`,
`axis input-last-dimension`, `sign_mode explicit` over 201 weight names,
inverse on `token_embd.weight`, `gdn_v_grouped true`). Stock llama.cpp
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.
## Header census (read from the GGUF, not the card)
`general.architecture qwen35`, `general.name Ternary-Bonsai-2-9B`,
`general.size_label 9B`, `general.quantization_version 2`,
`general.file_type 141`:
| Key | Value | Note |
|---|---|---|
| `qwen35.block_count` | **33** | 32 trunk blocks + `blk.32` = the MTP block |
| `qwen35.full_attention_interval` | 4 | ⇒ **8 full-attention + 24 Gated DeltaNet** trunk layers |
| `qwen35.context_length` | 262,144 | native; the deployed slot equals it |
| `qwen35.embedding_length` / `feed_forward_length` | 4,096 / 12,288 | |
| `attention.head_count` / `head_count_kv` | 16 / **4** | |
| `attention.key_length` / `value_length` | 256 / 256 | |
| `ssm.conv_kernel` / `state_size` / `group_count` | 4 / 128 / 16 | the 27B: 4 / 128 / 16 |
| `ssm.time_step_rank` / `inner_size` | **32 / 4,096** | the 27B: 48 / 6,144 |
| `rope.freq_base` / `dimension_count` / `dimension_sections` | 1e7 / 64 / `[11,11,10,0]` | |
| `qwen35.nextn_predict_layers` | **1** | the MTP head is IN the file |
| `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 — `RS buffer size = 452.25 MiB (3 cells, 32 layers, 3 seqs)` ≈ 150 MiB
each — so 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`,
`--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.
## 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.
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 |
|---|---|---|---|---|---|---|
| **3 × 262,144** | **off** | **2048** | **17,876 / 6,249** | **167.2** | **280.2** | **2,877** |
| 3 × 262,144 | on | 2048 | 22,706 / 1,419 | 109.2 | — | — |
| 3 × 262,144 | on | 1024 | 21,400 / 2,725 | 213.5 | 250.8 | 2,395 |
| 2 × 262,144 | on | 2048 | 17,178 / 6,947 | 213.2 | — | — |
| 1 × 262,144 | on | 2048 | 11,652 / 12,473 | 214.2 | — | — |
**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
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.
## Boot ledger (deployed arm, verbosity 4)
```
CUDA0 KV buffer size = 13056.00 MiB # q8_0 K + q8_0 V, 786,432 positions
CUDA0 RS buffer size = 452.25 MiB # 3 recurrent-state cells, 32 layers, 3 seqs
compute 1,409 MiB CUDA + 1,201 MiB CUDA_Host
mmproj 595 MiB + 248 MiB # charged AFTER the fit check
nvidia-smi settled: 17,876 MiB used / 6,249 MiB free of 24,123
```
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 (deployed arm)
- **167.2 t/s** greedy decode (512 tokens, `/completion` `timings`); 158.9 t/s
single-stream chat with the card's sampler.
- **280.2 t/s aggregate** across three concurrent streams (≈93.8 each).
- **2,877 t/s prefill** at 106,418 prompt tokens.
- The 27B record it replaces: 76.6 decode, 109.4 aggregate across two streams,
691.7 prefill at 166k. Beaten on every axis.
- The **first request after a boot is a warmup artifact** (37.7 t/s class) —
never report it.
## 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).
- **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.
- **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.
- **≥256 px probe images** or the model answers "video".
- **Thinking tokens count against `max_tokens`.** A vision probe at 64 tokens
returns EMPTY `content` + `finish_reason: "length"` — the endpoint looks blind
while it is only mid-thought. Budget ≥512.
- **`presence_penalty` has no GGUF metadata key**, so the one recipe value that
must reach the argv is this one. The serve script uses
`${PRESENCE_PENALTY=1.0}` — **not `:=`** — because `""` is the documented
opt-out and `:=` would silently re-add the flag (caught by the seam, fixed
2026-10-18).
- **Spec flags move as a SET.** A bare `--model-draft`/`--spec-type` arm without
the whole set has segfaulted at load on this fork family in the past;
`SPEC_TYPE=none` strips all of them.
- **Base alignment is the censored base lineage, and the CRACK swap is NOT
one line here.** `/home/dank/models/Bonsai2-CRACK/` matches the **27B**
geometry, not this 9B file; a dealigned 9B would have to be fetched.
- **A raw `/completion` request** logs `failed to parse reasoning content
(… parse_reasoning=true)` when the model closes a bare prompt with `` —
cosmetic (`probe_request.cpp:789`); `/v1/chat/completions` parses
`reasoning_content` correctly. Do not lower `VERBOSITY`: the KV/buffer ledger
is what VRAM forensics read.
- **A failed stop leaves a `failed` unit, and an orphan keeps answering.** On
2026-10-18 the trial stop hit `State 'stop-sigterm' timed out. Killing.` →
`Active: failed (Result: timeout)`, while the hand-booted arm kept serving
7771 — a live port with no unit behind it. `TimeoutStopSec=180` is now on the
unit (llama.cpp needs the drain time to release slots and the card); the
hand-booted arm must be killed by PID, never `pkill -f llama-server`.
## Compaction hub
`models.yml` routes `compactionModel` for `frank-archon`, `frank-dragoon` and
`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).
## 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.
## Rollback
Revert the swap commit. On disk: the **27B payload**
(`/home/dank/models/Bonsai2-PQ2_0/`) + the **10754** fork tree, the Qwen3.5-9B
Q6_K payload (`/home/dank/models/Qwen35-9B-HauhauCS-Q6K/`) + master CUDA build,
the Gemma4, MiMo, Swift and Ornith payloads. Then
`./scripts/deploy.sh dankclaw --service frank-zealot`.
## Related
- [[frank-zealot-llama-prism-bonsai2-pq20]] — the retired 27B era (rollback target)
- [[Llama Cpp Serving Sizing]] — the two-boot VRAM pricing method
- [[frank-dragoon-flash-server-v2-native]] — the other 2026-10-18 cutover
- [[deployment]] — deploy flow and incidents
+68 -27
View File
@@ -11,15 +11,19 @@ TP=2 (`--tp-wire wht6`), experts in a 44 GiB/rank pinned host pool + VRAM cache
(explicit `--vram-weights-mib 25600 --vram-kv-mib 5120` = 30,720 of the (explicit `--vram-weights-mib 25600 --vram-kv-mib 5120` = 30,720 of the
30,969 MiB the engine's own budget table makes claimable), the 47.7 GiB PLE 30,969 MiB the engine's own budget table makes claimable), the 47.7 GiB PLE
table pinned to host RAM, **fp8 KV at 3 × 200,000**, MTP spec decode depth 2, table pinned to host RAM, **fp8 KV at 3 × 200,000**, MTP spec decode depth 2,
froggeric v22.5 template. Rollback: the in-house Swift1.5 w4dn8 `.rad` (on froggeric v22.5 template. Rollback: revert the swap commit and re-download the
disk); tags frank-archon-freetoken-pr587-20261005 and in-house Swift1.5 w4dn8 `.rad` byte-exact (purged from disk 2026-10-17; Hub
frank-archon-vllm-shapeG-20261003), `frank8465/Swift-1.5-Qwen3.8-Flash-Next-GPTQ-W4A8-Radiance-AMD-R9700`); tags
frank-archon-freetoken-pr587-20261005 and frank-archon-vllm-shapeG-20261003
(AGENTS.md Runbook Index; `find /home/dank/models -name '*.rad'` on
Dankserver 2026-10-18 lists only the iq4r file),
and Dankclaw runs two endpoints — **halogen-flash-server 0.16.2 and Dankclaw runs two endpoints — **halogen-flash-server 0.16.2
(frank-dragoon, port 7770: ghcr.io/peonist-ai/halogen-flash-server @ pinned (frank-dragoon, port 7770: ghcr.io/peonist-ai/halogen-flash-server @ pinned
digest serving the NATIVE v2 conversion of the abliterated digest serving the NATIVE v2 conversion of the abliterated
Qwen3.8-Flash-Next, a ~62 GiB v2 .hgn that loads in no other engine, Qwen3.8-Flash-Next, a ~62 GiB v2 .hgn that loads in no other engine,
MTP spec decode always-on (HALOGEN_SPEC_ADAPT=0), TWO slots over ONE MTP spec decode always-on (HALOGEN_SPEC_ADAPT=0), TWO slots over ONE
401,408-position KV pool at ctx 200704, vision ON)** and 200,704-position KV pool at ctx 200704 — the 2 × ctx = 401,408 raise is
committed 2026-10-18 but NOT yet deployed, vision ON)** and
**llama.cpp on the PrismML fork (frank-zealot, port 7771: prism-ml **llama.cpp on the PrismML fork (frank-zealot, port 7771: prism-ml
Ternary-Bonsai-2-27B at **PQ2_0** — 27B qwen35 hybrid (16 full-attention + 48 Ternary-Bonsai-2-27B at **PQ2_0** — 27B qwen35 hybrid (16 full-attention + 48
Gated DeltaNet layers), TWO 200,704-token conversations, MTP disabled, Gated DeltaNet layers), TWO 200,704-token conversations, MTP disabled,
@@ -54,7 +58,7 @@ gfx1151 iGPU 114 GiB GTT]
E[flash-server container gfx1151, digest-pinned image E[flash-server container gfx1151, digest-pinned image
frank-dragoon, port 7770] frank-dragoon, port 7770]
F[flash-server v2 native abliterated .hgn + ngram table + vision tower F[flash-server v2 native abliterated .hgn + ngram table + vision tower
ONE 401408-position KV pool at ctx 200704, 2 slots, MTP always-on, image input] ONE 200704-position KV pool at ctx 200704 (401408 committed, awaiting deploy), 2 slots, MTP always-on, image input]
G[llama.cpp PrismML fork CUDA build G[llama.cpp PrismML fork CUDA build
frank-zealot, port 7771] frank-zealot, port 7771]
H[Ternary-Bonsai-2-27B PQ2_0 GGUF + Q8_0 mmproj H[Ternary-Bonsai-2-27B PQ2_0 GGUF + Q8_0 mmproj
@@ -91,7 +95,11 @@ frank-zealot, port 7771]
**one pool behind two slots**, sized so TWO maximum-length requests coexist — **one pool behind two slots**, sized so TWO maximum-length requests coexist —
raised from 200,704 (5.5 GiB) on 2026-10-18 for frank-zealot parity on total raised from 200,704 (5.5 GiB) on 2026-10-18 for frank-zealot parity on total
in-flight context, ~10.8 GiB reserved against the card's 262,144/262,144 = in-flight context, ~10.8 GiB reserved against the card's 262,144/262,144 =
7.2 GiB. The RAM comes out of the n-gram table's page cache, not out of a 7.2 GiB. **Deploy state 2026-10-18: the running container and the
host-deployed env still carry the 200,704-position pool** (boot ledger
`reserving the KV pool, 200704 positions (about 5.5 GiB)`; host
`frank-dragoon.env` `HALOGEN_KV_POOL_POSITIONS=200704`) — the doubling lands
on the next redeploy (commit `2b12716`).
longer window: the per-request ceiling stays 200,704, because 262,144 is the longer window: the per-request ceiling stays 200,704, because 262,144 is the
native wall and the engine refuses more without `HALOGEN_ROPE_YARN`. native wall and the engine refuses more without `HALOGEN_ROPE_YARN`.
MTP head always on, sampler 1.0/0.95/20, coupled cap/timeout MTP head always on, sampler 1.0/0.95/20, coupled cap/timeout
@@ -120,17 +128,41 @@ frank-zealot, port 7771]
(`slow`/`advisor` → frank-archon) carried forward. Superseded by the (`slow`/`advisor` → frank-archon) carried forward. Superseded by the
MODEL swap: the abliterated v2 .hgn loads only in flash-server, so the MODEL swap: the abliterated v2 .hgn loads only in flash-server, so the
engine moved with the model engine moved with the model
- [[frank-zealot-llama-prism-bonsai2-pq20]] — **ACTIVE since 2026-10-17** - [[frank-zealot-llama-prism-bonsai9-mtp]] — **ACTIVE since 2026-10-18**
(root systemd unit, port 7771): the **PrismML fork** build of llama.cpp (root systemd unit, port 7771): the **PrismML fork** build of llama.cpp
(vendor 10754, commit `2459f68b5` — the only loader for the payload's custom (vendor **10777**, commit `e3de71522` — the only loader for the payload's
tensor type 142 + `prism.hadamard.*` transform) serving custom PQ2_0 tensor type + `prism.hadamard.*` transform, and since this swap
prism-ml/Ternary-Bonsai-2-27B-gguf **PQ2_0** (2.13 bpw) on the RTX 3090 — also the minimum build that pairs MTP with a quantized V cache) serving
TWO 200,704-token conversations (`--ctx-size 401408 --parallel 2`), **q8_0 K prism-ml/Ternary-Bonsai-2-9B-gguf **PQ2_0 with the MTP head IN the file** on
+ q8_0 V** (34 KiB/token over the 16 full layers = 13,328 MiB; the f16 pair the RTX 3090 — **THREE 262,144-token conversations**
needs 25,088 MiB and dies), settled 22,664 used / 1,461 free, vision via the (`--ctx-size 786432 --parallel 3`, every slot at the window the model was
publisher's Q8_0 mmproj (+873 MiB, charged AFTER the engine's fit check), MTP trained at), **q8_0 K + q8_0 V** (17 KiB/token over the 8 full layers =
OFF (no MTP tensors). 76.6 t/s single-stream decode, 691.7 t/s prefill at 13,056 MiB ledger-exact; the f16 pair needs 26,112 MiB and dies at
166k, 109.4 t/s aggregate at two streams. Supersedes the Qwen3.5-9B era below. `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`):
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
is the compaction hub. The `--presence-penalty` opt-out law (`${VAR=}`, not
`${VAR:=}`) and the `TimeoutStopSec=180` stop-drain law were both earned here.
- [[frank-zealot-llama-prism-bonsai2-pq20]] — **RETIRED 2026-10-17 → 10-18**
(payload + fork build 10754 tree on disk as rollback): the first PrismML-fork
era — prism-ml/Ternary-Bonsai-2-**27B**-gguf **PQ2_0**, TWO 200,704-token
conversations (`--ctx-size 401408 --parallel 2`), q8_0 K + q8_0 V
(34,816 B/token over the 16 full layers = 13,328 MiB; f16 needs 25,088 MiB and
dies), settled 22,664 used / 1,461 free, MTP **impossible** (that export
carries no head), 76.6 t/s decode, 691.7 t/s prefill at 166k, 109.4 t/s
aggregate at two streams. Owns the 34 KiB/token KV law, the f16 OOM wall, the
two-boot VRAM pricing, and the record of the 2026-10-18 bench window
(production unit stopped 03:38, hand-booted 9B candidate answering the port)
that closed with this swap.
- [[frank-zealot-llama-qwen35-9b-q6k-2x200704]] — **RETIRED 2026-10-07 → - [[frank-zealot-llama-qwen35-9b-q6k-2x200704]] — **RETIRED 2026-10-07 →
10-17** (payload + master CUDA build on disk as rollback): llama.cpp master 10-17** (payload + master CUDA build on disk as rollback): llama.cpp master
(CUDA sm_86) serving HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive (CUDA sm_86) serving HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive
@@ -197,10 +229,13 @@ frank-zealot, port 7771]
dankserver 2026-10-15 (Frank's one-time rm, volume for this payload; dankserver 2026-10-15 (Frank's one-time rm, volume for this payload;
rebuild via the fetch + build scripts named in the fp8-native runbook) rebuild via the fetch + build scripts named in the fp8-native runbook)
- [[frank-archon-radiance-swift15-w4dn8]] — **RETIRED 2026-10-08 → 10-15 - [[frank-archon-radiance-swift15-w4dn8]] — **RETIRED 2026-10-08 → 10-15
(superseded by the publisher iq4r swap; payload on disk as the rollback)**: (superseded by the publisher iq4r swap; the `.rad` was PURGED from disk
2026-10-17 — rollback = revert + byte-exact re-download of
`frank8465/Swift-1.5-Qwen3.8-Flash-Next-GPTQ-W4A8-Radiance-AMD-R9700`)**:
Swift1.5 Qwen3.8-Flash-Next BF16 → GPTQ-calibrated W4Dn8 `.rad` on radiance Swift1.5 Qwen3.8-Flash-Next BF16 → GPTQ-calibrated W4Dn8 `.rad` on radiance
1.0.13 (recipe + 10M-token calibration + KLD gate vs the BF16 container); 1.0.13 (recipe + 10M-token calibration + KLD gate vs the BF16 container);
the FP8-native payload remains the rollback. **2026-10-13: fp8 KV, and the the FP8-native payload was the second rollback until it too was deleted
2026-10-15. **2026-10-13: fp8 KV, and the
pair re-cut twice** — `--kv-cache-dtype fp8` first at pair re-cut twice** — `--kv-cache-dtype fp8` first at
`--max-num-seqs 4`/6912 MiB pool (843,566 tokens, slab 22208), then **3 slots `--max-num-seqs 4`/6912 MiB pool (843,566 tokens, slab 22208), then **3 slots
with the pool cut to the contract (5120 MiB, 608,027 tokens) and the slab with the pool cut to the contract (5120 MiB, 608,027 tokens) and the slab
@@ -219,7 +254,9 @@ frank-zealot, port 7771]
reset the knob to its default (125,988 MiB) and the endpoint refused to boot reset the knob to its default (125,988 MiB) and the endpoint refused to boot
6,919 times in ~20 h; `scripts/ttm-preflight.sh` is the repair. 6,919 times in ~20 h; `scripts/ttm-preflight.sh` is the repair.
- [[frank-archon-radiance-qwen38-native-fp8]] — **RETIRED 2026-10-08 (the - [[frank-archon-radiance-qwen38-native-fp8]] — **RETIRED 2026-10-08 (the
w4dn8 swap replaced it; its `.rad` stays on disk as the rollback)** w4dn8 swap replaced it; its `.rad` was DELETED from disk 2026-10-15 —
rebuild = `scripts/download-frank-archon.sh` + `scripts/build-frank-archon-rad.sh`,
`scripts/frank-archon.env:59-62`)**
(root systemd unit, port 7770): the StillDeadcode **radiance** engine (root systemd unit, port 7770): the StillDeadcode **radiance** engine
(C++/HIP, libr4d gfx1201 kernels, digest-pinned 287 MiB image, engine as (C++/HIP, libr4d gfx1201 kernels, digest-pinned 287 MiB image, engine as
the image entrypoint) serving the OFFICIAL `Qwen/Qwen3.8-Flash-Next-FP8` the image entrypoint) serving the OFFICIAL `Qwen/Qwen3.8-Flash-Next-FP8`
@@ -248,8 +285,10 @@ frank-zealot, port 7771]
`reasoning_content`). Measured: boot ~2.3 min, decode 17.1 tok/s `reasoning_content`). Measured: boot ~2.3 min, decode 17.1 tok/s
single-stream, vision "Red" probe passes, native tool_calls; TP=2 does single-stream, vision "Red" probe passes, native tool_calls; TP=2 does
not boot on ROCm (pynccl JIT) — second card idle. Superseded by the not boot on ROCm (pynccl JIT) — second card idle. Superseded by the
radiance swap for TP=2; the image + payload stay on disk until the radiance swap for TP=2. Disk state 2026-10-18: the image is still in
rollback window closes dankserver's rootless storage (`localhost/frank-primary-freetoken:pr587-77a7201`,
29.7 GB — the pre-rename name); the orcarouter FP8 payload is NO LONGER on
disk (no `*orcarouter*` path under /home/dank, `find` 2026-10-18)
- [[dankserver-qwen38-fp8-expert-offload-tuning]] — **RETIRED 2026-10-05 - [[dankserver-qwen38-fp8-expert-offload-tuning]] — **RETIRED 2026-10-05
(rollback target; the vLLM trio + env lore live in the tag)**: the (rollback target; the vLLM trio + env lore live in the tag)**: the
tcclaviger RDNA4 tcclaviger RDNA4
@@ -278,12 +317,14 @@ frank-zealot, port 7771]
35 tok/s target is NOT met, bounded by the fork's expert-gather read path 35 tok/s target is NOT met, bounded by the fork's expert-gather read path
(~8.2 GB/s effective vs a 22.74 GB/s link) at a 0.53-0.66 cache hit rate.** (~8.2 GB/s effective vs a 22.74 GB/s link) at a 0.53-0.66 cache hit rate.**
Rollback: commit `2339fae` (the qwen FP8 + MTP4 shape of 2026-09-29 → 09-30) Rollback: commit `2339fae` (the qwen FP8 + MTP4 shape of 2026-09-29 → 09-30)
- [[frank-archon-r9v-radiance-qwen38-q8]] — RETIRED 2026-09-30 (two swaps [[frank-archon-r9v-radiance-qwen38-q8]] — RETIRED 2026-09-30 (two swaps
back; payload and R9V state dirs intact on disk, git tag back; the R9V state dirs are on disk (`/home/dank/r9v-state`,
`~/.local/state/r9v`) but the payload GGUFs are not — `/home/dank/models`
holds only the iq4r `.rad` as of 2026-10-18; git tag
frank-archon-r9v-q8-20260929 is the last pre-cutover commit): frank-archon-r9v-q8-20260929 is the last pre-cutover commit):
[[frank-archon-r9v-radiance-qwen38-uncensored]] — RETIRED 2026-09-27 [[frank-archon-r9v-radiance-qwen38-uncensored]] — RETIRED 2026-09-27
(same-day swap to the Q8_0 payload; rollback target, state + payload (same-day swap to the Q8_0 payload; state + payloads no longer on disk
intact on disk): the R9V radiance-vLLM fork serving 2026-10-18, only the R9V state dirs remain): the R9V radiance-vLLM fork serving
Dyluhn/Qwen3.8-Flash-Next-Uncensored-R9V-IQ4_XS (92.39 GiB, 3 shards) + Dyluhn/Qwen3.8-Flash-Next-Uncensored-R9V-IQ4_XS (92.39 GiB, 3 shards) +
blockfp8 MTP4 (depth 4) + F16 vision on BOTH R9700s — TP=2 tiered blockfp8 MTP4 (depth 4) + F16 vision on BOTH R9700s — TP=2 tiered
experts (UVA pinned host copy, 62 hot + 160-slot LRU on rank 0, 428 experts (UVA pinned host copy, 62 hot + 160-slot LRU on rank 0, 428
@@ -505,8 +546,8 @@ frank-zealot, port 7771]
## CIRU Reference (Dankclaw) ## CIRU Reference (Dankclaw)
- [[ciru-activefpx-promptforge]] — the RETIRED v3 model, ROCm 7.15 toolchain, - [[ciru-activefpx-promptforge]] — the RETIRED v3 model, ROCm 7.15 toolchain,
and build history (on Dankclaw; the slot now serves the kingjones and build history (on Dankclaw; the gfx1151 slot now serves frank-dragoon's
ROCmFP4 model, see [[kingjones-strix-mtp]]) flash-server v2-native payload — see [[frank-dragoon-flash-server-v2-native]])
- [[kairic-edge]] — Kairic Edge IU4 runtime: build runbook, binary - [[kairic-edge]] — Kairic Edge IU4 runtime: build runbook, binary
identity, and experiment summary (retired 2026-08-22) identity, and experiment summary (retired 2026-08-22)
+13 -7
View File
@@ -399,13 +399,19 @@ The LLM inference fleet on the two hosts. Full index:
- [[ciru-activefpx-promptforge]] — the RETIRED v3 CIRU PromptForge runtime on Dankclaw (superseded 2026-08-24) - [[ciru-activefpx-promptforge]] — the RETIRED v3 CIRU PromptForge runtime on Dankclaw (superseded 2026-08-24)
- [[kingjones-strix-mtp]] — the kingjones Qwen3.8-27B ROCmFP4 STRIX MTP model on Dankclaw (active) - [[kingjones-strix-mtp]] — the kingjones Qwen3.8-27B ROCmFP4 STRIX MTP model on Dankclaw (active)
- [[dealign-ornith-crack]] — the retired Ornith-1.5-35B-A3B CRACK model on Dankserver (retired 2026-09-02) - [[dealign-ornith-crack]] — the retired Ornith-1.5-35B-A3B CRACK model on Dankserver (retired 2026-09-02)
- [[frank-zealot-llama-prism-bonsai2-pq20]] — the **ACTIVE** frank-zealot - [[frank-zealot-llama-prism-bonsai9-mtp]] — the **ACTIVE** frank-zealot
runbook since 2026-10-17: the PrismML vendor llama.cpp build 10754 (the only runbook since 2026-10-18: the PrismML vendor llama.cpp build 10777 (the only
loader for GGML type 142 / `prism.hadamard.*`) serving loader for the custom PQ2_0 tensor type / `prism.hadamard.*`, and the floor
prism-ml/Ternary-Bonsai-2-27B-gguf **PQ2_0** on the RTX 3090 — TWO for MTP beside a quantized V cache) serving
200,704-token conversations, q8_0 K + q8_0 V (f16 OOMs at 2×200,704: 34 KiB prism-ml/Ternary-Bonsai-2-9B-gguf **PQ2_0 with the MTP head** on the RTX 3090
vs 64 KiB per token over 16 full-attention layers), MTP **off** (no head in — THREE 262,144-token conversations, q8_0 K + q8_0 V (f16 OOMs at this pool:
the file), vision through the Q8_0 mmproj; boot 22,664 used / 1,461 free 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
- [[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**
(no head in that export), vision through the Q8_0 mmproj; boot 22,664 / 1,461
- [[frank-zealot-llama-qwen35-9b-q6k-2x200704]] — RETIRED 2026-10-17 (rollback - [[frank-zealot-llama-qwen35-9b-q6k-2x200704]] — RETIRED 2026-10-17 (rollback
target, payload on disk): llama.cpp master (CUDA sm_86) serving target, payload on disk): llama.cpp master (CUDA sm_86) serving
HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive **Q6_K** on the RTX 3090 — HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive **Q6_K** on the RTX 3090 —
+49
View File
@@ -2108,3 +2108,52 @@ every restart since the cutover.
satisfied, endpoint still absent), AGENTS.md key lesson, both index pages. satisfied, endpoint still absent), AGENTS.md key lesson, both index pages.
bats 371/371. frank-dragoon and frank-zealot were live the whole window; only bats 371/371. frank-dragoon and frank-zealot were live the whole window; only
the reboot interrupted them (~40 s to listen, health 200). the reboot interrupted them (~40 s to listen, health 200).
## 2026-10-18 — frank-zealot: Bonsai-2-9B PQ2_0 with the MTP head (3 × 262,144)
- Swapped `prism-ml/Ternary-Bonsai-2-27B-gguf` → **`Ternary-Bonsai-2-9B-gguf`**
at PQ2_0 (`Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf`, 2,675,410,720 B, Hub-LFS
sha256 `c79fcdfaf6b7…` verified at fetch) plus the publisher's **Q8_0** mmproj
(624,229,664 B, `e0cb6f69…`), into `/home/dank/models/Bonsai2-9B-MTP/`; the
runtime moved to vendor fork build **10777** (`e3de71522`) because the MTP
arm needs the build that pairs MTP with a quantized V cache.
- The win is the **window, not the quant**: `--ctx-size 786432 --parallel 3`
puts every conversation back at the trained 262,144 (the 27B era sold each
slot 200,704), and the fleet's widest context makes :7771 the **compaction
hub** — `models.yml` `contextWindow` 200704 → 262144. Measured vs the 27B:
decode 167.2 vs 76.6 t/s single-stream, aggregate 280.2 vs 109.4 t/s across
the (now three) concurrent streams, prefill 2,877 t/s at 106,418 depth.
- **MTP armed, then OFF by measurement.** The head is in the file, the fork
loads it, and the draft context bills its own **f16** KV over the WHOLE pool
— 3,072 MiB at 786,432 positions, ledger-confirmed. At `UBATCH_SIZE=2048`
that squeeze **halved decode (167.2 → 109.2 t/s)**. The documented MTP shape
is `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` → 213.5 t/s; declined, because
+17 % decode is not worth spending 6.2 GiB of the 6,249 MiB free. The arm is
recorded in the env header, not left to rediscovery.
- Two bugs the trial caught, both fixed in the repo: `${SPEC_PRESENCE_PENALTY:=0.0}`
was a *default-assignment*, so the env value always won and the documented
"MTP runs without `--presence-penalty`" arm was unreachable — now `${VAR=0.0}`,
which lets `SPEC_TYPE=draft-mtp` drop the flag. And `up{job="frank-zealot"}`
had read 0 for the **entire retained Prometheus history**: llama.cpp answers
**501** on `/metrics` unless `--metrics` is passed, so the scrape failed beside
a healthy `/health` and `LlmEndpointDown` fired for days. `METRICS=1` is now
the default and the seam pins it.
- Boot ledger: KV `13,056.00 MiB` (8 full-attention layers × 4 KV heads × 256
dim, q8_0 K + q8_0 V over 786,432 positions — ledger-exact), settled
**17,876 used / 6,249 free**; the f16 pair would need 26,112 MiB and cannot
boot this card. `TimeoutStopSec=180` carries over (SIGTERM ignored past the
old default during the 03:38 stop).
- Header census that corrected my own first pass: `block_count` is **33**
(32 trunk + the `blk.32` MTP block, which carries a full attention + FFN in
q8_0 — that is why the draft context bills real KV), **202 of 442** tensors
carry the fork's PQ2_0 type (norms f32, MTP projections q8_0), `hadamard`
`block_size` **4096** with `gdn_grouped=True`. The "2.13 bpw / type 142 / 32
layers" figures inherited from the 27B page were wrong for this file and are
corrected in AGENTS.md, the env header and the runbooks.
- Docs: new runbook `LLM/frank-zealot-llama-prism-bonsai9-mtp.md`; the 27B page
re-banned **RETIRED 2026-10-17 → 10-18** with its bench-window section closed;
both index pages, the homepage card, `Dankclaw/OpenWebUI.md` and the live
picker row (DB `description` + context length 262,144) re-keyed. Rollback:
revert the swap commit — the 27B payload and the build-10754 fork tree are
both still on disk.
+2 -2
View File
@@ -295,13 +295,13 @@
"model": { "model": {
"unit": "zealot", "unit": "zealot",
"role": "Frank's utility model", "role": "Frank's utility model",
"hf": "https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-gguf", "hf": "https://huggingface.co/prism-ml/Ternary-Bonsai-2-9B-gguf",
"tagline": "HONOR GUIDE ME!", "tagline": "HONOR GUIDE ME!",
"sfx": [ "sfx": [
"zealot-honor.ogg" "zealot-honor.ogg"
], ],
"equiv": "Equivalent to last year's frontier level (2025).", "equiv": "Equivalent to last year's frontier level (2025).",
"spec": "Ternary-Bonsai-2-27B (PQ2_0 ternary weights) on an NVIDIA RTX 3090 — ~77 tokens/s; 200k-token context, two submissions at a time; sees images.", "spec": "Ternary-Bonsai-2-9B (PQ2_0 ternary weights, MTP head in the file) on an NVIDIA RTX 3090 — ~167 tokens/s; 262k-token context, three submissions at a time; sees images.",
"inputs": [ "inputs": [
"text", "text",
"image" "image"
+101 -52
View File
@@ -1,14 +1,17 @@
# Seam: the frank-zealot llama.cpp root unit on dankclaw. # Seam: the frank-zealot llama.cpp root unit on dankclaw.
# frank-zealot is the RTX 3090 endpoint; since 2026-10-17 it serves # frank-zealot is the RTX 3090 endpoint; since 2026-10-18 it serves
# prism-ml/Ternary-Bonsai-2-27B-gguf at PQ2_0 — the ternary Bonsai-2-27B, a # prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 — the ternary Bonsai-2-9B, a
# qwen35 hybrid with 64 blocks (16 full-attention + 48 Gated DeltaNet, no # qwen35 hybrid with 32 layers (8 full-attention + 24 Gated DeltaNet) and the
# nextn/mtp tensors in the file) — via the PrismML fork build of llama.cpp # MTP draft head INSIDE the file — via the PrismML fork build of llama.cpp
# (vendor build 10754), the ONLY loader that registers the payload's custom # (vendor build 10777, the minimum that pairs MTP with a quantized V cache),
# tensor type 142 and honours its prism.hadamard.* metadata: TWO 200,704-token # the ONLY loader that registers the payload's custom tensor type 142 and
# conversations (--ctx-size 401408 --parallel 2), q8_0 K + q8_0 V under flash # honours its prism.hadamard.* metadata: THREE 262,144-token conversations
# attention (f16 KV does not fit this card at this pool — the boot dies on a # (--ctx-size 786432 --parallel 3), q8_0 K + q8_0 V under flash attention (f16
# 25,088 MiB cudaMalloc), the publisher's own Q8_0 vision projector, and MTP # KV would bill 26,112 MiB of a 24,123 MiB card), the publisher's own Q8_0
# off. Log verbosity 4 ships so the VRAM-fit forensics land in the journal. # 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.
# This seam runs the serve script against a stub engine and asserts on the # 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. # argv it actually execs, so what is pinned is the command the operator gets.
@@ -97,15 +100,16 @@ run_stage() {
grep -q 'FRANK_ZEALOT_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-zealot.env' "$UNIT" grep -q 'FRANK_ZEALOT_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-zealot.env' "$UNIT"
} }
@test "the shipped env points at the fork runtime and the Bonsai-2 PQ2_0 pair" { @test "the shipped env points at the fork runtime and the Bonsai-2 9B MTP pair" {
# The payload path IS the endpoint's identity, and the runtime is part of # The payload path IS the endpoint's identity, and the runtime is part of
# it here: the file's custom tensor type 142 + prism.hadamard.* metadata # it here: the file's custom tensor type 142 + prism.hadamard.* metadata
# make the PrismML build the only loader, so a retarget that moves the # make the PrismML build the only loader, and this build must be >= b10776
# model but leaves the master build behind ships an endpoint that cannot # because MTP beside q8_0 V is illegal below it. A retarget that moves the
# load at all. # model but leaves the old build behind ships an endpoint that cannot load
grep -qE '^LLAMA_BIN=/home/dank/GitPackages/llama-prism/llama-server$' "$ENV_FILE" # (type 142) or cannot run the documented MTP arm (the build gate).
grep -qE '^MODEL_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0\.gguf$' "$ENV_FILE" grep -qE '^LLAMA_BIN=/home/dank/GitPackages/llama-prism-b10777/llama-server$' "$ENV_FILE"
grep -qE '^MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0\.gguf$' "$ENV_FILE" grep -qE '^MODEL_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0\.gguf$' "$ENV_FILE"
grep -qE '^MMPROJ_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0\.gguf$' "$ENV_FILE"
} }
@test "the deployer gates on exactly the payload paths the env file ships" { @test "the deployer gates on exactly the payload paths the env file ships" {
@@ -122,7 +126,7 @@ run_stage() {
done done
} }
@test "two conversations share the pool at a 200704 window each" { @test "three conversations share the pool at a 262144 window each" {
serve_argv serve_argv
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ "$(argv_value --alias)" = "frank-zealot" ] [ "$(argv_value --alias)" = "frank-zealot" ]
@@ -131,27 +135,31 @@ run_stage() {
[ "$(argv_value --model)" = "$MODEL_DIR/$MODEL_STUB" ] [ "$(argv_value --model)" = "$MODEL_DIR/$MODEL_STUB" ]
[ "$(argv_value --n-gpu-layers)" -gt 0 ] [ "$(argv_value --n-gpu-layers)" -gt 0 ]
# llama.cpp divides --ctx-size across slots, so the observable contract # llama.cpp divides --ctx-size across slots, so the observable contract
# is the per-slot window. Frank's 200k cap is a per-conversation cap, so # is the per-slot window. The 9B swap exists precisely so every
# the pair is 401,408 / 2 — and the harness contextWindow must quote # conversation gets the model's TRAINED 262,144 (the 27B had to be cut to
# 200704, not the pool and not the model's native 262,144. # 200,704 to fund two), and the harness contextWindow must quote 262144 —
[ "$(argv_value --parallel)" -eq 2 ] # the pool and the slot count are neither, and a longer prompt is a 400.
[ "$(argv_value --ctx-size)" -eq 401408 ] # 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 ]
local pool slots local pool slots
pool="$(argv_value --ctx-size)"; slots="$(argv_value --parallel)" pool="$(argv_value --ctx-size)"; slots="$(argv_value --parallel)"
[ $((pool % slots)) -eq 0 ] [ $((pool % slots)) -eq 0 ]
[ $((pool / slots)) -eq 200704 ] [ $((pool / slots)) -eq 262144 ]
} }
@test "the KV dtype pair follows the env file and flash-attn stays pinned on" { @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 # 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 # file, not two. The shipped pair is q8_0/q8_0: this payload bills
# 16 full-attention layers × 4 KV heads × 256 dim = 16,384 elements per # 8 full-attention layers × 4 KV heads × 256 dim = 8,192 elements per token
# token per cache, so f16/f16 would need 25,088 MiB of the 24,123 MiB card # per cache, so f16/f16 would need 26,112 MiB of the 24,123 MiB card while
# (measured: cudaMalloc OOM, engine fit 33,198 vs 23,558 free) while q8_0 # q8_0 bills 17,408 B/token = 13,056.00 MiB at the 786,432 pool — the
# bills 34,816 B/token = 13,328.00 MiB at the 401,408 pool. The boot # ledger said exactly that, and it is the referee. The 24 DeltaNet layers
# ledger, not this file, is the referee. flash-attn is not decoration: # bill a fixed recurrent state per slot (452.25 MiB for three cells), which
# quantized V is only legal with it, and it must stay explicit so a # is why pool arithmetic alone under-prices the shape. flash-attn is not
# future quant swap cannot silently fall back. # decoration: quantized V is only legal with it, and it must stay explicit
# so a future quant swap cannot silently fall back.
local env_k env_v local env_k env_v
env_k="$(sed -nE 's/^CACHE_TYPE_K=(.+)$/\1/p' "$ENV_FILE")" env_k="$(sed -nE 's/^CACHE_TYPE_K=(.+)$/\1/p' "$ENV_FILE")"
env_v="$(sed -nE 's/^CACHE_TYPE_V=(.+)$/\1/p' "$ENV_FILE")" env_v="$(sed -nE 's/^CACHE_TYPE_V=(.+)$/\1/p' "$ENV_FILE")"
@@ -166,15 +174,16 @@ run_stage() {
serve_argv serve_argv
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
# The shipped 4 (debug) is what makes VRAM-fit forensics possible from # The shipped 4 (debug) is what makes VRAM-fit forensics possible from
# the journal (KV layout, buffer sizes, graph capture). # the journal (KV layout, buffer sizes, graph capture) — the only way to
# prove the q8_0 KV contract and the draft-KV bill from a log.
[ "$(argv_value --log-verbosity)" = "4" ] [ "$(argv_value --log-verbosity)" = "4" ]
# And an env that names no verbosity still ships the engine default # And an env that names no verbosity still ships an explicit value: the
# explicitly — the script's written-down 3, never a blank. # serve script's written-down default is 4 too, never a blank.
sed -i '/^VERBOSITY=/d' "$STAGE/test.env" sed -i '/^VERBOSITY=/d' "$STAGE/test.env"
run_stage run_stage
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ "$(argv_value --log-verbosity)" = "3" ] [ "$(argv_value --log-verbosity)" = "4" ]
} }
@test "the Prometheus metrics endpoint is served unless opted out" { @test "the Prometheus metrics endpoint is served unless opted out" {
@@ -221,12 +230,16 @@ run_stage() {
} }
@test "speculation is off: no drafter flag reaches the argv" { @test "speculation is off: no drafter flag reaches the argv" {
# Frank, 2026-10-07: MTP is off because it CANNOT be on — the Q6_K file # Frank, 2026-10-18: this file DOES carry the MTP head (the boot arms prove
# carries no nextn/mtp tensors (verified from its tensor-name list), and # it loads and drafts, acceptance 0.798/0.617 per position, mean accepted
# MTP is a property of the file, not of the flags. The flags move as a # length 2.41) and it is still off, because the draft context bills its own
# set: SPEC_TYPE=none strips --spec-type, --spec-draft-n-max AND # f16 KV over the WHOLE pool — 4,096 B/position × 786,432 = 3,072 MiB plus
# --spec-draft-model together, so a no-spec boot cannot carry half a # buffers — and at --ubatch-size 2048 that squeeze halves decode: 167.2 →
# spec pair (the ROCmFPX-era baseline arm segfaulted at load doing that). # 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:
# 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).
serve_argv serve_argv
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
! grep -qxF -- '--spec-type' <<<"$output" ! grep -qxF -- '--spec-type' <<<"$output"
@@ -235,27 +248,35 @@ run_stage() {
grep -qE '^SPEC_TYPE=none$' "$ENV_FILE" grep -qE '^SPEC_TYPE=none$' "$ENV_FILE"
} }
@test "naming a drafter ships the full MTP trio" { @test "naming a drafter ships the MTP set, with or without a separate draft file" {
# The opt-back-in path (e.g. a future Qwen3.5 -mtp build): SPEC_TYPE + # The documented MTP arm (frank-zealot.env): SPEC_TYPE=draft-mtp ALONE is
# SPEC_DRAFT_FILE must reach the engine as a complete set — a draft-mtp # legal here because the head is IN the checkpoint and the draft context
# flag without its model file boots a drafter against the main weights and # rides the main model's weights — so --spec-draft-model must NOT appear,
# drafts nothing. # or the trunk would be loaded twice.
serve_argv serve_argv
{ {
echo 'SPEC_TYPE=draft-mtp' echo 'SPEC_TYPE=draft-mtp'
echo 'SPEC_DRAFT_N_MAX=3' echo 'SPEC_DRAFT_N_MAX=2'
echo "SPEC_DRAFT_FILE=$MODEL_DIR/$DRAFT_STUB"
} >> "$STAGE/test.env" } >> "$STAGE/test.env"
run_stage run_stage
[ "$status" -eq 0 ]
[ "$(argv_value --spec-type)" = "draft-mtp" ] [ "$(argv_value --spec-type)" = "draft-mtp" ]
[ "$(argv_value --spec-draft-n-max)" = "3" ] [ "$(argv_value --spec-draft-n-max)" = "2" ]
! grep -qxF -- '--spec-draft-model' <<<"$output"
# And an external drafter still reaches the engine as a complete set — the
# pair the ROCmFPX-era crash was about.
echo "SPEC_DRAFT_FILE=$MODEL_DIR/$DRAFT_STUB" >> "$STAGE/test.env"
run_stage
[ "$status" -eq 0 ]
[ "$(argv_value --spec-draft-model)" = "$MODEL_DIR/$DRAFT_STUB" ] [ "$(argv_value --spec-draft-model)" = "$MODEL_DIR/$DRAFT_STUB" ]
} }
@test "vision ships by default and MMPROJ_FILE empty opts out of the image path" { @test "vision ships by default and MMPROJ_FILE empty opts out of the image path" {
# Qwen3.5-9B is natively multimodal, but the Q6_K file carries no vision # The Bonsai-2 text file carries no vision tensors — the publisher's Q8_0
# tensors — the publisher's projector argument IS the vision capability: # projector argument IS the vision capability: no flag, no eyes. Q8_0 (not
# no flag, no eyes. # BF16) is the publisher's own requirement for concurrent image requests,
# which is what --parallel 3 means in practice.
serve_argv serve_argv
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ "$(argv_value --mmproj)" = "$MODEL_DIR/$MM_STUB" ] [ "$(argv_value --mmproj)" = "$MODEL_DIR/$MM_STUB" ]
@@ -268,6 +289,34 @@ run_stage() {
! grep -qxF -- '--mmproj' <<<"$output" ! grep -qxF -- '--mmproj' <<<"$output"
} }
@test "the card sampler's only non-metadata term reaches the argv" {
# general.sampling.* (temperature / top_p / top_k / min_p / repeat_penalty)
# rides in the checkpoint and the engine reads it itself; presence penalty
# is the ONE recipe value with no GGUF metadata key, so it must reach argv
# or the recipe is silently incomplete. Distinctive value first: proof the
# flag is wired to the env and not merely to the script's default.
serve_argv
{ echo 'PRESENCE_PENALTY=0.5'; } >> "$STAGE/test.env"
run_stage
[ "$status" -eq 0 ]
[ "$(argv_value --presence-penalty)" = "0.5" ]
# The shipped recipe value reaches the engine too.
local pp
pp="$(sed -nE 's/^PRESENCE_PENALTY=(.+)$/\1/p' "$ENV_FILE")"
sed -i "s/^PRESENCE_PENALTY=.*/PRESENCE_PENALTY=$pp/" "$STAGE/test.env"
run_stage
[ "$status" -eq 0 ]
[ "$(argv_value --presence-penalty)" = "$pp" ]
# The documented opt-out is absence of the flag, not an empty argument —
# which is why the default uses ${VAR=default}, not ${VAR:=default}.
sed -i "s/^PRESENCE_PENALTY=.*/PRESENCE_PENALTY=/" "$STAGE/test.env"
run_stage
[ "$status" -eq 0 ]
! grep -qxF -- '--presence-penalty' <<<"$output"
}
@test "the fleet reasoning contract reaches the argv" { @test "the fleet reasoning contract reaches the argv" {
serve_argv serve_argv
[ "$status" -eq 0 ] [ "$status" -eq 0 ]