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
2026-10-18 13:15:18 -07:00
parent 2b127163c8
commit 466fd477d8
14 changed files with 1001 additions and 314 deletions
+132 -70
View File
@@ -26,8 +26,8 @@ merge absorbed the former `vllmScripts` repo, history preserved).
| 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 |
| 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-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-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 **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
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
are the engine core; `--mm-max-patches` available),
and `frank-zealot` through
the publisher's own BF16 mmproj passed as `--mmproj` — the model is natively
multimodal but the Q6_K GGUF carries no vision tensors (same arrangement the
Gemma4 era used). `frank-zealot`'s `contextWindow` is
**200704** — the PER-SLOT window (`--ctx-size 401408 --parallel 2` since the
2026-10-07 Qwen3.5-9B swap), not the pool and not the model's native 262144:
the harness must compact before llama.cpp answers a longer prompt with a 400.
the publisher's own **Q8_0** mmproj passed as `--mmproj` — Q8_0 is the
publisher's requirement for concurrent image requests (the card states the
BF16 tower can crash `llama-server` when several image requests run at once,
and this endpoint runs three slots). `frank-zealot`'s `contextWindow` is
**262144** — the PER-SLOT window (`--ctx-size 786432 --parallel 3` since the
2026-10-18 Bonsai-2-**9B** swap), which since that swap EQUALS the model's
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
that
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)
**The PrismML fork build of llama.cpp** (vendor release, `--version` =
`0.2.0-dev (build 10754, commit 2459f68b5)`;
`/home/dank/GitPackages/llama-prism/llama-server`) as a **root systemd unit**
(`scripts/frank-zealot.service`, `relink_unit`ed into
`/etc/systemd/system/`), serving `prism-ml/Ternary-Bonsai-2-27B-gguf` at
**PQ2_0** (7,206,168,928 B, sha256 `3907dc16…` = the Hub LFS oid) **plus the
publisher's own Q8_0 mmproj** (629,246,976 B), both
`/home/dank/models/Bonsai2-PQ2_0/`, on port 7771. Cutover 2026-10-17 from the
Qwen3.5-9B-Uncensored Q6_K era (same shape).
`0.2.0-dev (build 10777, commit e3de71522)`;
`/home/dank/GitPackages/llama-prism-b10777/llama-server`) as a **root systemd
unit** (`scripts/frank-zealot.service`, `relink_unit`ed into
`/etc/systemd/system/`), serving `prism-ml/Ternary-Bonsai-2-9B-gguf` at
**PQ2_0 with the MTP head IN the file** (2,675,410,720 B, Hub-LFS sha256
`c79fcdfa…` verified at fetch) **plus the publisher's own Q8_0 mmproj**
(624,229,664 B, sha256 `e0cb6f69…`), both `/home/dank/models/Bonsai2-9B-MTP/`,
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
`general.file_type 141` (PQ2_0, 2.13 bpw, group 128) with **402 tensors of the
custom GGML type 142** plus `prism.hadamard.*` metadata (normalized
Sylvester–Walsh transform, block 1024, 401 named weights, inverse on
`token_embd`, `gdn_v_grouped`). Stock llama.cpp refuses type 142 at load, and a
loader that ignored the Hadamard metadata would dequant wrongly — same lock class
as the ROCmFPX era. Retargeting the payload without the fork ships an endpoint
that cannot load, so the seam pins both paths.
**The runtime is part of the payload identity — twice over.** The file is
`general.file_type 141` — **202 of the 442 tensors carry the fork's PQ2_0 type**
(the rest are f32 norms/scalars plus 8 q8_0 in the MTP block) — beside
`prism.hadamard.*` metadata (normalized Sylvester–Walsh transform): stock
llama.cpp refuses the custom type at load, and a loader that
ignored the Hadamard metadata would dequant WRONG — same lock class as the
ROCmFPX era. On top of that lock, **the fork gates MTP beside a quantized V
cache on build `prism-b10776-d00e252` or newer**, which is why the prebuilt
moved 10754 → 10777; the 10754 tree stays on disk as the 27B rollback runtime.
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
blocks**, `full_attention_interval = 4` ⇒ **16 full-attention + 48 Gated
DeltaNet layers**, 24 attention heads / **4 KV heads** × 256 dim, embedding
5,120, ffn 17,408, `ssm` conv_kernel 4 / state_size 128 / group_count 16 /
time_step_rank 48 / inner_size 6,144, native 262,144, vocab 248,320. **KV
accounting:** the pool scales with the 16 full layers — 16 × 4 × 256 = 16,384
elements/token per cache, so **q8_0 K + q8_0 V bills 34 KiB/token** = 13,328
MiB at the 401,408 pool (ledger-confirmed to the MiB), while **f16 KV needs
25,088 MiB and cannot boot on a 24,123 MiB card** (measured `cudaMalloc` OOM).
The 48 DeltaNet layers bill a fixed recurrent state per SLOT — ~150 MiB each —
so pool arithmetic alone under-prices the shape.
**Architecture: qwen35 hybrid** — read from the GGUF's own header: `block_count`
**33 = 32 trunk layers + the `blk.32` MTP block** (the MTP block has its own
attention + FFN in q8_0, which is why the draft context bills real KV), and
`qwen35.full_attention_interval = 4` ⇒ **8 full-attention + 24 Gated
DeltaNet**, 4 KV heads × 256 dim, native 262,144. **KV accounting:** the pool
scales with the 8 full layers — 8 × 4 × 256 = 8,192 elements/token per cache,
so **q8_0 K + q8_0 V bills 17,408 B/token ≈ 17 KiB** = **13,056.00 MiB** at the
786,432 pool (ledger-exact) — exactly HALF the 27B's 34,816 B/token, which is
the whole reason three native windows fit where the 27B afforded only two
shortened ones. **f16 K would bill 26,112 MiB and cannot boot on a 24,123 MiB
card.** The 24 DeltaNet layers bill a fixed recurrent state per SLOT —
`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
conversations of 200,704 (Frank's cap; the harness `contextWindow` quotes
the per-slot number), `--flash-attn on`, **q8_0 K + q8_0 V**, batch 4096 /
**ubatch 2048** (the measured CUDA optimum carried from the MiMo/Swift A/Bs on
this card), `--log-verbosity 4`, `--mmproj` for vision, **speculative decoding
OFF** (`SPEC_TYPE=none`). Boot: **22,664 MiB used / 1,461 free**; the
projection (21,439) excludes the projector's 873.10 MiB worst-case, which
lands AFTER the fit check — read settled `nvidia-smi`, never the projection.
Measured: 76.6 t/s single-stream decode (the 9B ran 94.4), 1,258 t/s prefill
at 35k, 691.7 t/s at 166k, 109.4 t/s aggregate across two concurrent streams.
The **first request after boot is a warmup artifact** (2.6 t/s prefill / 37.7
t/s decode measured) — never report it.
**The shape (2026-10-18):** `--ctx-size 786432 --parallel 3` = THREE
conversations of 262,144, every one at the model's TRAINED window (Frank: "if
you can possibly fit a third context, do it"), `--flash-attn on`, **q8_0 K +
q8_0 V**, batch 4096 / **ubatch 2048** (the measured CUDA optimum carried from
the MiMo/Swift A/Bs on this card; it also sets the compute-buffer size, which
is what decides whether MTP fits), `--log-verbosity 4`, `--metrics`,
`--jinja`, `--mmproj` for vision, **speculative decoding OFF**
(`SPEC_TYPE=none`). Boot: **17,876 MiB used / 6,249 free**; compute 1,409 MiB
CUDA + 1,201 MiB CUDA_Host, projector 595 + 248 MiB. Measured on the same
boot: **167.2 t/s** greedy decode, 158.9 t/s single-stream chat, **280.2 t/s
aggregate across three concurrent streams**, **2,877 t/s prefill at 106,418**
tokens — the 27B record (76.6 decode / 109.4 aggregate / 691.7 prefill at 166k)
beaten on every axis. The **first request after boot is a warmup artifact**
(37.7 t/s class) — never report it.
**Why MTP is off**: the PQ2_0 export carries no MTP head (upstream ships a
separate `…-CRACK-MTP-GGUF`, +451 MB, for that), and MTP is a property of the
file, not of the flags.
**Why MTP is off even though the head is in the file.** The drafter is GOOD —
acceptance 0.798 / 0.617 per position, mean accepted length 2.41 — and the card
recommends depth 2. It still loses AT THIS SHAPE because the draft context
bills its OWN **f16** KV over the WHOLE pool (4,096 B/position × 786,432 =
**3,072.00 MiB**, 4,830 MiB with its compute buffers) and ubatch 2048 refuses to
give it up. Booted arms, same card, same traffic (2026-10-18):
**Vision**: the publisher's own Q8_0 mmproj via `--mmproj` (Qwen3-VL tower: 27
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
OpenAI `image_url` content part reaches the model. **New trap:** thinking
tokens count against `max_tokens`, so a vision probe at 64 tokens returns
EMPTY `content` with `finish_reason: "length"` — budget ≥512 or the endpoint
looks blind while it is only mid-thought.
| slots | spec | ubatch | used / free | decode |
|---|---|---|---|---|
| 3 × 262,144 | off | 2048 | 17,876 / 6,249 | **167.2** |
| 3 × 262,144 | on | 2048 | 22,706 / 1,419 | 109.2 |
| 3 × 262,144 | on | 1024 | 21,400 / 2,725 | **213.5** |
| 2 × 262,144 | on | 2048 | 17,178 / 6,947 | 213.2 |
| 1 × 262,144 | on | 2048 | 11,652 / 12,473 | 214.2 |
**Base alignment**: Bonsai-2 is the censored BASE lineage (the 9B it replaced
was uncensored). The CRACK re-export on disk
(`/home/dank/models/Bonsai2-CRACK/`, header census proves byte-identical
geometry) is a one-line `MODEL_FILE` swap if a refusal ever surfaces.
**MTP is worth +28 % decode whenever the card has room**; the squeeze, not the
drafter, is what halves it at three windows. The arm that WOULD run it at three
windows is `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` (213.5 t/s single-stream,
250.8 three-stream aggregate) and pays prefill −17 % and 2.7 GiB of headroom on
a card whose projector worst-case lands AFTER the engine's own fit check. This
endpoint is the fleet's compaction hub — prefill-bound, 2–3 concurrent
subagents — so the shipped arm takes the +17 % prefill (2,877 vs 2,395 t/s) and
the +12 % three-stream aggregate (280.2 vs 250.8) over single-stream speed, and
keeps 6.2 GiB free. `SPEC_TYPE=none` strips the WHOLE spec set (`--spec-type`
and every `--spec-draft-*`) — flags move as a set or the arm measures a crash
instead of an absence.
**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
`frank-archon`, `frank-dragoon` and `frank-zealot` (the 3090 endpoint) here — 200,704 is the
only window in the fleet that fits a worst-case compaction input (~170k
tokens). With `--parallel 2` a compaction request no longer serializes behind
a live generation (the Gemma4 era's `--parallel 1` law). Retrieval through a
`frank-archon`, `frank-dragoon` and `frank-zealot` (the 3090 endpoint) here —
262,144 (since the 2026-10-18 reslice) is the widest window in the fleet and
the only one that comfortably fits a worst-case compaction input (~170k
tokens). With `--parallel 3` a compaction request no longer serializes behind a
live generation (the Gemma4 era's `--parallel 1` law). Retrieval through a
25,960-token prompt was verified exact after the swap.
**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
`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
read.
read. `--metrics` is required or the Prometheus scrape reads 501 = DOWN.
Runbook:
`serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai2-pq20.md`.
Rollback: revert the swap commit — the Qwen3.5-9B Q6_K payload
`serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai9-mtp.md`.
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
(`~/GitPackages/llama-master/build-cuda/`), the Gemma4 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 ->
36:29), which is the practical size of the effect. See
[[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 —
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
@@ -1185,15 +1246,16 @@ Active:
| 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-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` |
| 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` |
Retired (rollback targets and history):
| 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` |
| 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` |
+25 -17
View File
@@ -1225,24 +1225,31 @@ deploy_llm_dragoon() {
# 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
# ~/GitPackages/llama-prism (vendor release build 10754, commit 2459f68b5 —
# the ONLY loader that registers the payload's custom tensor type 142 and
# reads its prism.hadamard.* metadata), 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.
# ~/GitPackages/llama-prism-b10777 (vendor release prism-b10777-e3de715,
# `--version` = 0.2.0-dev build 10777 commit e3de71522 — the ONLY loader class
# that registers the payload's custom tensor type 142 and reads its
# prism.hadamard.* metadata, and since 2026-10-18 ALSO the minimum build for
# this endpoint: the card gates quantized V alongside MTP on
# `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
# 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_RUNTIME="${LLM_ZEALOT_RUNTIME:-/home/dank/GitPackages/llama-prism/llama-server}"
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-b10777/llama-server}"
# 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,
# 768 px, qwen3vl_merger, 629,246,976 B) is the whole image path — a deploy
# that starts without it silently ships a text-only endpoint. Checked
# unconditionally, like frank-dragoon's. Its 873.10 MiB worst-case VRAM 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-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf}"
# 768 px, qwen3vl_merger, projection_dim 4096, 624,229,664 B) is the whole
# image path — a deploy that starts without it silently ships a text-only
# endpoint. Checked unconditionally, like frank-dragoon's. Q8_0 is MANDATORY,
# not a preference: the card says the BF16 tower can crash llama-server when
# several image requests run at once, and this endpoint runs --parallel 3. Its
# 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() {
# 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.
if [[ ! -x "$LLM_ZEALOT_RUNTIME" ]]; then
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: commit 2459f68b5) into ~/GitPackages/llama-prism first —" >&2
echo "frank-zealot: stock llama.cpp refuses the payload's custom tensor type 142." >&2
echo "frank-zealot: fetch the PrismML fork release build (prism-b10777-e3de715," >&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, and" >&2
echo "frank-zealot: builds older than b10776 cannot pair MTP with a quantized V cache." >&2
return 1
fi
for f in "$LLM_ZEALOT_MODEL_FILE" "$LLM_ZEALOT_MMPROJ"; do
if [[ ! -f "$f" ]]; then
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
return 1
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
# prism-ml/Ternary-Bonsai-2-27B-gguf at PQ2_0 on the RTX 3090, :7771.
# frank-zealot — llama.cpp (PrismML fork build 10777, CUDA) serving
# 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
# ternary checkpoint at the SAME shape Frank asked for — TWO 200,704-token
# conversations — with q8_0 K + q8_0 V (f16 is arithmetically impossible here,
# see the KV law), MTP off (the file carries no nextn/mtp tensors), verbosity 4.
# 2026-10-18 (Frank): swap from the Bonsai-2-**27B** PQ2_0 to the 9B MTP export
# at THREE 262,144-token conversations (Frank: "if you can possibly fit a third
# context, do it"). The 9B bills HALF the KV of the 27B per token, which is what
# funds 3 x the native window on one 24,123 MiB card. Speculative decoding ships
# OFF — measured, not assumed; see the MTP law below, which is the whole story
# and the reason the publisher's "depth 2 works best" line does not transfer.
#
# 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.*
# metadata (normalized-Sylvester-Walsh transform, block 1024, 401 named
# weights, inverse on token_embd, gdn_v_grouped). Stock llama.cpp refuses type
# 142 at load; only the PrismML fork registers the type AND reads the Hadamard
# metadata. The vendor prebuilt build 10754 (commit 2459f68b5) is what every
# number in this file was measured on; sha256 12ae5baba033d8cd…
LLAMA_BIN=/home/dank/GitPackages/llama-prism/llama-server
MODEL_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0.gguf
MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf
# The CRACK re-export (dealignai/Bonsai-2-27B-Ternary-CRACK-GGUF, uncensored,
# already on disk at /home/dank/models/Bonsai2-CRACK/) is a ONE-LINE swap: the
# header census proves byte-identical geometry (851 tensors, same
# {pq2_0:402, f32:353, bf16:96} histogram, 64 blocks, vocab 248,320, same
# 8,952-char chat template) and the official file's sha256 is the Hub LFS oid.
# Bonsai-2 is the BASE (censored) Qwen3.8 family; if a compaction or subagent
# request ever comes back with a refusal, point MODEL_FILE at the CRACK file
# and restart — the VRAM ledger below does not move.
# carrying custom GGML type 142 tensors plus prism.hadamard.* metadata
# (normalized-Sylvester-Walsh transform); stock llama.cpp refuses type 142 at
# load and a loader that ignored the Hadamard metadata would dequant WRONG. On
# top of that lock, MTP beside a quantized V cache needs fork build >= b10776,
# so the vendor prebuilt moved from 10754 (commit 2459f68b5, the 27B era) to
# 10777 (commit e3de71522). The 10754 tree stays on disk as the rollback
# runtime for the 27B payload.
LLAMA_BIN=/home/dank/GitPackages/llama-prism-b10777/llama-server
# 2,675,410,720 B, Hub LFS sha256 c79fcdfaf6b7… (verified at fetch by
# scripts/download-frank-zealot-bonsai9.sh). This IS the MTP build: the draft
# head ships INSIDE the checkpoint, so MTP availability is a property of the
# FILE, never of a flag. The plain PQ2_0 sibling could not run it at any flag
# value; loading this file instead of that one is the only change that matters.
MODEL_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf
# 624,229,664 B, Hub LFS sha256 e0cb6f69… — Q8_0 IS MANDATORY, not a preference:
# the publisher's card says the BF16 mmproj "can crash llama-server when several
# image requests run at once; Q8_0 is not affected", and this endpoint runs
# --parallel 3, i.e. exactly that shape. The projector's worst case lands AFTER
# 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
# 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
BIND_ADDR=0.0.0.0
LISTEN_PORT=7771
# CTX_SIZE is the POOL llama.cpp divides across slots: 401,408 / 2 = 200,704
# per conversation. MUST stay divisible by PARALLEL (the seam enforces it) and
# the harness contextWindow quotes the PER-SLOT number — a longer prompt is
# answered with a 400, never truncated. The model's native window is 262,144;
# 2 × 262,144 at q8_0 projects 25,758 MiB and does NOT fit this card (measured
# f16 analogue: 41,118). One 262,144 slot fits with 5,995 MiB free but
# serializes compaction, so the pair stays.
CTX_SIZE=401408
PARALLEL=2
# CTX_SIZE is the POOL llama.cpp divides across slots: 786,432 / 3 = 262,144 per
# conversation — each slot at the model's TRAINED window, which is the point of
# the swap (the 27B era had to cut each conversation to 200,704 to fit two).
# MUST stay divisible by PARALLEL (the seam enforces it) and the harness
# contextWindow quotes the PER-SLOT number — a longer prompt is answered with a
# 400, never truncated. The ceiling cannot go past 3 x 262,144: that is the
# native wall (no YaRN in this path) and the arithmetic agrees — a 4th slot
# would push each below the compaction contract.
CTX_SIZE=786432
PARALLEL=3
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
# KV law (from the GGUF header, then confirmed by the boot ledger): 16
# full-attention layers (full_attention_interval 4 over 64 blocks) × 4 KV heads
# × 256 dim = 16,384 elements per token per cache. q8_0 bills 17,408 B per
# cache ⇒ 34,816 B/token = 34 KiB, so the 401,408 pool is 13,328.00 MiB — the
# ledger said exactly that. f16 would bill 25,088 MiB and the boot DIES:
# `cudaMalloc failed: out of memory` on the 25,088.00 MiB request, engine fit
# projection 33,198 vs 23,558 free. The 48 DeltaNet layers bill a fixed
# recurrent state per SLOT — ~150 MiB each (context 13,627 − KV 13,328 = 299
# for two slots), which is why pool arithmetic alone under-prices the shape.
# KV law (header + boot ledger, both confirmed): qwen35 hybrid, 32 layers with
# qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated DeltaNet;
# 4 KV heads × 256 dim = 8,192 elements per token per cache. q8_0 bills 8,704 B
# per cache ⇒ 17,408 B/token = 17 KiB, so the 786,432 pool is 13,056.00 MiB —
# the ledger said exactly that. HALF the 27B's 34,816 B/token, which is the
# whole reason three native windows fit. f16 would bill 26,112 MiB at this pool
# (against a 24,123 MiB card) and dies at cudaMalloc; the 24 DeltaNet layers
# bill a fixed recurrent state per SLOT instead of per token — the ledger's
# "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_V=q8_0
# Boot ledger (2026-10-17, this exact shape, nvidia-smi settled):
# 22,664 MiB used / 1,461 MiB free of 24,123
# fit projection 21,439 = 6,539 model + 13,627 context + 1,272 compute
# the mtmd projector's 873.10 MiB worst-case is NOT inside that projection —
# it lands after the fit check, which is the whole difference between
# "2.6 GiB free on paper" and 1.4 GiB free in reality. Watch it.
# Fallback rung if the margin is ever eaten (desktop, co-tenant): q4_0 KV
# measured 16,392 used / 7,733 free at the same 2 × 200,704.
# 1024 is the ubatch fallback (−616 MiB, measured on the 9B predecessor at
# this card/arch; 2048 was the measured CUDA optimum, +4.6 % prefill).
# SPECULATIVE DECODING: OFF, MEASURED. The head is in the file and the drafter
# is GOOD (acceptance 0.798/0.617 per position, mean accepted length 2.41) — it
# still loses, because the draft context bills its OWN f16 KV over the WHOLE
# pool: 4,096 B/position × 786,432 = 3,072.00 MiB, plus its compute buffers,
# 4,830 MiB all told. Booted arms, same card, same traffic (2026-10-18):
# 3 × 262,144 spec OFF ub 2048 → 17,876 used / 6,249 free, decode 167.2 t/s
# 3 × 262,144 spec ON ub 2048 → 22,706 used / 1,419 free, decode 109.2 t/s
# 3 × 262,144 spec ON ub 1024 → 21,400 used / 2,725 free, decode 213.5 t/s
# 2 × 262,144 spec ON ub 2048 → 17,178 used / 6,947 free, decode 213.2 t/s
# 1 × 262,144 spec ON ub 2048 → 11,652 used / 12,473 free, decode 214.2 t/s
# i.e. MTP is worth +28 % decode whenever the card has room, and at the 3-slot
# pool it costs 1.3 GiB of compute buffers that ubatch 2048 refuses to give up —
# the squeeze, not the drafter, is what halves decode. The arm that WOULD run
# MTP at three windows is SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024 (decode 213.5,
# 3-stream 250.8 aggregate) and pays prefill −17 % and 2.7 GiB of headroom on a
# card whose projector worst-case lands after the fit check. This endpoint's
# load is the fleet's COMPACTION HUB (prefill-bound, ~170k in) plus 2-3
# concurrent subagents, so the deployed arm takes the +17 % prefill
# (2,877 vs 2,395 t/s at 106,418 tokens) and the +12 % three-stream aggregate
# (280.2 vs 250.8) over single-stream speed, and keeps 6.2 GiB free.
# 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_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
# 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
# and graph capture land in the journal, which is what the VRAM-fit forensics
# read. Levels above 4 flood the journal.
+8 -1
View File
@@ -1,5 +1,5 @@
[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
Wants=network-online.target
@@ -12,6 +12,13 @@ Group=dank
LimitMEMLOCK=infinity
Environment="FRANK_ZEALOT_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-zealot.env"
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
RestartSec=10
+98 -53
View File
@@ -1,30 +1,42 @@
#!/usr/bin/env bash
# Serve prism-ml/Ternary-Bonsai-2-27B (PQ2_0 ternary quant) as frank-zealot on
# Dankclaw's RTX 3090 via llama.cpp (PrismML vendor build 10754, CUDA sm_86).
# Serve prism-ml/Ternary-Bonsai-2-9B-gguf (PQ2_0 ternary quant WITH the MTP head
# 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
# (--ctx-size 401408 --parallel 2) with q8_0 K + q8_0 V — f16 KV does not fit
# this geometry: the engine's own fit projected 33,198 MiB against 23,558 free
# and cudaMalloc failed on the 25,088 MiB pool. MTP stays off: this GGUF
# carries no MTP tensors (verified from its tensor-name list). Verbosity 4
# ships so the VRAM-fit forensics (KV layout, buffer sizes, graph capture)
# land in the journal.
# 2026-10-18 (Frank): Bonsai-2 27B PQ2_0 → the 9B MTP export at THREE
# 262,144-token conversations (--ctx-size 786432 --parallel 3), q8_0 K + q8_0 V.
# The 9B bills 17,408 B/token against the 27B's 34,816 (8 full-attention layers
# instead of 16), and that halved rate is what funds three NATIVE windows where
# the 27B could only afford two cut to 200,704. Verbosity 4 ships so the
# VRAM-fit forensics (KV layout, buffer sizes, graph capture) land in the
# journal.
#
# Architecture: qwen35 — 27B, 64 blocks with full_attention_interval 4 (16
# full-attention layers + 48 Gated DeltaNet linear layers), 24 heads / 4 KV
# heads × 256 dim, native 262,144, vocab 248,320. The KV pool scales with the
# 16 full layers: 34 KiB/token at q8_0 K + q8_0 V (13,328 MiB at the 401,408
# pool); the DeltaNet layers cost a fixed recurrent state per SLOT (~150 MiB —
# boot-ledger context minus KV). Boot ledger: 22,664 used / 1,461 free, with
# the Q8_0 projector's 873 MiB landing AFTER the engine's fit projection.
# The runtime is a hard lock, not a preference: the file carries 402 tensors
# of custom GGML type 142 plus prism.hadamard.* metadata, which only the
# vendor build registers — stock llama.cpp refuses it at load.
# Vision comes from the publisher's Q8_0 projector, so --mmproj is what gives
# this endpoint eyes; MMPROJ_FILE="" is the documented text-only opt-out.
# Architecture (engine ledger, not the marketing page): qwen35 hybrid, 32
# layers with qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated
# DeltaNet, 4 KV heads × 256 dim, native 262,144. The KV pool scales with the 8
# full layers (13,056.00 MiB at the 786,432 pool, ledger-exact); the DeltaNet
# layers cost a fixed recurrent state per SLOT — 452.25 MiB for 3 cells, ~151
# MiB each. The runtime is a hard lock, not a preference: custom GGML type 142
# plus prism.hadamard.* metadata, which only the vendor build registers, and
# MTP beside quantized V needs build ≥ b10776 — so the prebuilt moved 10754 →
# 10777 with the payload. Vision comes from the publisher's Q8_0 projector (the
# BF16 one can crash the server on concurrent images, and this endpoint runs
# three slots), so --mmproj is what gives this endpoint eyes; MMPROJ_FILE="" is
# the documented text-only opt-out.
#
# Sampling: llama.cpp takes it per request (the fleet harness sends the file's
# own template defaults from models.yml: thinking temp 1.0 / top_p 0.95 / top_k 20).
# Speculative decoding ships OFF, measured: the head loads and accepts well
# (0.798/0.617 per position, mean length 2.41) but the draft context bills its
# own f16 KV over the WHOLE pool — 4,096 B/position × 786,432 = 3,072 MiB plus
# buffers — and at ubatch 2048 that squeeze HALVES decode (167.2 → 109.2 t/s).
# frank-zealot.env carries the six-arm table and the arm that turns it back on
# (--ubatch-size 1024, decode 213.5 t/s) with its price.
#
# 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.
set -euo pipefail
@@ -39,45 +51,62 @@ if [[ -f "$ENV_FILE" ]]; then
fi
# --- defaults (used when the env file does not set them) -------------------
: "${LLAMA_BIN:=/home/dank/GitPackages/llama-prism/llama-server}"
: "${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}"
: "${MODEL_FILE:=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf}"
: "${ALIAS:=frank-zealot}"
: "${BIND_ADDR:=0.0.0.0}"
: "${LISTEN_PORT:=7771}"
# ctx-size is the POOL llama.cpp divides across slots: 401,408 / 2 = 200,704
# per conversation, inside the model's trained 262,144. The harness
# ctx-size is the POOL llama.cpp divides across slots: 786,432 / 3 = 262,144
# per conversation — every slot at the model's trained window. The harness
# contextWindow MUST quote the PER-SLOT number — a longer prompt is answered
# with a 400, never truncated.
: "${CTX_SIZE:=401408}"
: "${PARALLEL:=2}"
: "${CTX_SIZE:=786432}"
: "${PARALLEL:=3}"
# Compute-buffer geometry. Ubatch is a prefill knob (decode never moves).
# 2048 is the measured CUDA optimum (MiMo era, same arch/shape: +4.6 % prefill
# at 8.9k depth, decode flat, 616 MiB). The logical batch is VRAM- and
# throughput-neutral at a fixed ubatch.
# 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}"
: "${UBATCH_SIZE:=2048}"
# Quantized KV: q8_0 K + q8_0 V — the only pair that funds 2 × 200,704 on this
# card. 34 KiB/token at this geometry (16 full-attention layers × 4 KV heads ×
# 256 dim); 13,328 MiB at the 401,408 pool. f16 measured OOM; q4_0 is the
# fallback rung (16,392 used / 7,733 free). Explicit either way so the contract
# is visible in the argv, and --flash-attn stays required for quantized V
# (no true fp8 KV dtype exists in this build).
# Quantized KV: q8_0 K + q8_0 V — the only pair that funds 3 × 262,144 on this
# card. 17 KiB/token at this geometry (8 full-attention layers × 4 KV heads ×
# 256 dim); 13,056.00 MiB at the 786,432 pool, ledger-exact. f16 would bill
# 26,112 MiB against a 24,123 MiB card and dies at cudaMalloc; q4_0 is the
# fallback rung. Explicit either way so the contract is visible in the argv,
# and --flash-attn stays required for quantized V (no true fp8 KV dtype exists
# in this build).
: "${CACHE_TYPE_K:=q8_0}"
: "${CACHE_TYPE_V:=q8_0}"
# Speculative decoding is OFF: this PQ2_0 file carries NO nextn/mtp tensors, so
# there is no in-file head (the law: MTP is a property of the file, not of the
# flags). SPEC_TYPE=none strips BOTH speculative flags (flags move as a set);
# an MTP-bearing CRACK re-export exists upstream (ManniX-ITA, +451 MB) if
# decode ever needs it — that is a new payload with its own VRAM bill, never a
# flag flip on this file.
# Speculative decoding defaults OFF, measured on this card and shape: the head
# IS in the checkpoint (qwen35.nextn_predict_layers = 1 — MTP is a property of
# the FILE, so the plain PQ2_0 sibling could not run it at any flag value), it
# loads, and it accepts well (mean accepted length 2.41) — but the draft context
# bills its own f16 KV over the WHOLE pool (4,096 B/position, 3,072 MiB here)
# and at ubatch 2048 that squeeze halves decode: 167.2 → 109.2 t/s. There is no
# --spec-draft-cache-type knob in this build to make the draft cheap, and no
# --spec-draft-model either: the draft rides the main model's weights. Card gate:
# quantized V alongside MTP needs fork build >= prism-b10776, which is why
# LLAMA_BIN moved to the 10777 prebuilt; on an older build the pair is illegal.
# 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_DRAFT_N_MAX:=3}"
: "${SPEC_DRAFT_N_MAX:=2}"
: "${SPEC_DRAFT_N_MIN:=0}"
: "${SPEC_DRAFT_P_MIN:=0}"
: "${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}"
# Log verbosity threshold (llama.cpp -lv). 3 is the engine default (info);
# 4 adds debug detail (KV layout, buffer sizes, graph capture) which is what
# the VRAM-fit forensics need. Levels above 4 flood the journal.
: "${VERBOSITY:=3}"
# the VRAM-fit forensics need — the q8_0 KV contract and the draft-KV bill are
# only provable from this log. Levels above 4 flood the journal.
: "${VERBOSITY:=4}"
# /metrics needs the flag: llama.cpp returns 501 unless the server starts with
# --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
@@ -89,7 +118,7 @@ fi
: "${METRICS:=1}"
# Vision projector. No colon on purpose: an explicitly empty value means
# "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
# 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)
fi
# Speculative flags move as a set: they reach the argv ONLY when a drafter
# is named. SPEC_TYPE=none (the shipped contract) strips both — a no-spec
# baseline must not carry half a spec pair.
# Speculative flags move as a SET: they reach the argv ONLY when a drafter is
# named, and all of them together — a no-spec baseline must not carry half a
# 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
EXTRA_ARGS+=(--spec-type "$SPEC_TYPE" --spec-draft-n-max "$SPEC_DRAFT_N_MAX")
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")
EXTRA_ARGS+=(--spec-type "$SPEC_TYPE"
--spec-draft-n-max "$SPEC_DRAFT_N_MAX"
--spec-draft-n-min "$SPEC_DRAFT_N_MIN"
--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
# 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
# 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 |
|------|-------|
| 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. |
| 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) |
| Persistence | Quadlet unit `open-webui.service`; `[Install] WantedBy=default.target` |
| 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)
plus the `model_ids` of each **enabled** OpenAI connection, NOT from live
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 |
| 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`
(context length 200,704 = the per-slot window; description since 2026-10-17:
Ternary-Bonsai-2-27B PQ2_0 + Q8_0 vision mmproj — the row's `description` is
live DB state, so a payload swap must re-key it in the database too, not only
in the repo).
(context length 262,144 = the per-slot window since 2026-10-18; description
re-keyed the same day to Ternary-Bonsai-2-9B PQ2_0 + MTP + Q8_0 vision mmproj —
the row's `description` is live DB state, so a payload swap must re-key it in
the database too, not only in the repo).
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
@@ -66,15 +77,22 @@ Two gotchas make the wiring non-obvious:
OpenAI `{"data":[{"id":...}]}` shape, so connection discovery silently misses
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
builds the model list directly and never fetches `/v1/models`. frank-medium
(vLLM) serves the OpenAI shape and would discover fine, but uses `model_ids`
too for consistency.
- **The host's own services are reached via `host.containers.internal`.** The
container sits on a netavark bridge; it can hairpin to a *different* host's
LAN IP (dankserver `192.168.0.235`) but not to its *own* host's non-published
ports via the host LAN IP (`192.168.0.143:7770/7771` is refused).
`host.containers.internal` (169.254.1.2) resolves to the host on the bridge
and reaches both llama.cpp services.
builds the model list directly and never fetches `/v1/models`. The old
`frank-medium` (vLLM) endpoint — retired, no longer in the fleet — served the
OpenAI shape and would have discovered fine, but used `model_ids` too.
- **The host's own services are reached via the host LAN IP, not
`host.containers.internal`.** The container sits on the pinned kernel bridge
`systemd-open-webui` (10.89.3.0/24, gateway 10.89.3.1, container IP 10.89.3.2 —
`podman inspect open-webui` 2026-10-18), and `host.containers.internal`
(169.254.1.2) is a **slirp4netns/pasta-only name that does not resolve on this
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`).
@@ -140,8 +158,14 @@ role (`ui.default_user_role=pending`), which cannot chat. Verified
`GET /api/v1/models/list`, `GET /api/v1/models` and
`POST /api/chat/completions` all return **401** with no key (also with a
bogus bearer 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.
`POST /api/chat/completions` all return **401** with no key (also with a bogus bearer
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
@@ -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
(`ENABLE_WEB_SEARCH`, `WEB_SEARCH_ENGINE=searxng`, `SEARXNG_QUERY_URL`).
- **Access path:** the backend calls `http://192.168.0.143:8081/search?format=json`
directly — gluetun's published port, NOT the `search.franksdank.cloud` vhost.
- **Access path:** the quadlet sets `SEARXNG_QUERY_URL=http://192.168.0.143:8081/search`
(`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 +
session flow); a plain HTTP client cannot solve it, and the internal port
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
serves `:8188`.
Endpoint: `POST /api/images/generations` (image `0.11.x`: upstream moved the
route to `POST /api/v1/images/generations`; the `comfyui` engine is still
upstream) with `Authorization: Bearer <token>` and body
Endpoint: `POST /api/v1/images/generations` — measured on the running build
(`0.11.4`): the legacy `POST /api/images/generations` answers **405** while the `/v1`
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
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 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.
- 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
@@ -1,7 +1,7 @@
---
type: reference
title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork), Bonsai-2-27B PQ2_0, 2 x 200704, q8_0 KV (ACTIVE)"
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."
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 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:
- llm
- 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
Live since 2026-10-17, replacing the
[[frank-zealot-llama-qwen35-9b-q6k-2x200704]] era (Qwen3.5-9B Q6_K, exact f16
KV, same 2 × 200,704 shape). The trade: the ternary 27B costs ~3.4× the weights
of the 9B and buys a 3× larger, stronger base at the SAME window contract —
paid for by quantizing KV (f16 KV is arithmetically impossible for this
payload on this card, measured below).
**RETIRED 2026-10-17 → 2026-10-18** (AGENTS.md Runbook Index), superseded by
[[frank-zealot-llama-prism-bonsai9-mtp]] — prism-ml/Ternary-Bonsai-2-**9B**
PQ2_0 **with the MTP head**, THREE conversations at the native 262,144 window
on fork build 10777. This era stays the **rollback target**: the payload
(`/home/dank/models/Bonsai2-PQ2_0/`) and the 10754 fork tree
(`~/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
@@ -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
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,
froggeric v22.5 template. Rollback: the in-house Swift1.5 w4dn8 `.rad` (on
disk); tags frank-archon-freetoken-pr587-20261005 and
frank-archon-vllm-shapeG-20261003),
froggeric v22.5 template. Rollback: revert the swap commit and re-download the
in-house Swift1.5 w4dn8 `.rad` byte-exact (purged from disk 2026-10-17; Hub
`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
(frank-dragoon, port 7770: ghcr.io/peonist-ai/halogen-flash-server @ pinned
digest serving the NATIVE v2 conversion of the abliterated
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
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
Ternary-Bonsai-2-27B at **PQ2_0** — 27B qwen35 hybrid (16 full-attention + 48
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
frank-dragoon, port 7770]
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
frank-zealot, port 7771]
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 —
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 =
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
native wall and the engine refuses more without `HALOGEN_ROPE_YARN`.
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
MODEL swap: the abliterated v2 .hgn loads only in flash-server, so the
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
(vendor 10754, commit `2459f68b5` — the only loader for the payload's custom
tensor type 142 + `prism.hadamard.*` transform) serving
prism-ml/Ternary-Bonsai-2-27B-gguf **PQ2_0** (2.13 bpw) on the RTX 3090 —
TWO 200,704-token conversations (`--ctx-size 401408 --parallel 2`), **q8_0 K
+ q8_0 V** (34 KiB/token over the 16 full layers = 13,328 MiB; the f16 pair
needs 25,088 MiB and dies), settled 22,664 used / 1,461 free, vision via the
publisher's Q8_0 mmproj (+873 MiB, charged AFTER the engine's fit check), MTP
OFF (no MTP tensors). 76.6 t/s single-stream decode, 691.7 t/s prefill at
166k, 109.4 t/s aggregate at two streams. Supersedes the Qwen3.5-9B era below.
(vendor **10777**, commit `e3de71522` — the only loader for the payload's
custom PQ2_0 tensor type + `prism.hadamard.*` transform, and since this swap
also the minimum build that pairs MTP with a quantized V cache) serving
prism-ml/Ternary-Bonsai-2-9B-gguf **PQ2_0 with the MTP head IN the file** on
the RTX 3090 — **THREE 262,144-token conversations**
(`--ctx-size 786432 --parallel 3`, every slot at the window the model was
trained at), **q8_0 K + q8_0 V** (17 KiB/token over the 8 full layers =
13,056 MiB ledger-exact; the f16 pair needs 26,112 MiB and dies at
`cudaMalloc`), settled **17,876 used / 6,249 free**, vision via the
publisher's **Q8_0** mmproj (Q8_0 is the publisher's requirement for
concurrent image requests, and this runs three slots), **MTP OFF by
measurement** — the draft context bills its own f16 KV over the WHOLE pool
(3,072 MiB) and at `UBATCH_SIZE=2048` that squeeze halves decode
(167.2 → 109.2 t/s); `SPEC_TYPE=draft-mtp` + `UBATCH_SIZE=1024` is the
documented 213.5 t/s arm, declined for +17 % prefill and 6.2 GiB of headroom.
Measured: 167.2 t/s single-stream decode, 2,877 t/s prefill at 106,418,
280.2 t/s aggregate across three streams. `--metrics` rides (`METRICS=1`):
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 →
10-17** (payload + master CUDA build on disk as rollback): llama.cpp master
(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;
rebuild via the fetch + build scripts named in the fp8-native runbook)
- [[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
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
`--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
@@ -219,7 +254,9 @@ frank-zealot, port 7771]
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.
- [[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
(C++/HIP, libr4d gfx1201 kernels, digest-pinned 287 MiB image, engine as
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
single-stream, vision "Red" probe passes, native tool_calls; TP=2 does
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
rollback window closes
radiance swap for TP=2. Disk state 2026-10-18: the image is still in
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
(rollback target; the vLLM trio + env lore live in the tag)**: the
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
(~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)
- [[frank-archon-r9v-radiance-qwen38-q8]] — RETIRED 2026-09-30 (two swaps
back; payload and R9V state dirs intact on disk, git tag
[[frank-archon-r9v-radiance-qwen38-q8]] — RETIRED 2026-09-30 (two swaps
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-radiance-qwen38-uncensored]] — RETIRED 2026-09-27
(same-day swap to the Q8_0 payload; rollback target, state + payload
intact on disk): the R9V radiance-vLLM fork serving
(same-day swap to the Q8_0 payload; state + payloads no longer on disk
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) +
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
@@ -505,8 +546,8 @@ frank-zealot, port 7771]
## CIRU Reference (Dankclaw)
- [[ciru-activefpx-promptforge]] — the RETIRED v3 model, ROCm 7.15 toolchain,
and build history (on Dankclaw; the slot now serves the kingjones
ROCmFP4 model, see [[kingjones-strix-mtp]])
and build history (on Dankclaw; the gfx1151 slot now serves frank-dragoon's
flash-server v2-native payload — see [[frank-dragoon-flash-server-v2-native]])
- [[kairic-edge]] — Kairic Edge IU4 runtime: build runbook, binary
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)
- [[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)
- [[frank-zealot-llama-prism-bonsai2-pq20]] — the **ACTIVE** frank-zealot
runbook since 2026-10-17: the PrismML vendor llama.cpp build 10754 (the only
loader for GGML type 142 / `prism.hadamard.*`) serving
prism-ml/Ternary-Bonsai-2-27B-gguf **PQ2_0** on the RTX 3090 — TWO
200,704-token conversations, q8_0 K + q8_0 V (f16 OOMs at 2×200,704: 34 KiB
vs 64 KiB per token over 16 full-attention layers), MTP **off** (no head in
the file), vision through the Q8_0 mmproj; boot 22,664 used / 1,461 free
- [[frank-zealot-llama-prism-bonsai9-mtp]] — the **ACTIVE** frank-zealot
runbook since 2026-10-18: the PrismML vendor llama.cpp build 10777 (the only
loader for the custom PQ2_0 tensor type / `prism.hadamard.*`, and the floor
for MTP beside a quantized V cache) serving
prism-ml/Ternary-Bonsai-2-9B-gguf **PQ2_0 with the MTP head** on the RTX 3090
— THREE 262,144-token conversations, q8_0 K + q8_0 V (f16 OOMs at this pool:
26,112 vs 13,056 MiB over 8 full-attention layers), MTP **off by
measurement** (the draft context bills 3,072 MiB of f16 KV over the whole
pool), vision through the Q8_0 mmproj; boot 17,876 used / 6,249 free
- [[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
target, payload on disk): llama.cpp master (CUDA sm_86) serving
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.
bats 371/371. frank-dragoon and frank-zealot were live the whole window; only
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": {
"unit": "zealot",
"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!",
"sfx": [
"zealot-honor.ogg"
],
"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": [
"text",
"image"
+101 -52
View File
@@ -1,14 +1,17 @@
# Seam: the frank-zealot llama.cpp root unit on dankclaw.
# frank-zealot is the RTX 3090 endpoint; since 2026-10-17 it serves
# prism-ml/Ternary-Bonsai-2-27B-gguf at PQ2_0 — the ternary Bonsai-2-27B, a
# qwen35 hybrid with 64 blocks (16 full-attention + 48 Gated DeltaNet, no
# nextn/mtp tensors in the file) — via the PrismML fork build of llama.cpp
# (vendor build 10754), the ONLY loader that registers the payload's custom
# tensor type 142 and honours its prism.hadamard.* metadata: TWO 200,704-token
# conversations (--ctx-size 401408 --parallel 2), q8_0 K + q8_0 V under flash
# attention (f16 KV does not fit this card at this pool — the boot dies on a
# 25,088 MiB cudaMalloc), the publisher's own Q8_0 vision projector, and MTP
# off. Log verbosity 4 ships so the VRAM-fit forensics land in the journal.
# frank-zealot is the RTX 3090 endpoint; since 2026-10-18 it serves
# prism-ml/Ternary-Bonsai-2-9B-gguf at PQ2_0 — the ternary Bonsai-2-9B, a
# qwen35 hybrid with 32 layers (8 full-attention + 24 Gated DeltaNet) and the
# MTP draft head INSIDE the file — via the PrismML fork build of llama.cpp
# (vendor build 10777, the minimum that pairs MTP with a quantized V cache),
# the ONLY loader that registers the payload's custom tensor type 142 and
# honours its prism.hadamard.* metadata: THREE 262,144-token conversations
# (--ctx-size 786432 --parallel 3), q8_0 K + q8_0 V under flash attention (f16
# KV would bill 26,112 MiB of a 24,123 MiB card), the publisher's own Q8_0
# vision projector (the BF16 one can crash on concurrent images), and MTP OFF
# — measured, not because the head is missing: the draft context bills f16 KV
# over the whole pool and halves decode at this shape. Log verbosity 4 ships so
# the VRAM-fit forensics land in the journal.
# 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.
@@ -97,15 +100,16 @@ run_stage() {
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
# 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
# model but leaves the master build behind ships an endpoint that cannot
# load at all.
grep -qE '^LLAMA_BIN=/home/dank/GitPackages/llama-prism/llama-server$' "$ENV_FILE"
grep -qE '^MODEL_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0\.gguf$' "$ENV_FILE"
grep -qE '^MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0\.gguf$' "$ENV_FILE"
# make the PrismML build the only loader, and this build must be >= b10776
# because MTP beside q8_0 V is illegal below it. A retarget that moves the
# model but leaves the old build behind ships an endpoint that cannot load
# (type 142) or cannot run the documented MTP arm (the build gate).
grep -qE '^LLAMA_BIN=/home/dank/GitPackages/llama-prism-b10777/llama-server$' "$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" {
@@ -122,7 +126,7 @@ run_stage() {
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
[ "$status" -eq 0 ]
[ "$(argv_value --alias)" = "frank-zealot" ]
@@ -131,27 +135,31 @@ run_stage() {
[ "$(argv_value --model)" = "$MODEL_DIR/$MODEL_STUB" ]
[ "$(argv_value --n-gpu-layers)" -gt 0 ]
# llama.cpp divides --ctx-size across slots, so the observable contract
# is the per-slot window. Frank's 200k cap is a per-conversation cap, so
# the pair is 401,408 / 2 — and the harness contextWindow must quote
# 200704, not the pool and not the model's native 262,144.
[ "$(argv_value --parallel)" -eq 2 ]
[ "$(argv_value --ctx-size)" -eq 401408 ]
# is the per-slot window. The 9B swap exists precisely so every
# conversation gets the model's TRAINED 262,144 (the 27B had to be cut to
# 200,704 to fund two), and the harness contextWindow must quote 262144 —
# the pool and the slot count are neither, and a longer prompt is a 400.
# Three is also the ceiling: a fourth slot would push each window under the
# fleet's worst-case compaction input, which is this endpoint's job.
[ "$(argv_value --parallel)" -eq 3 ]
[ "$(argv_value --ctx-size)" -eq 786432 ]
local pool slots
pool="$(argv_value --ctx-size)"; slots="$(argv_value --parallel)"
[ $((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" {
# 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
# 16 full-attention layers × 4 KV heads × 256 dim = 16,384 elements per
# token per cache, so f16/f16 would need 25,088 MiB of the 24,123 MiB card
# (measured: cudaMalloc OOM, engine fit 33,198 vs 23,558 free) while q8_0
# bills 34,816 B/token = 13,328.00 MiB at the 401,408 pool. The boot
# ledger, not this file, is the referee. flash-attn is not decoration:
# quantized V is only legal with it, and it must stay explicit so a
# future quant swap cannot silently fall back.
# 8 full-attention layers × 4 KV heads × 256 dim = 8,192 elements per token
# per cache, so f16/f16 would need 26,112 MiB of the 24,123 MiB card while
# q8_0 bills 17,408 B/token = 13,056.00 MiB at the 786,432 pool — the
# ledger said exactly that, and it is the referee. The 24 DeltaNet layers
# bill a fixed recurrent state per slot (452.25 MiB for three cells), which
# is why pool arithmetic alone under-prices the shape. flash-attn is not
# decoration: quantized V is only legal with it, and it must stay explicit
# so a future quant swap cannot silently fall back.
local env_k env_v
env_k="$(sed -nE 's/^CACHE_TYPE_K=(.+)$/\1/p' "$ENV_FILE")"
env_v="$(sed -nE 's/^CACHE_TYPE_V=(.+)$/\1/p' "$ENV_FILE")"
@@ -166,15 +174,16 @@ run_stage() {
serve_argv
[ "$status" -eq 0 ]
# 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" ]
# And an env that names no verbosity still ships the engine default
# explicitly — the script's written-down 3, never a blank.
# And an env that names no verbosity still ships an explicit value: the
# serve script's written-down default is 4 too, never a blank.
sed -i '/^VERBOSITY=/d' "$STAGE/test.env"
run_stage
[ "$status" -eq 0 ]
[ "$(argv_value --log-verbosity)" = "3" ]
[ "$(argv_value --log-verbosity)" = "4" ]
}
@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" {
# Frank, 2026-10-07: MTP is off because it CANNOT be on — the Q6_K file
# carries no nextn/mtp tensors (verified from its tensor-name list), and
# MTP is a property of the file, not of the flags. 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).
# Frank, 2026-10-18: this file DOES carry the MTP head (the boot arms prove
# it loads and drafts, acceptance 0.798/0.617 per position, mean accepted
# length 2.41) and it is still off, because the draft context bills its own
# f16 KV over the WHOLE pool — 4,096 B/position × 786,432 = 3,072 MiB plus
# buffers — and at --ubatch-size 2048 that squeeze halves decode: 167.2 →
# 109.2 t/s. The MTP arm is SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024 (213.5
# t/s), documented in frank-zealot.env. The flags move as a set:
# 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
[ "$status" -eq 0 ]
! grep -qxF -- '--spec-type' <<<"$output"
@@ -235,27 +248,35 @@ run_stage() {
grep -qE '^SPEC_TYPE=none$' "$ENV_FILE"
}
@test "naming a drafter ships the full MTP trio" {
# The opt-back-in path (e.g. a future Qwen3.5 -mtp build): SPEC_TYPE +
# SPEC_DRAFT_FILE must reach the engine as a complete set — a draft-mtp
# flag without its model file boots a drafter against the main weights and
# drafts nothing.
@test "naming a drafter ships the MTP set, with or without a separate draft file" {
# The documented MTP arm (frank-zealot.env): SPEC_TYPE=draft-mtp ALONE is
# legal here because the head is IN the checkpoint and the draft context
# rides the main model's weights — so --spec-draft-model must NOT appear,
# or the trunk would be loaded twice.
serve_argv
{
echo 'SPEC_TYPE=draft-mtp'
echo 'SPEC_DRAFT_N_MAX=3'
echo "SPEC_DRAFT_FILE=$MODEL_DIR/$DRAFT_STUB"
echo 'SPEC_DRAFT_N_MAX=2'
} >> "$STAGE/test.env"
run_stage
[ "$status" -eq 0 ]
[ "$(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" ]
}
@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
# tensors — the publisher's projector argument IS the vision capability:
# no flag, no eyes.
# The Bonsai-2 text file carries no vision tensors — the publisher's Q8_0
# projector argument IS the vision capability: no flag, no eyes. Q8_0 (not
# BF16) is the publisher's own requirement for concurrent image requests,
# which is what --parallel 3 means in practice.
serve_argv
[ "$status" -eq 0 ]
[ "$(argv_value --mmproj)" = "$MODEL_DIR/$MM_STUB" ]
@@ -268,6 +289,34 @@ run_stage() {
! 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" {
serve_argv
[ "$status" -eq 0 ]