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.
15 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| reference | frank-zealot on Dankclaw — llama.cpp (PrismML fork 10754), Bonsai-2-27B PQ2_0, 2 x 200704, q8_0 KV (RETIRED, rollback target) | 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. |
|
2026-10-17 |
frank-zealot — llama.cpp (PrismML) Bonsai-2-27B PQ2_0, 2 × 200,704, q8_0 KV
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
| Host / device | Dankclaw (192.168.0.143), RTX 3090, 24,576 MiB (24,123 MiB visible to the engine) |
| Endpoint | http://192.168.0.143:7771 — served model id frank-zealot (ALIAS), /v1 OpenAI surface |
| Runtime | PrismML fork build /home/dank/GitPackages/llama-prism/llama-server — vendor release, --version = 0.2.0-dev (build 10754, commit 2459f68b5), sha256 12ae5baba033d8cd…. NOT a preference: see Why the fork |
| Unit | root systemd frank-zealot.service (scripts/frank-zealot.service), User=dank, Restart=on-failure, LimitMEMLOCK=infinity |
| Serve script | scripts/serve-frank-zealot.sh, tunables scripts/frank-zealot.env |
| Weights | /home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0.gguf — 7,206,168,928 B, sha256 3907dc1658db1f78a9826bf8d5bcb8dc65db0d466388937af57f2294fae62ec1 |
| Projector | Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf — 629,246,976 B (full sha256 pinned in the fetch script), Qwen3-VL tower; 873.10 MiB worst-case VRAM, loaded AFTER the engine's fit check |
| Fetch | scripts/download-frank-zealot-bonsai2.sh — -f, byte-count + sha256 verified, idempotent, --verify-only, no token on the command line |
| Seam | tests/frank_zealot_unit.bats (argv assertions against a stub engine) |
Why the fork — the format is the lock
Read from the file's own metadata, not the model card:
| Key | Value |
|---|---|
general.architecture |
qwen35 (hybrid: Gated DeltaNet + softmax attention) |
qwen35.block_count |
64, full_attention_interval 4 ⇒ 16 full-attention + 48 DeltaNet layers |
| Attention | 24 heads / 4 KV heads × key 256 / value 256; rope.dimension_sections [11,11,10,0], freq_base 1e7, partial rope dimension_count 64 |
| Widths | embedding 5,120, feed-forward 17,408 |
qwen35.ssm.* |
conv_kernel 4, state_size 128, group_count 16, time_step_rank 48, inner_size 6,144 |
| Window / vocab | context_length 262,144; vocab 248,320 (gpt2 BPE, pre qwen35), eos 248046 |
general.file_type |
141 = PQ2_0 — 2.13 bpw (group 128) |
| Tensor histogram | 851 tensors: pq2_0 ×402 (custom GGML type 142), f32 ×353, bf16 ×96 |
prism.hadamard.* |
transform normalized-sylvester-walsh-hadamard, block_size 1024, axis input-last-dimension, sign_mode explicit, 401 named weights, sign widths [5120, 6144, 17408], 28,672 sign values, inverse applied on token_embd.weight, gdn_v_grouped true |
| Sampler metadata | general.sampling.temp 1.0 / top_p 0.95 / top_k 20 |
| Chat template | 8,952 chars; reads enable_thinking, reasoning_effort ∈ {xhigh (default), medium, low — anything else is raise_exception, preserve_thinking, tools/tool_call, image + video content parts |
Stock llama.cpp cannot load this file: type 142 is not in its enum (gguf-py's
stock GGMLQuantizationType raises ValueError: 142 is not a valid… on the
same read), and even a loader that parsed it would ignore the
prism.hadamard.* dequant transform the weights depend on. Same class of lock
as the ROCmFPX custom-type era. The vendor build 10754 already carries the
September upstream fixes (AVX-512 load segfault, and a GGML_CUDA_FA_ALL_QUANTS
-capable CUDA backend — proven by q8_0 V serving under flash attention at the
correct 34,816 B/token, below).
VRAM census (2026-10-17, four arms, production stopped)
The engine's own common_memory_breakdown_print ledger + settled nvidia-smi,
all arms --n-gpu-layers 99 --flash-attn on --batch-size 4096 --ubatch-size 2048,
mmproj attached:
| Shape | KV | projected model + context + compute |
settled used / free | verdict |
|---|---|---|---|---|
| 2 × 200,704 (pool 401,408) | q8_0 / q8_0 | 21,439 = 6,539 + 13,627 + 1,272 | 22,664 / 1,461 | DEPLOYED |
| 2 × 200,704 | f16 / f16 | 33,198 = 6,539 + 25,387 + 1,272 | — | cudaMalloc failed: out of memory on the 25,088.00 MiB KV request |
| 1 × 262,144 | q8_0 / q8_0 | 16,905 = 6,539 + 8,853 + 1,512 | 18,130 / 5,995 | fits, but one slot serializes compaction |
| 2 × 262,144 | q8_0 / q8_0 | ≈25,758 (arithmetic, same terms) | — | does not fit (f16 analogue measured 41,118) |
| 2 × 200,704 | q4_0 / q4_0 | 15,167 = 6,539 + 7,355 + 1,272 | 16,392 / 7,733 | the roomy fallback rung |
KV rate law (confirmed to the MiB): 16 full layers × 4 KV heads × 256 dim =
16,384 elements/token/cache; q8_0 bills 17,408 B per cache ⇒ 34,816 B/token,
so the 401,408 pool = 13,328.00 MiB — the ledger printed exactly that
(CUDA0 KV buffer size = 13328.00 MiB). f16 doubles it to 25,088 MiB, which is
the wall. The 48 DeltaNet layers bill a fixed recurrent state per slot:
context 13,627 − KV 13,328 = 299 MiB for two slots (~150 MiB each) — pool
arithmetic alone under-prices the shape by that much.
The projector is outside the fit check. [mtmd] estimated worst-case memory usage of mmproj is 873.10 MiB is printed before fitting but not billed in it:
settled used (22,664) − projection (21,439) ≈ 1.2 GiB is that projector plus the
CUDA context. This is the difference between "2.6 GiB free on paper" and the
real 1.4 GiB — the same late-allocation failure class that killed the
DSpark drafter boot in the llama.cpp eras. Any resize must re-read the settled
nvidia-smi, never the projection.
Measured rates (greedy, Server-Timing-free /completion timings)
| Probe | Result |
|---|---|
| Short, warm, single stream (128 tok) | 76.6 t/s decode (the 9B Q6_K predecessor ran 94.4 — the ternary 27B is ~19 % slower, weight bandwidth + Hadamard dequant) |
| First request after boot | 2.6 t/s prefill / 37.7 t/s decode — warmup artifact; never report the first request (measured twice on this payload) |
| Prefill at ~35k depth | 1,258.4 t/s (27.8 s) |
| Prefill at 166,027-token depth | 691.7 t/s (240.0 s), zero errors, slot intact |
| Two concurrent streams, 256 tok each | 54.7 + 54.7 = 109.4 t/s aggregate — --parallel 2 really batches |
| Long-context retrieval (25,960-token prompt, needle at position 180 of 300 passages) | exact: "Frank keeps the fleet in git, only through it." |
Production boot (the 19:44 cutover deploy, same binary/flags/file): 75.8 t/s single-stream decode at a short prompt — within 1 % of the trial arm, so the ladder above carries to the shipped shape.
Behavior contract (probes, not assumptions)
- Reasoning lands in
reasoning_content(--reasoning-format deepseek) ✓. - Tool calls: a
get_weatherrequest returnsfinish_reason: tool_callswitharguments: {"city":"Seattle"}— valid JSON, no loop, no HTTP 500. The fork'sKNOWN_ISSUES.mdwarning about malformed/looping tool calls did not reproduce on build 10754 at this shape. - Vision: a 512 px solid-red PNG answers "Red" — but ONLY with a
thinking budget ≥ ~512 tokens. At
max_tokens: 64the request returnsfinish_reason: "length"with EMPTYcontentand half a thought inreasoning_content: thinking tokens count against the budget, so a short-budget vision probe reads as "the model is blind". Images do reach the model (a 512 px image bills ~280 prompt tokens: 322 total vs 42 for the text alone). Keep the ≥256 px probe rule from the earlier eras. - Log noise law:
/completion(text context) atVERBOSITY=4printsfailed to parse reasoning content (response_format json_schema is currently only supported with parse_reasoning=true), content will be stored in 'content' field(probe_request.cpp:789) whenever the model closes a raw prompt with ``. It is cosmetic — the full chain of thought is incontent. The OpenAI/v1/chat/completionspath parses it intoreasoning_contentcorrectly, which is the path the harness and every consumer uses. Do not diagnose this as a fault, and do not drop verbosity below 4 — the KV/buffer ledger in the journal is what VRAM forensics read. - Long raw-prompt completions can come back empty: at ~26k+ tokens a bare
/completionrequest generated 0–1 tokens with no error (the model emits an end-of-turn marker immediately, and a text context has no role frame to keep it talking). Chat traffic is unaffected; nothing in the harness posts raw/completionat depth.
Ops notes
- Trial-window law (cost one boot window on 2026-10-17): a scratch env file
must use the serve script's OWN variable names —
CACHE_TYPE_K/CACHE_TYPE_V,BIND_ADDR,NGL. An inventedKV_CACHE_TYPEis silently swallowed by the script's: "${CACHE_TYPE_K:=f16}"default and the arm boots f16, which for this payload is an OOM that looks like a model problem. /metricsneeds--metrics(fixed 2026-10-18): the fork answers 501 Not Implemented without the flag, and Dankclaw's Prometheus scrapes/metrics, soup{job="frank-zealot"}has been 0 for the whole retained metric history — the oldest sample (2026-10-11) is already 0 — while the endpoint itself answered/health200 the entire time.METRICS=1inscripts/frank-zealot.envnow ships the flag and the seam asserts it reaches the argv. halogen serves/metricsnatively, which is why that target was always green.- Base alignment: Bonsai-2 is the BASE (censored) Qwen3.8-27B lineage, so a
refusal is possible on a compaction or subagent request. The CRACK re-export
(
dealignai/Bonsai-2-27B-Ternary-CRACK-GGUF, already on disk at/home/dank/models/Bonsai2-CRACK/Bonsai-2-27B-PQ2_0-CRACK.gguf, sha2565b24ea3e…) is a one-lineMODEL_FILEswap: the header census proves byte-identical geometry — 851 tensors, the same{142:402, f32:353, bf16:96}histogram, 64 blocks, vocab 248,320, the same 8,952-char template. The VRAM ledger does not move. If a refusal ever surfaces, that is the fix; there is also an MTP-bearing CRACK re-export upstream (ManniX-ITA/…-CRACK-MTP-GGUF, 7,657,489,920 B, +451 MB head) if decode ever needs the drafter — untested here. - Compaction hub unchanged: all three endpoints still route
compactionModelhere; 200,704 per slot is the fleet's only window that fits a worst-case compaction input, and--parallel 2keeps compaction off the critical path of a live generation. - Harness side (
~/.omp/agent/models.yml):contextWindow: 200704andmaxTokens: 16384unchanged;extraBodynow quotes the payload's own 1.0 / 0.95 / 20 withpreserve_thinking: true. ThereasoningEffortMapfold became load-bearing (the template raises on any level outsidexhigh|medium|low) — an unmapped level is a hard 500 on the role's first request, naming Jinja rather than the model. - Rollback: revert the swap commit. The Qwen3.5-9B Q6_K payload, its mmproj
and the master CUDA build all remain on disk
(
/home/dank/models/Qwen35-9B-HauhauCS-Q6K/,~/GitPackages/llama-master/build-cuda/), and the retired era keeps its own page: frank-zealot-llama-qwen35-9b-q6k-2x200704. - Deployed 2026-10-17 19:44 (
deploy.sh dankclaw --service frank-zealot --service homepage): boot ledger 22,664 used / 1,461 free, engine fit projected 21,439 and left 2,119 ≥ 1,872 MiB, KV 13,328.00 MiB atn_ctx_seq = 200,704,n_slots = 2, projector loaded; live probes green (/v1/modelsservesfrank-zealot, exactpong+reasoning_content,tool_calls{"city":"Seattle"}, 512 px PNG → Red). e2e gate 33/33. - Picker row is live state: the OpenWebUI
modelrow'smeta.descriptionnamed the 9B, so it was re-keyed in the database too (backupwebui.db.bak-20261017a). The row was written WITHOUT restartingopen-webui— the cached list refreshes on its next restart, and forcing one would have logged every family session out for a cosmetic string. See OpenWebUI for the picker trio.
Related
- frank-zealot-llama-qwen35-9b-q6k-2x200704 — the replaced era (rollback target)
- frank-dragoon-flash-server-v2-swift15 — the abliterated sibling on the iGPU
- frank-archon-radiance-qwen38-iq4r — the censored-base flagship this payload shares base alignment with
- deployment — deploy flow and incidents