Files
serverAppConfigs/serverAppConfigs-docs/LLM/frank-zealot-llama-prism-bonsai2-pq20.md
T
Frank 466fd477d8 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.
2026-10-18 13:15:18 -07:00

227 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: reference
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
- dankclaw
- llama-cpp
- gguf
- rtx3090
timestamp: 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`](../../scripts/frank-zealot.service)), `User=dank`, `Restart=on-failure`, `LimitMEMLOCK=infinity` |
| Serve script | [`scripts/serve-frank-zealot.sh`](../../scripts/serve-frank-zealot.sh), tunables [`scripts/frank-zealot.env`](../../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`](../../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_weather` request returns `finish_reason: tool_calls`
with `arguments: {"city":"Seattle"}` — valid JSON, no loop, no HTTP 500. The
fork's `KNOWN_ISSUES.md` warning 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: 64` the request returns
`finish_reason: "length"` with EMPTY `content` and half a thought in
`reasoning_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) at `VERBOSITY=4` prints
`failed 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 in `content`. The OpenAI `/v1/chat/completions` path parses it into
`reasoning_content` correctly, 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
`/completion` request 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
`/completion` at 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 invented `KV_CACHE_TYPE` is 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.
- **`/metrics` needs `--metrics` (fixed 2026-10-18):** the fork answers **501 Not
Implemented** without the flag, and Dankclaw's Prometheus scrapes `/metrics`, so
`up{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
`/health` 200 the entire time. `METRICS=1` in `scripts/frank-zealot.env` now
ships the flag and the seam asserts it reaches the argv. halogen serves
`/metrics` natively, 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`, sha256
`5b24ea3e…`) is a **one-line `MODEL_FILE` swap**: 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
`compactionModel` here; 200,704 per slot is the fleet's only window that fits
a worst-case compaction input, and `--parallel 2` keeps compaction off the
critical path of a live generation.
- **Harness side** (`~/.omp/agent/models.yml`): `contextWindow: 200704` and
`maxTokens: 16384` unchanged; `extraBody` now quotes the payload's own
1.0 / 0.95 / 20 with `preserve_thinking: true`. The `reasoningEffortMap` fold
became **load-bearing** (the template raises on any level outside
`xhigh|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 at
`n_ctx_seq = 200,704`, `n_slots = 2`, projector loaded; live probes green
(`/v1/models` serves `frank-zealot`, exact `pong` + `reasoning_content`,
`tool_calls` `{"city":"Seattle"}`, 512 px PNG → **Red**). e2e gate 33/33.
- **Picker row is live state:** the OpenWebUI `model` row's
`meta.description` named the 9B, so it was re-keyed in the database too
(backup `webui.db.bak-20261017a`). The row was written WITHOUT restarting
`open-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