Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d41366f4c | ||
|
|
64765c6a47 | ||
|
|
d5934a6b47 | ||
|
|
40ad1f4a60 | ||
|
|
9de58ed6fd | ||
|
|
b9d21ae947 | ||
|
|
30e8ed55a0 | ||
|
|
ba69ea563e | ||
|
|
a7062d1cb3 | ||
|
|
25a8b00043 | ||
|
|
a5fe8db877 | ||
|
|
c195c839b4 | ||
|
|
500ac2b871 | ||
|
|
6a8cbde260 | ||
|
|
73fb27254e | ||
|
|
e32df1b04f | ||
|
|
a518fc7c22 | ||
|
|
129b953843 |
No files matched your search
@@ -388,10 +388,11 @@ 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 several slots). `frank-zealot`'s `contextWindow` is
|
||||
**262144** — the PER-SLOT window (`--ctx-size 524288 --parallel 2` since the
|
||||
2026-10-18 reslice to exact f16 KV), which EQUALS the model's
|
||||
native window; llama.cpp answers a longer prompt with a 400 naming the limit,
|
||||
never a truncation, so the harness must compact before it.
|
||||
**200704** — the PER-SLOT window (`--ctx-size 602112 --parallel 3` since the
|
||||
2026-10-19 reslice that funded a third slot AND an armed MTP drafter),
|
||||
deliberately below 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
|
||||
@@ -439,10 +440,11 @@ disagrees with it is the bug.
|
||||
loader ignoring that metadata dequantizes wrong); payload
|
||||
`Bonsai2-9B-MTP/…PQ2_0.gguf` + the publisher's **Q8_0** mmproj (the BF16
|
||||
tower can crash the server with concurrent image requests).
|
||||
- Shape: `--ctx-size 524288 --parallel 2` = **two conversations at the
|
||||
model's native 262,144**, `CACHE_TYPE_K/V=f16`, batch 4096 / ubatch 2048,
|
||||
`SPEC_TYPE=none`, `--flash-attn on`, `--jinja`, `--metrics`.
|
||||
- Harness/gate contract: `contextWindow: 262144` is the **per-slot** window
|
||||
- Shape: `--ctx-size 602112 --parallel 3` = **three conversations at
|
||||
200,704 each**, `CACHE_TYPE_K=f16` + `CACHE_TYPE_V=q8_0` (asymmetric: 25,088
|
||||
B/token, 14,406 MiB of pool), batch 6144 / ubatch 1024,
|
||||
`SPEC_TYPE=draft-mtp` (depth 2), `--flash-attn on`, `--jinja`, `--metrics`.
|
||||
- Harness/gate contract: `contextWindow: 200704` is the **per-slot** window
|
||||
(`n_ctx_slot = CTX_SIZE / PARALLEL`), and llama.cpp answers a longer prompt
|
||||
with a 400 rather than truncating. `--metrics` is mandatory or Prometheus
|
||||
reads 501 = DOWN beside a healthy service. Presence penalty has no GGUF
|
||||
@@ -942,6 +944,14 @@ linked runbooks.
|
||||
already does all three; ad-hoc fetches tend not to. Gated repos (e.g.
|
||||
`orcarouter/Qwen3.8-Flash-Next-Uncensored-GGUF`, same 104.09 GiB Q4_K_S)
|
||||
return 403 even with the host's HF token until that account accepts the gate.
|
||||
- **A scout verdict is a lead, never a finding.** In the 2026-10-19 vault de-dup
|
||||
campaign, four read-only scouts worked the same pages: one classified era pages as
|
||||
stale *because* they document retired payloads (that is an era page's content), one
|
||||
reported files that do not exist, one reported duplications that grep disproved
|
||||
(`457.9 t/s` and the ping-RTT table are solo-held by `Ethernet Bandwidth Math`),
|
||||
and one cited a line number past the end of the file it judged. Re-verify every
|
||||
claimed duplicate and every claimed staleness against the file and today's fleet
|
||||
state before editing; a scout yield buys no evidence.
|
||||
|
||||
## Runbook Index
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
# The scratch mirrors the production container contract exactly (serve-frank-
|
||||
# primary.sh): --network host, --init, seccomp=unconfined (io_uring for the
|
||||
# n-gram table), memlock unbounded (the ~132 GiB pinned ledger), /dev/kfd +
|
||||
# /dev/dri, the .rad ro, the state dir rw, the tune cache mount. The ONLY
|
||||
# differences: the container name (arbench, so prod's identity is untouched)
|
||||
# and a FRESH prefix-cache disk tier (arbench/, wiped per run) so the cold
|
||||
# prefill is cold — the prod tier at kvcache/frank-archon is left intact.
|
||||
# /dev/dri, the .rad ro, the tune cache mount. The ONLY differences: the
|
||||
# container name (arbench, so prod's identity is untouched) and the prefix cache
|
||||
# switched OFF (--prefix-cache-mib 0), so every prefill is cold — the tier is
|
||||
# host RAM now, there is no per-run disk directory to wipe.
|
||||
set -uo pipefail
|
||||
|
||||
ENV_FILE="${1:-/home/dank/serverAppConfigs/scripts/frank-archon.env}"
|
||||
@@ -60,9 +60,8 @@ set -a; . "$ENV_FILE"; set +a
|
||||
sudo systemctl stop frank-archon >/dev/null 2>&1 || true
|
||||
podman rm -f "$SCRATCH" >/dev/null 2>&1 || true
|
||||
|
||||
# --- fresh prefix-cache tier for a cold prefill (prod's tier untouched) ------
|
||||
FRESH_KV="$FRANK_ARCHON_STATE_DIR/kvcache/$SCRATCH"
|
||||
rm -rf "$FRESH_KV"; mkdir -p "$FRESH_KV"
|
||||
# --- the prefix cache is switched off below, so there is no scratch KV tree ---
|
||||
# to wipe; the cold prefill comes from --prefix-cache-mib 0.
|
||||
mkdir -p "$FRANK_ARCHON_STATE_DIR/tune"
|
||||
|
||||
# --- boot the scratch: the production run line, scratch name + fresh KV ------
|
||||
@@ -73,7 +72,6 @@ podman run -d --name "$SCRATCH" \
|
||||
--ulimit memlock=-1:-1 \
|
||||
--device /dev/kfd --device /dev/dri \
|
||||
-v "$FRANK_ARCHON_MODEL_DIR":/models:ro \
|
||||
-v "$FRANK_ARCHON_STATE_DIR":/data \
|
||||
-v "$FRANK_ARCHON_STATE_DIR/tune":/opt/radiance/share/radiance/tune \
|
||||
-v "$FRANK_ARCHON_TEMPLATE":/templates/template.jinja:ro \
|
||||
"$FRANK_ARCHON_IMAGE" \
|
||||
@@ -87,12 +85,9 @@ podman run -d --name "$SCRATCH" \
|
||||
--placement "$FRANK_ARCHON_PLACEMENT" \
|
||||
--host-pool-mib "$FRANK_ARCHON_HOST_POOL_MIB" \
|
||||
--gpu-headroom-mib "$FRANK_ARCHON_GPU_HEADROOM_MIB" \
|
||||
--vram-weights-mib "$FRANK_ARCHON_VRAM_WEIGHTS_MIB" \
|
||||
--vram-kv-mib "$FRANK_ARCHON_VRAM_KV_MIB" \
|
||||
--kv-cache-dtype "$FRANK_ARCHON_KV_DTYPE" \
|
||||
--prefix-cache-host-mib "$FRANK_ARCHON_PREFIX_HOST_MIB" \
|
||||
--prefix-cache-dir "/data/kvcache/$SCRATCH" \
|
||||
--prefix-cache-disk-mib "$FRANK_ARCHON_PREFIX_DISK_MIB" \
|
||||
--prefix-cache-mib 0 \
|
||||
--busy-prefill-chunk "${FRANK_ARCHON_BUSY_PREFILL_CHUNK:-auto}" \
|
||||
--ngram-placement "$FRANK_ARCHON_NGRAM_PLACEMENT" \
|
||||
--num-speculative-tokens "$FRANK_ARCHON_SPEC_TOKENS" \
|
||||
--max-num-batched-tokens "$FRANK_ARCHON_MAX_BATCHED_TOKENS" \
|
||||
|
||||
@@ -1459,7 +1459,7 @@ LLM_ZEALOT_RUNTIME="${LLM_ZEALOT_RUNTIME:-/home/dank/GitPackages/llama-prism-b10
|
||||
# image path — a deploy that starts without it silently ships a text-only
|
||||
# endpoint. Checked unconditionally, like frank-dragoon's. Q8_0 is MANDATORY,
|
||||
# not a preference: the card says the BF16 tower can crash llama-server when
|
||||
# several image requests run at once, and this endpoint runs --parallel 2. Its
|
||||
# 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}"
|
||||
|
||||
+59
-27
@@ -7,10 +7,15 @@
|
||||
# only the reason a reader editing THIS line cannot recover from the page.
|
||||
# Where the two disagree, this file wins on values; the boot ledger arbitrates
|
||||
# the engine shape.
|
||||
FRANK_ARCHON_IMAGE=stilldeadcode/radiance@sha256:4c3f484564bb2872784e46b0a59cbd71becc9c1e746e301a3596ffae253c322e
|
||||
# 1.0.13 is pinned by digest on purpose: 1.2.0 is a DEAD END on this payload
|
||||
# (acceptance 0.7123 vs 0.7549 — the page's Dead ends section names the suspect).
|
||||
# The .rad file mounted under /models; /data holds the prefix-cache disk tier.
|
||||
FRANK_ARCHON_IMAGE=stilldeadcode/radiance@sha256:d590e300234bf6d2f942a0b8d456d183153fd808f28820e6c2fa69c9389178ed
|
||||
# 1.8.1 (2026-10-18), pinned by digest. Two CLI breaks sit between it and the
|
||||
# 1.0.13 line this endpoint ran for two eras, and both are boot refusals, not
|
||||
# warnings: --vram-weights-mib/--vram-kv-mib were REMOVED at 1.3.0 (the card is
|
||||
# now split by the engine's budget resolver — see below), and the prefix cache
|
||||
# lost its disk tier. Draft acceptance has to be re-scored on any pin past 1.2.0:
|
||||
# 1.2.0's MoE weight rotation measured 0.7123 against the 0.7549 control, and the
|
||||
# suspect is untangled (the page's Dead ends section names it).
|
||||
# The .rad file mounts under /models; the tune cache mounts separately below.
|
||||
# The model dir holds ONLY .rad files — the HF download and staging stay beside it.
|
||||
FRANK_ARCHON_MODEL_DIR=/home/dank/models/rad
|
||||
FRANK_ARCHON_MODEL_FILE=qwen3.8-next-flash-fp8-iq4r-moe.rad
|
||||
@@ -42,31 +47,42 @@ FRANK_ARCHON_TP=2
|
||||
# MAX_SEQS x (1 + SPEC_TOKENS) below.
|
||||
FRANK_ARCHON_TP_WIRE=wht6
|
||||
# Decode slots. A slot costs KV, never decode; the pool is sized to this
|
||||
# contract and nothing more, so a FOURTH full-height conversation is REFUSED as
|
||||
# too large to fit — never squeezed, paged or truncated. That is Frank's
|
||||
# explicit trade: the freed MiB went to the expert slab.
|
||||
FRANK_ARCHON_MAX_SEQS=3
|
||||
# contract and nothing more. At 1.8.1 the budget resolver caps the cache at
|
||||
# 556,348 fp8 tokens — 2.8 sessions at 200,000 — so a THIRD full-height
|
||||
# conversation answers 400 while /health and every shorter chat work fine
|
||||
# (3 x 50k streams run at 77.6 t/s aggregate). Frank's trade: declare the two
|
||||
# slots the engine can hold and let the freed ~1.1 GiB go to the expert plane,
|
||||
# instead of advertising a third slot that only exists at shorter prompts.
|
||||
FRANK_ARCHON_MAX_SEQS=2
|
||||
# Served window. The engine treats `kv group tokens >= max-model-len` as a HARD
|
||||
# limit, and models.yml contextWindow MUST quote this number (over-length is a
|
||||
# refusal, not a truncation). The pool is sized for seqs x maxlen, not one
|
||||
# sequence.
|
||||
FRANK_ARCHON_MAX_MODEL_LEN=200000
|
||||
FRANK_ARCHON_PLACEMENT=expert_tiered
|
||||
# Pinned host pool for expert rows that do not fit VRAM. It moves WITH
|
||||
# VRAM_WEIGHTS: the pair and this pool serve the per-rank resident set together,
|
||||
# so cutting the slab spills here — under-sizing it is a boot failure (36,864
|
||||
# Pinned host pool for expert rows that do not fit VRAM. Under the budget
|
||||
# resolver this is ALSO the lever that decides which experts stay on the card:
|
||||
# the card's leftover goes to the expert plane first and the KV cache takes the
|
||||
# rest, so growing the pool moves experts off the card and shrinking it spills
|
||||
# them onto VRAM the cache wanted. Under-sizing it is a boot failure (36,864
|
||||
# refused a boot). The ledger's coverage line is the referee, not this file.
|
||||
# Measured, not guessed: 45,568 (PR #30) moved host_pool to 45.09 GiB and left
|
||||
# vram_kv byte-identical at 4.20 GiB / 556,348 tokens on both ranks — extra
|
||||
# host RAM above what the expert plane's footprint needs buys no cache. Back at
|
||||
# 44,928, the size that boots clean.
|
||||
FRANK_ARCHON_HOST_POOL_MIB=44928
|
||||
# VRAM left unclaimed per card (publisher default 96 MiB); the engine's settle
|
||||
# floor is built from it.
|
||||
FRANK_ARCHON_GPU_HEADROOM_MIB=96
|
||||
# ONE budget per card: VRAM_WEIGHTS + VRAM_KV <= 30,969 MiB, the ceiling the
|
||||
# engine's own budget table prints (derivation and the slab/pool price: the
|
||||
# page's VRAM laws). Idle KV VRAM is ALSO the expert cache's spillover, so a
|
||||
# bigger pool pays twice — which is why the pool sits at the contract and the
|
||||
# slab takes the rest.
|
||||
FRANK_ARCHON_VRAM_WEIGHTS_MIB=25600
|
||||
FRANK_ARCHON_VRAM_KV_MIB=5120
|
||||
# There is no VRAM pair any more. --vram-weights-mib/--vram-kv-mib were removed
|
||||
# at 1.3.0: the engine takes capacity - already-held - GPU_HEADROOM - arena,
|
||||
# puts the static (non-expert) weights off the top, then divides the rest between
|
||||
# the expert plane and the KV cache — experts first when the cache has no idle
|
||||
# blocks to lend, and never less than ONE MAX_MODEL_LEN sequence for the cache.
|
||||
# What moves the split from here is HOST_POOL_MIB (which experts stay resident),
|
||||
# MAX_MODEL_LEN/MAX_SEQS (the cache bound) and GPU_HEADROOM_MIB. The slab size
|
||||
# the prefill-slope law cares about is a boot-ledger line now, not a knob: read
|
||||
# it after every resize instead of asserting it here.
|
||||
# fp8 is the ONLY rung below bf16 on this engine: one kv_dtype for the whole
|
||||
# cache, so llama.cpp's exact-K/quantized-V split (frank-zealot) is unservable
|
||||
# here. bf16 costs 1.89x fp8, not 2x — the QSA block-key plane is dtype-fixed.
|
||||
@@ -75,19 +91,35 @@ FRANK_ARCHON_KV_DTYPE=fp8
|
||||
# 2, and 3 x (1+2) = 9 rows stays inside the wht6 25-row exactness bound.
|
||||
FRANK_ARCHON_SPEC_TOKENS=2
|
||||
FRANK_ARCHON_MAX_BATCHED_TOKENS=2048
|
||||
# Prefix cache tiers. The host tier is sized in ENTRIES and one entry tracks the
|
||||
# cache dtype (58.50 KiB at fp8), so a dtype change re-sizes the parked set
|
||||
# without touching these numbers.
|
||||
FRANK_ARCHON_PREFIX_HOST_MIB=8192
|
||||
FRANK_ARCHON_PREFIX_DISK_MIB=65536
|
||||
# Step sharing. A prefill chunk that shares a step with decoding sequences holds
|
||||
# the device for the chunk's whole time, and every decode row in that step waits
|
||||
# that long for its next token (the census: [[LLM Performance Diagnosis]] §9).
|
||||
# `auto` leaves each decode row behind the chunk a row plus its draft window and
|
||||
# keeps a share for whatever is waiting to be admitted; a number pins the chunk's
|
||||
# cap; `off` is the pre-1.4 behaviour this endpoint ran for two eras, where the
|
||||
# chunk took the step and admission never ran. Interleave inserts N decode-only
|
||||
# steps after a chunk step taken beside decoders: it protects their cadence and
|
||||
# holds a waiting request out of those steps, so it stays 0 until the post-migration
|
||||
# decode-vs-prefill buckets say it is wanted.
|
||||
FRANK_ARCHON_BUSY_PREFILL_CHUNK=auto
|
||||
FRANK_ARCHON_PREFILL_INTERLEAVE=0
|
||||
# Prefix cache: pinned host memory only. The disk tier was removed outright
|
||||
# (--prefix-cache-dir and --prefix-cache-disk-mib are refusals), so what used to
|
||||
# be restored from NVMe is now re-prefilled. The tier is sized in ENTRIES and one
|
||||
# entry tracks the cache dtype (58.50 KiB at fp8). Watch prefix_hit_rate after
|
||||
# this change: a drop is extra prefill, and prefill is exactly the traffic that
|
||||
# costs decode its cadence.
|
||||
FRANK_ARCHON_PREFIX_CACHE_MIB=8192
|
||||
# Frank's pin law: the PLE n-gram table serves from pinned host RAM the cards
|
||||
# read. `ram` REFUSES the start if memlock is too small — the unit's
|
||||
# LimitMEMLOCK=infinity is the other half of this law.
|
||||
FRANK_ARCHON_NGRAM_PLACEMENT=ram
|
||||
# TTM preflight. The pinned ledger (~147 GiB) rides TTM's shared pages, and the
|
||||
# kernel RESETS pages_limit to half of RAM on every boot; below the plan the
|
||||
# load dies mid-pin with an anonymous allocator error. ttm-preflight.sh raises
|
||||
# the knob itself before refusing by name, so recovery is a boot, not a visit.
|
||||
# TTM preflight. The pinned ledger rides TTM's shared pages, and the kernel
|
||||
# RESETS pages_limit to half of RAM on every boot; below the plan the load dies
|
||||
# mid-pin with an anonymous allocator error. ttm-preflight.sh raises the knob
|
||||
# itself before refusing by name, so recovery is a boot, not a visit.
|
||||
# Floor = 2 x HOST_POOL + 48,824 (static weights + arena) + PREFIX_CACHE + MNBT
|
||||
# per the bats ledger test; at the 44,928 pool that computes to 148,920 MiB.
|
||||
FRANK_ARCHON_TTM_MIN_MIB=150000
|
||||
FRANK_ARCHON_TTM_TARGET_MIB=204800
|
||||
# Escape hatch for experiments. Unset -> no extra argv; the serve script expands
|
||||
|
||||
+37
-21
@@ -11,35 +11,51 @@ LLAMA_BIN=/home/dank/GitPackages/llama-prism-b10777/llama-server
|
||||
# metadata dequantizes wrong, silently.
|
||||
MODEL_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-PQ2_0-MTP-Q8_0.gguf
|
||||
# Q8_0 IS MANDATORY: the publisher states the BF16 tower can crash llama-server
|
||||
# under concurrent image requests, which is exactly --parallel 2.
|
||||
# under concurrent image requests, which is exactly --parallel 3.
|
||||
MMPROJ_FILE=/home/dank/models/Bonsai2-9B-MTP/Ternary-Bonsai-2-9B-mmproj-Q8_0.gguf
|
||||
# Served id — the harness/family name, not a fact about the payload.
|
||||
# Id-keyed consumers: OpenWebUI (model row, access_grant, api_configs), models.yml.
|
||||
ALIAS=frank-zealot
|
||||
BIND_ADDR=0.0.0.0
|
||||
LISTEN_PORT=7771
|
||||
# CTX_SIZE is the POOL llama.cpp divides across slots: 524,288 / 2 = 262,144 a
|
||||
# conversation, each still at the trained window. MUST stay divisible by
|
||||
# PARALLEL (the seam enforces it) and models.yml contextWindow quotes the
|
||||
# PER-SLOT number — a longer prompt is a 400, never a truncation. The pool
|
||||
# cannot go back to 786,432 under exact f16 KV: 24,576 MiB of cache against the
|
||||
# 24,123 MiB the driver leaves addressable, before a weight loads.
|
||||
CTX_SIZE=524288
|
||||
PARALLEL=2
|
||||
BATCH_SIZE=4096
|
||||
# CTX_SIZE is the POOL llama.cpp divides across slots: 602,112 / 3 = 200,704 a
|
||||
# conversation. MUST stay divisible by PARALLEL (the seam enforces it) and
|
||||
# models.yml contextWindow quotes the PER-SLOT number — a longer prompt is a
|
||||
# 400, never a truncation. The pool is bought, not granted: K+V at 25,088
|
||||
# B/token is 14,406.00 MiB here and the MTP draft context bills its own f16 KV
|
||||
# over the WHOLE pool (4,096 B/position ⇒ 2,352.00 MiB), so the old 786,432
|
||||
# pool cannot carry a drafter at any KV dtype — 18,816 + 3,072 of cache alone
|
||||
# against the 24,123 MiB the driver leaves addressable.
|
||||
CTX_SIZE=602112
|
||||
PARALLEL=3
|
||||
# The logical batch is VRAM- and throughput-neutral at a fixed ubatch (the law
|
||||
# on [[frank-zealot]]); 1024×6 keeps it a clean multiple of the ubatch.
|
||||
BATCH_SIZE=6144
|
||||
# Ubatch is a prefill knob (decode never moves with it) and it sets the
|
||||
# compute-buffer size — which is what decides whether a drafter fits.
|
||||
# UBATCH_SIZE=1024 is the documented MTP arm.
|
||||
UBATCH_SIZE=2048
|
||||
# Exact K and V: the retrieval-fidelity ask, paid with one conversation of
|
||||
# concurrency. Rate law and the arithmetic: [[frank-zealot]] → Laws.
|
||||
# compute-buffer size, which decides whether a drafter fits. 1024 is the rung
|
||||
# every measured MTP arm here ran on (213.5 t/s against 109.2 at 2048). Its
|
||||
# VRAM credit is SHAPE-specific: the 786,432-pool arm census showed ~1,306 MiB
|
||||
# back, this boot measured 2,089.88 MiB of target compute against 2,496.13 at
|
||||
# ubatch 2048 — 406 MiB back, with the split moving (CUDA0 +257, CUDA_Host
|
||||
# −663). The drafter is funded by CACHE_TYPE_V below, not by this knob.
|
||||
UBATCH_SIZE=1024
|
||||
# K exact, V in the 8-bit class: 16,384 + 8,704 = 25,088 B/token (8,192
|
||||
# elements per token per cache × 2 B for f16, × 1.0625 for q8_0 — there is no
|
||||
# true fp8 KV dtype in this build). That 7,680 B/token saved against exact V is
|
||||
# what funds BOTH the third conversation and the drafter. Quantized V is
|
||||
# illegal without --flash-attn, which the serve script pins. Rate law and the
|
||||
# arithmetic: [[frank-zealot]] → Laws.
|
||||
CACHE_TYPE_K=f16
|
||||
CACHE_TYPE_V=f16
|
||||
# Speculation OFF — measured, not assumed: the draft context bills its own f16
|
||||
# KV over the WHOLE pool, and there is no room beside exact KV (the arm census
|
||||
# is on the page). SPEC_TYPE=none strips the WHOLE set (--spec-type and every
|
||||
# --spec-draft-*); half a set has segfaulted at load.
|
||||
SPEC_TYPE=none
|
||||
CACHE_TYPE_V=q8_0
|
||||
# Speculation ON (Frank, 2026-10-19): the head is in the file
|
||||
# (qwen35.nextn_predict_layers = 1, blk.32.nextn.*) and rides the MAIN model's
|
||||
# weights, so there is no --spec-draft-model. Its bill is pool × 4,096 B of
|
||||
# draft KV plus ~0.45 GiB of buffers and a tripled recurrent state — paid for
|
||||
# by CACHE_TYPE_V=q8_0 and UBATCH_SIZE=1024 above. Build gate: quantized V
|
||||
# beside MTP needs fork build >= b10776; LLAMA_BIN is 10777.
|
||||
# SPEC_TYPE=none is the rollback and strips the WHOLE set (--spec-type and
|
||||
# every --spec-draft-*); half a set has segfaulted at load.
|
||||
SPEC_TYPE=draft-mtp
|
||||
SPEC_DRAFT_N_MAX=2
|
||||
SPEC_DRAFT_N_MIN=0
|
||||
SPEC_DRAFT_P_MIN=0
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
# log states every budget before anything is allocated (weights vs KV
|
||||
# split, the expert host pool's coverage of the worst case, the PLE pin,
|
||||
# the P2P answer per pair) — read it after every resize.
|
||||
# Deployed shape (2026-10-13): THREE decode slots x 200,000 tokens, fp8
|
||||
# (quantized) KV in a 5,120 MiB pool (contract 3 x 200,000 + 1.3 %) and a
|
||||
# 25,600 MiB resident expert slab — 30,720 MiB of the 30,969 MiB the engine's
|
||||
# own budget table makes claimable, spent on the slab because prefill is
|
||||
# slab-limited. 44 GiB/rank pinned expert pool, MTP depth 2, prefix cache host
|
||||
# 8 GiB + disk 64 GiB, PLE pinned in RAM.
|
||||
# Deployed shape (2026-10-19, radiance 1.8.1): THREE decode slots x 200,000
|
||||
# tokens, fp8 KV, 44 GiB/rank pinned expert pool, MTP depth 2, prefix cache in
|
||||
# pinned host RAM, PLE pinned in RAM. The per-card split between the expert slab
|
||||
# and the KV pool is NO LONGER an operator number — 1.3.0 removed the VRAM pair
|
||||
# and the budget resolver derives it from headroom, the cache bound and the host
|
||||
# pool, so the slab size and the pool's token count are LEDGER lines: after every
|
||||
# boot read them out of the log and check the pool still backs 3 x 200,000.
|
||||
#
|
||||
# The root unit (scripts/frank-archon.service, User=dank,
|
||||
# LimitMEMLOCK=infinity) grants the pin budget: 2 x 44 GiB expert pools +
|
||||
@@ -56,13 +57,11 @@ set +a
|
||||
: "${FRANK_ARCHON_PLACEMENT:?FRANK_ARCHON_PLACEMENT must be set}"
|
||||
: "${FRANK_ARCHON_HOST_POOL_MIB:?FRANK_ARCHON_HOST_POOL_MIB must be set}"
|
||||
: "${FRANK_ARCHON_GPU_HEADROOM_MIB:?FRANK_ARCHON_GPU_HEADROOM_MIB must be set}"
|
||||
: "${FRANK_ARCHON_VRAM_WEIGHTS_MIB:?FRANK_ARCHON_VRAM_WEIGHTS_MIB must be set}"
|
||||
: "${FRANK_ARCHON_VRAM_KV_MIB:?FRANK_ARCHON_VRAM_KV_MIB must be set}"
|
||||
: "${FRANK_ARCHON_BUSY_PREFILL_CHUNK:?FRANK_ARCHON_BUSY_PREFILL_CHUNK must be set (auto|off|N)}"
|
||||
: "${FRANK_ARCHON_KV_DTYPE:?FRANK_ARCHON_KV_DTYPE must be set}"
|
||||
: "${FRANK_ARCHON_SPEC_TOKENS:?FRANK_ARCHON_SPEC_TOKENS must be set}"
|
||||
: "${FRANK_ARCHON_MAX_BATCHED_TOKENS:?FRANK_ARCHON_MAX_BATCHED_TOKENS must be set}"
|
||||
: "${FRANK_ARCHON_PREFIX_HOST_MIB:?FRANK_ARCHON_PREFIX_HOST_MIB must be set}"
|
||||
: "${FRANK_ARCHON_PREFIX_DISK_MIB:?FRANK_ARCHON_PREFIX_DISK_MIB must be set}"
|
||||
: "${FRANK_ARCHON_PREFIX_CACHE_MIB:?FRANK_ARCHON_PREFIX_CACHE_MIB must be set}"
|
||||
: "${FRANK_ARCHON_NGRAM_PLACEMENT:?FRANK_ARCHON_NGRAM_PLACEMENT must be set}"
|
||||
: "${FRANK_ARCHON_TTM_MIN_MIB:?FRANK_ARCHON_TTM_MIN_MIB must be set}"
|
||||
: "${FRANK_ARCHON_TTM_TARGET_MIB:?FRANK_ARCHON_TTM_TARGET_MIB must be set}"
|
||||
@@ -103,19 +102,12 @@ if [ ! -f "$FRANK_ARCHON_TEMPLATE" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$FRANK_ARCHON_STATE_DIR/kvcache/frank-archon"
|
||||
# The engine's create is NOT recursive: with only the state dir mounted, its
|
||||
# "cannot create /data/kvcache/frank-archon: No such file or directory"
|
||||
# left the SSD prefix tier off (measured 2026-10-06 boot ledger). Pre-create
|
||||
# the full path on the host so the tier opens on first boot.
|
||||
|
||||
mkdir -p "$FRANK_ARCHON_STATE_DIR/tune"
|
||||
# rad-tune's kernel-selection cache (see the runbook's "Tuning levers"):
|
||||
# the engine's selector reads $RADIANCE_HOME/tune/<machine>.tune at boot;
|
||||
# $RADIANCE_HOME defaults to /opt/radiance, which lives in the image, so
|
||||
# the cache must ride a host mount or it dies with the --rm container
|
||||
# that wrote it. The dir is pre-created for the same reason as kvcache
|
||||
# (the engine's create is not recursive).
|
||||
# that wrote it. The engine's create is not recursive, so the dir is pre-created.
|
||||
|
||||
if [ ! -e /dev/kfd ] || [ ! -d /dev/dri ]; then
|
||||
echo "ERROR: GPU device nodes absent (/dev/kfd, /dev/dri)" >&2
|
||||
@@ -148,6 +140,15 @@ done
|
||||
LOG=/home/dank/logs/frank_archon.log
|
||||
mkdir -p "$(dirname "$LOG")"
|
||||
|
||||
# Interleave is opt-in: 0 is the engine's default and passing it anyway would put
|
||||
# a flag in argv that changes nothing but the record. The sharing cap is always
|
||||
# explicit because `auto` vs `off` is the difference between decode waiting one
|
||||
# chunk and waiting the whole step.
|
||||
PREFILL_ARGS=()
|
||||
if [ "${FRANK_ARCHON_PREFILL_INTERLEAVE:-0}" -gt 0 ]; then
|
||||
PREFILL_ARGS=(--prefill-interleave "$FRANK_ARCHON_PREFILL_INTERLEAVE")
|
||||
fi
|
||||
|
||||
# Publisher-vetted container contract (docs/DOCKER.md, compose/common.yaml):
|
||||
# --network host (the server binds the host's addresses directly, no NAT),
|
||||
# --init (the engine releases the cards on SIGTERM), seccomp=unconfined
|
||||
@@ -156,8 +157,9 @@ mkdir -p "$(dirname "$LOG")"
|
||||
# makes for host memory), memlock unbounded (the pinned ledger — legal
|
||||
# because the root unit grants LimitMEMLOCK=infinity to this dank process),
|
||||
# /dev/kfd + /dev/dri (render/video groups are moot for a root container).
|
||||
# The template rides a single-file mount; the .rad dir is ro; /data holds
|
||||
# the prefix-cache disk tier.
|
||||
# The template rides a single-file mount and the .rad dir is ro. The prefix cache
|
||||
# has no disk tier any more, so the only host state this container writes is the
|
||||
# rad-tune cache under /opt/radiance/share/radiance/tune.
|
||||
podman run -d --name "$FRANK_ARCHON_CONTAINER" \
|
||||
--network host \
|
||||
--init \
|
||||
@@ -165,7 +167,6 @@ podman run -d --name "$FRANK_ARCHON_CONTAINER" \
|
||||
--ulimit memlock=-1:-1 \
|
||||
--device /dev/kfd --device /dev/dri \
|
||||
-v "$FRANK_ARCHON_MODEL_DIR":/models:ro \
|
||||
-v "$FRANK_ARCHON_STATE_DIR":/data \
|
||||
-v "$FRANK_ARCHON_STATE_DIR/tune":/opt/radiance/share/radiance/tune \
|
||||
-v "$FRANK_ARCHON_TEMPLATE":/templates/template.jinja:ro \
|
||||
"$FRANK_ARCHON_IMAGE" \
|
||||
@@ -179,15 +180,13 @@ podman run -d --name "$FRANK_ARCHON_CONTAINER" \
|
||||
--placement "$FRANK_ARCHON_PLACEMENT" \
|
||||
--host-pool-mib "$FRANK_ARCHON_HOST_POOL_MIB" \
|
||||
--gpu-headroom-mib "$FRANK_ARCHON_GPU_HEADROOM_MIB" \
|
||||
--vram-weights-mib "$FRANK_ARCHON_VRAM_WEIGHTS_MIB" \
|
||||
--vram-kv-mib "$FRANK_ARCHON_VRAM_KV_MIB" \
|
||||
--kv-cache-dtype "$FRANK_ARCHON_KV_DTYPE" \
|
||||
--prefix-cache-host-mib "$FRANK_ARCHON_PREFIX_HOST_MIB" \
|
||||
--prefix-cache-dir /data/kvcache/frank-archon \
|
||||
--prefix-cache-disk-mib "$FRANK_ARCHON_PREFIX_DISK_MIB" \
|
||||
--prefix-cache-mib "$FRANK_ARCHON_PREFIX_CACHE_MIB" \
|
||||
--ngram-placement "$FRANK_ARCHON_NGRAM_PLACEMENT" \
|
||||
--num-speculative-tokens "$FRANK_ARCHON_SPEC_TOKENS" \
|
||||
--max-num-batched-tokens "$FRANK_ARCHON_MAX_BATCHED_TOKENS" \
|
||||
--busy-prefill-chunk "$FRANK_ARCHON_BUSY_PREFILL_CHUNK" \
|
||||
"${PREFILL_ARGS[@]}" \
|
||||
${FRANK_ARCHON_EXTRA_ARGS:+$FRANK_ARCHON_EXTRA_ARGS} \
|
||||
> "$LOG" 2>&1
|
||||
|
||||
|
||||
@@ -5,33 +5,45 @@
|
||||
#
|
||||
# 2026-10-18 (Frank): Bonsai-2 27B PQ2_0 → the 9B MTP export, then the same
|
||||
# evening TWO 262,144-token conversations (--ctx-size 524288 --parallel 2) over
|
||||
# an UNQUANTIZED f16 KV cache. The 9B bills 8,192 elements per token per cache
|
||||
# (8 full-attention layers instead of the 27B's 16), so exact K and V costs
|
||||
# 32,768 B/token — 16,384.00 MiB at the 524,288 pool, which is what a 24,123 MiB
|
||||
# addressable card can carry beside the trunk. Verbosity 4 ships so the VRAM-fit
|
||||
# forensics (KV layout, buffer sizes, graph capture) land in the journal.
|
||||
# an UNQUANTIZED f16 KV cache.
|
||||
#
|
||||
# 2026-10-19 (Frank): THREE 200,704-token conversations over a 602,112 pool
|
||||
# (--ctx-size 602112 --parallel 3), K left at the engine's own default (f16),
|
||||
# V dropped to q8_0, batch 1024×6 / ubatch 1024, and the MTP drafter ARMED
|
||||
# (--spec-type draft-mtp). The 9B bills 8,192 elements per token per cache (8
|
||||
# full-attention layers instead of the 27B's 16), so f16 K is 16,384 B/token and
|
||||
# q8_0 V is 8,704: 25,088 B/token = 14,406.00 MiB at the 602,112 pool. That
|
||||
# 7,680 B/token against exact V is exactly what funds the third conversation
|
||||
# AND a draft context that bills f16 KV over the whole pool. Verbosity 4 ships
|
||||
# so the VRAM-fit forensics (KV layout, buffer sizes, graph capture) land in
|
||||
# the journal.
|
||||
#
|
||||
# Architecture (engine ledger, not the marketing page): qwen35 hybrid, 32
|
||||
# layers with qwen35.full_attention_interval = 4 ⇒ 8 full-attention + 24 Gated
|
||||
# DeltaNet, 4 KV heads × 256 dim, native 262,144. The KV pool scales with the 8
|
||||
# full layers (16,384.00 MiB of f16 at the 524,288 pool); the DeltaNet layers
|
||||
# cost a fixed recurrent state per SLOT — ~50.25 MiB a cell, so 100.50 MiB for
|
||||
# two. The runtime is a hard lock, not a preference: custom GGML type 142 plus
|
||||
# prism.hadamard.* metadata, which only the vendor build registers (the b10776
|
||||
# gate that moved the prebuilt 10754 → 10777 was MTP beside quantized V, which
|
||||
# exact KV now makes moot — the type-142 lock alone keeps the build pinned).
|
||||
# full layers (14,406.00 MiB of f16 K + q8_0 V at the 602,112 pool); the
|
||||
# DeltaNet layers cost a fixed recurrent state per SLOT — ~50.25 MiB a cell, so
|
||||
# 150.75 MiB for three, and an armed depth-2 drafter triples that again. The
|
||||
# runtime is a hard lock, not a preference: custom GGML type 142 plus
|
||||
# prism.hadamard.* metadata, which only the vendor build registers — and the
|
||||
# b10776 gate that moved the prebuilt 10754 → 10777 (MTP beside a quantized V)
|
||||
# is LOAD-BEARING again on this shape, not moot as it was under exact KV.
|
||||
# Vision comes from the publisher's Q8_0 projector (the BF16 one can crash the
|
||||
# server on concurrent images, and this endpoint runs two slots), so --mmproj is
|
||||
# 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.
|
||||
#
|
||||
# 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 × 524,288 = 2,048 MiB plus
|
||||
# buffers — and the trunk already projects 21,234 MiB with exact KV. Exact KV
|
||||
# and MTP cannot share this card; frank-zealot.env carries the q8_0-era arm table
|
||||
# and the two doors back to the drafter (one window, or KV back to q8_0 at
|
||||
# --ubatch-size 1024 for 213.5 t/s).
|
||||
# Speculative decoding ships ON (Frank, 2026-10-19). The head is IN the file and
|
||||
# accepts well (0.798/0.617 per position, mean length 2.41, measured 2026-10-18);
|
||||
# what made it lose that evening was the bill, not the drafts — the draft context
|
||||
# is built against the target's WHOLE pool, so blk.32 adds 4,096 B/position
|
||||
# (2,352.00 MiB at 602,112) plus 480.97 MiB of its own compute buffers on the
|
||||
# card, and it triples the recurrent state per slot (452.25 MiB for three cells
|
||||
# against 50.25 a cell with the drafter off). CACHE_TYPE_V=q8_0 is what pays
|
||||
# that: 7,680 B/token cheaper than exact V, 4,410 MiB at this pool. The ubatch
|
||||
# drop to 1024 contributes ~406 MiB here, NOT the ~1,306 the 786,432-pool census
|
||||
# showed — that figure does not transfer between shapes. SPEC_TYPE=none is
|
||||
# the rollback and strips the whole flag set.
|
||||
#
|
||||
# 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 /
|
||||
@@ -58,49 +70,46 @@ fi
|
||||
: "${ALIAS:=frank-zealot}"
|
||||
: "${BIND_ADDR:=0.0.0.0}"
|
||||
: "${LISTEN_PORT:=7771}"
|
||||
# ctx-size is the POOL llama.cpp divides across slots: 524,288 / 2 = 262,144
|
||||
# per conversation — every slot still at the model's trained window. The harness
|
||||
# contextWindow MUST quote the PER-SLOT number — a longer prompt is answered
|
||||
# with a 400, never truncated. Under exact KV the pool cannot stay at 786,432:
|
||||
# 32,768 B/token × 786,432 = 24,576 MiB, more than the card's whole addressable
|
||||
# 24,123 MiB before a single weight loads.
|
||||
: "${CTX_SIZE:=524288}"
|
||||
: "${PARALLEL:=2}"
|
||||
# ctx-size is the POOL llama.cpp divides across slots: 602,112 / 3 = 200,704 a
|
||||
# conversation. The harness contextWindow MUST quote the PER-SLOT number — a
|
||||
# longer prompt is answered with a 400, never truncated. The pool is priced by
|
||||
# the KV bill, not by the trained window: at 25,088 B/token (f16 K + q8_0 V) the
|
||||
# 602,112 pool costs 14,406.00 MiB and the armed drafter adds its own f16 KV over
|
||||
# the whole pool (2,352.00 MiB). Exact V at this pool (18,816 MiB) or the old
|
||||
# 786,432 pool at any dtype cannot boot inside the 24,123 MiB the driver leaves
|
||||
# addressable.
|
||||
: "${CTX_SIZE:=602112}"
|
||||
: "${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. It ALSO sets the compute-buffer size,
|
||||
# which is the term that decides whether an MTP draft context fits beside a KV
|
||||
# pool: 1024 hands back 1,306 MiB and was the arm that ran the drafter at three
|
||||
# q8_0 windows (frank-zealot.env carries the table).
|
||||
: "${BATCH_SIZE:=4096}"
|
||||
: "${UBATCH_SIZE:=2048}"
|
||||
# Exact KV: f16 K + f16 V — no quantization error anywhere in the attention
|
||||
# state. 32 KiB/token at this geometry (8 full-attention layers × 4 KV heads ×
|
||||
# 256 dim × 2 B × 2 caches); 16,384.00 MiB at the 524,288 pool. That is the whole
|
||||
# reason the slot count is 2: the same dtype at the old 786,432 pool is 24,576
|
||||
# MiB and dies at cudaMalloc. q8_0 K + q8_0 V (17,408 B/token, 13,056.00 MiB at
|
||||
# 786,432) is the documented fallback rung and funds three windows; CACHE_TYPE_V
|
||||
# alone is the halfway door. Explicit either way so the contract is visible in
|
||||
# the argv, and --flash-attn stays pinned because quantized V — the fallback —
|
||||
# is illegal without it (no true fp8 KV dtype exists in this build).
|
||||
# The logical batch is VRAM- and throughput-neutral at a fixed ubatch, so 6144
|
||||
# (1024×6) is free; the ubatch is what sets the compute-buffer size, and 1024 is
|
||||
# the rung every measured MTP arm here ran on (213.5 t/s at 1024 against 109.2
|
||||
# at 2048) — worth ~406 MiB at THIS shape, not the ~1,306 the old pool showed.
|
||||
: "${BATCH_SIZE:=6144}"
|
||||
: "${UBATCH_SIZE:=1024}"
|
||||
# Asymmetric KV: f16 K (the engine's own default, spelled out so the contract is
|
||||
# visible in argv) + q8_0 V. 8,192 elements per token per cache (8 full-attention
|
||||
# layers × 4 KV heads × 256 dim) ⇒ 16,384 B/token for K and 8,704 for V (q8_0 is
|
||||
# 1.0625 B/element — llama.cpp has no true fp8 KV dtype), 25,088 B/token and
|
||||
# 14,406.00 MiB at the 602,112 pool. That is the halfway door between exact KV
|
||||
# (32,768 B/token, which costs the third conversation and the drafter both) and
|
||||
# the fully quantized pair (17,408 B/token, which costs retrieval fidelity in the
|
||||
# keys); --flash-attn stays pinned because quantized V is illegal without it.
|
||||
: "${CACHE_TYPE_K:=f16}"
|
||||
: "${CACHE_TYPE_V:=f16}"
|
||||
# Speculative decoding defaults OFF, measured on this card and shape: the head
|
||||
# IS in the checkpoint (qwen35.nextn_predict_layers = 1 — MTP is a property of
|
||||
# the FILE, so the plain PQ2_0 sibling could not run it at any flag value), it
|
||||
# loads, and it accepts well (mean accepted length 2.41) — but the draft context
|
||||
# bills its own f16 KV over the WHOLE pool (4,096 B/position, 2,048 MiB at the
|
||||
# 524,288 pool) and at ubatch 2048 that squeeze halves decode: 167.2 → 109.2 t/s
|
||||
# on the q8_0 3-slot shape. The draft cache dtype IS a knob (--spec-draft-type-k
|
||||
# / --spec-draft-type-v, both default f16 — verified in this build's --help), so
|
||||
# an armed drafter can halve its own bill; there is no --spec-draft-model because
|
||||
# the draft rides the main model's weights. Card gate: quantized V alongside MTP
|
||||
# needs fork build >= prism-b10776, which is why LLAMA_BIN moved to the 10777
|
||||
# prebuilt; exact KV removes that gate but NOT the type-142 loader lock.
|
||||
# SPEC_TYPE=draft-mtp + UBATCH_SIZE=1024 is the documented MTP arm (213.5 t/s);
|
||||
# SPEC_TYPE=none strips the whole set (flags move together).
|
||||
: "${SPEC_TYPE:=none}"
|
||||
: "${CACHE_TYPE_V:=q8_0}"
|
||||
# Speculative decoding defaults ON: 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) and the draft context rides
|
||||
# the MAIN model's weights, so there is no --spec-draft-model. Its bill is the
|
||||
# target's whole pool at 4,096 B/position plus ~0.45 GiB of buffers at this
|
||||
# ubatch and a tripled recurrent state; CACHE_TYPE_V=q8_0 and UBATCH_SIZE=1024
|
||||
# above are what pay for it. The draft cache dtype IS a further knob
|
||||
# (--spec-draft-type-k / --spec-draft-type-v, both default f16 — verified in this
|
||||
# build's --help) and stays at its default here. Card gate: quantized V alongside
|
||||
# MTP needs fork build >= prism-b10776, which is why LLAMA_BIN is the 10777
|
||||
# prebuilt; the type-142 loader lock keeps it pinned regardless.
|
||||
# SPEC_TYPE=none is the rollback and strips the whole set (flags move together).
|
||||
: "${SPEC_TYPE:=draft-mtp}"
|
||||
: "${SPEC_DRAFT_N_MAX:=2}"
|
||||
: "${SPEC_DRAFT_N_MIN:=0}"
|
||||
: "${SPEC_DRAFT_P_MIN:=0}"
|
||||
|
||||
@@ -411,7 +411,7 @@ measurement that would settle it.
|
||||
|
||||
- Speculative decoding helps low-QPS, memory-bound, latency-focused workloads. It helps less at high QPS.
|
||||
- DFlash needs a non-causal attention backend; the ROCm flash_attn backend must be validated before production use.
|
||||
- The old `--swap-space` flag no longer exists in current vLLM. Use `--kv-offloading-size`.
|
||||
- The old `--swap-space` flag is retired; the live CPU-KV-offload knob pair is owned by [[RAM CPU Offloading in VLLM]].
|
||||
- `--speculative-config` is the preferred CLI. Legacy `--spec-method`, `--spec-model`, and `--spec-tokens` flags still exist.
|
||||
- Use `rejection_sample_method` default `strict` for lossless output.
|
||||
|
||||
|
||||
@@ -57,7 +57,10 @@ Key properties, per the project README and docs:
|
||||
|
||||
## NPU Hardware on Dankclaw
|
||||
|
||||
Dankclaw runs an AMD Ryzen AI MAX+ 395 (Strix Halo). The chip integrates an XDNA2 NPU rated at 50 TOPS (vendor spec: "NPU TOPS — Up to 50 TOPS", [amd.com product page](https://www.amd.com/en/products/processors/laptop/ryzen/ai-300-series/amd-ryzen-ai-max-plus-395.html)). It sits beside the Radeon 8060S iGPU and shares the same unified memory pool — 30 GiB exposed to the OS in the 2026-08 reading below; the box presents **121 GiB** today (`free -g` total 121, read 2026-10-18).
|
||||
Dankclaw runs an AMD Ryzen AI MAX+ 395 (Strix Halo). The chip integrates an XDNA2 NPU
|
||||
with 50 TOPS of compute and, crucially, **no dedicated VRAM** - it shares system memory
|
||||
via the IOMMU SVA mapping. The budget this page reasons about - 29 GiB usable at
|
||||
4800 MT/s LPDDR5X - is the 2026-08 host state; the live figure is [[Dankclaw System Specs]].
|
||||
|
||||
Live verification on Dankclaw, 2026-08-16:
|
||||
|
||||
@@ -89,10 +92,9 @@ Budget math for this fleet:
|
||||
| Dankserver (Ryzen 7 7700X) | 62 GB | no NPU |
|
||||
| FLM bench box (Kraken Point) | 32 or 96 GB | ~16 or ~48 GB |
|
||||
|
||||
2026-10-18 re-read: Dankclaw's OS now reports **121 GiB** (`free -g`) and
|
||||
Dankserver **246 GiB** (`free -g`), so the half-of-RAM rule would price the NPU
|
||||
budget at ~60 GiB and the 7700X box still has no NPU. The 30 GiB / 62 GB rows
|
||||
are the 2026-08-16 state; the table is moot while no NPU endpoint is deployed.
|
||||
The half-of-RAM gate scales with host RAM - live figures in [[Dankclaw System Specs]]
|
||||
and [[Dankserver System Specs]]. The live gate on Dankclaw is still `flm validate`
|
||||
(it owns the current number).
|
||||
|
||||
Weight sizes (approximate, Q4-class):
|
||||
|
||||
@@ -159,7 +161,10 @@ Qwen3.6-35B-A3B (Q4_KS), FLM v0.9.45, Kraken Point **96 GB DRAM**:
|
||||
| Decode t/s | 13.65 | 13.41 | 13.09 | 12.51 | 11.24 | 9.51 |
|
||||
| Prefill t/s | 78.98 | 118.04 | 156.43 | 197.93 | 218.84 | 221.96 |
|
||||
|
||||
Context: the current PP split (head + 3090 worker) decodes at 14-18 t/s for the 27B dense model. A 35B-A3B at ~10-14 t/s on an NPU with 48 GB of RAM budget is not a speed win over the existing topology.
|
||||
- PP and NPU together cost 2.2 GiB + NPU model + ~2 GiB of headroom. The then-current
|
||||
PP split (head + 3090 worker, 2026-08 era - see [[frank-redo-2026-08-17]]) decoded at
|
||||
14-18 t/s for the 27B dense model, so an NPU offload would free the head or upgrade
|
||||
the worker's model.
|
||||
|
||||
The vendor also claims "over 10x more power-efficient" versus GPU. That is a marketing claim. We have not measured it.
|
||||
|
||||
@@ -219,7 +224,12 @@ Notes:
|
||||
|
||||
## Promotion (2026-08-16)
|
||||
|
||||
`frank-zealot` now runs the NPU endpoint on Dankclaw (was the Dankserver llama.cpp service). Unit: `scripts/frank-zealot.service` (symlinked into `/etc/systemd/system`), wrapper `scripts/serve-flm-small.sh`, tunables `scripts/frank-zealot.env`. `LimitMEMLOCK=infinity` is set in the unit. Default model: `gemma4-it:e4b`. Swap `FLM_MODEL` in the env to switch.
|
||||
`frank-zealot` then ran the NPU endpoint on Dankclaw (2026-08; the name was reused
|
||||
twice after this window - see the banner - and the NPU unit today is `frank-npu`,
|
||||
masked). Unit: `scripts/frank-zealot.service` (symlinked into `/etc/systemd/system`),
|
||||
wrapper `scripts/serve-flm-small.sh`, tunables `scripts/frank-zealot.env`.
|
||||
`LimitMEMLOCK=infinity` is set in the unit. Default model: `gemma4-it:e4b`. Swap
|
||||
`FLM_MODEL` in the env to switch.
|
||||
|
||||
### Boot policy (2026-08-17)
|
||||
|
||||
@@ -256,11 +266,13 @@ The 4-stream wall times form a near-perfect 10.7 s ladder. Each stream self-repo
|
||||
Consequence: the NPU endpoint is a single lane. It suits low-frequency background work (triage, summarization, agent side-tasks). It does not suit interactive chat for several simultaneous users. The 27B llama.cpp service, by contrast, runs PARALLEL=2 with true concurrent slots.
|
||||
|
||||
## Test Runbook
|
||||
|
||||
This runbook targets a retired endpoint; keep it as the procedure if the NPU is
|
||||
ever revived. Do not run large model loads while the production service holds
|
||||
most of the unified pool — 30 GiB exposed to the OS in the 2026-08 reading,
|
||||
**121 GiB** today (`free -g`, read 2026-10-18). Check free RAM first, or stop
|
||||
ever revived.
|
||||
|
||||
**Host RAM availability.** The gate is half of system RAM by default - a relic of the
|
||||
install: the endpoint ran on a machine that presented 30 GiB of system RAM; on today's
|
||||
box that ceiling is far larger (live figure: [[Dankclaw System Specs]]). Check free RAM
|
||||
first, or stop
|
||||
the service the way the bench policy requires (`sudo systemctl stop
|
||||
frank-dragoon && sudo systemctl mask frank-dragoon` — self-healing would
|
||||
otherwise restart it). The NPU shares the unified pool with everything else on
|
||||
|
||||
@@ -119,5 +119,6 @@ re-download.
|
||||
|
||||
- [[Llama Cpp Runtimes]] — which fork ships which custom types
|
||||
- [[Llama Cpp Code Diff]] — the enum tables and code-level differences
|
||||
- [[frank-dragoon]] — the ftype-115 lock-in in production
|
||||
* [[kingjones-q8agent-rocmfpx]] — the era that served the ftype-115 file, and
|
||||
[[frank-dragoon]] for what runs there now
|
||||
- [[Llama Cpp Serving Sizing]] — pricing what a payload costs once it loads
|
||||
@@ -56,7 +56,10 @@ Lower values leave more GPU memory for KV cache. This reduces swap frequency.
|
||||
|
||||
### Swap Space / KV Offload
|
||||
|
||||
Recent vLLM releases removed the `--swap-space` engine argument — `CacheConfig` on `main` has no `swap_space` field ([source](https://raw.githubusercontent.com/vllm-project/vllm/main/vllm/config/cache.py), read 2026-10-18). Use `--kv-offloading-size` (`kv_offloading_size`, same file) to move KV cache to CPU RAM. In Podman, mount the host cache read-write for KV offload writes. The table below shows historical `--swap-space` guidance as a sizing reference.
|
||||
Current vLLM has no `--swap-space` engine argument, and the CPU KV-offload knob pair
|
||||
is owned by [[RAM CPU Offloading in VLLM]] (that page carries the upstream
|
||||
`CacheConfig` verification). In Podman, mount the host cache read-write for KV offload
|
||||
writes. The table below shows historical `--swap-space` guidance as a sizing reference.
|
||||
|
||||
| System RAM | Recommended Swap |
|
||||
|------------|------------------|
|
||||
|
||||
@@ -40,9 +40,9 @@ curl -s <health> # then a REAL completion
|
||||
```
|
||||
|
||||
Deploy caveat: "healthy after Ns" = systemd ACTIVE, not model LOADED —
|
||||
confirm with a real `/v1/chat/completions`. RTX 3090 (Ampere) has no
|
||||
FP8/FP4 tensor cores: FP8 weights run via upcast but must still fit; choose
|
||||
quant by size fit, then accept the compute path the card supports. Before
|
||||
confirm with a real `/v1/chat/completions`. On Ampere an FP8 file buys memory
|
||||
and no compute ([[Quantization by Hardware Architecture]]): choose the quant by
|
||||
size fit, then accept the compute path the card supports. Before
|
||||
choosing a smaller quant, state WHICH of the two you are buying — headroom,
|
||||
or a design change (a smaller quant does not change what the runtime can
|
||||
express; per-tensor cross-device placement stays refused under
|
||||
@@ -60,8 +60,8 @@ express; per-tensor cross-device placement stays refused under
|
||||
## 3. Fetch and verify (HF payloads)
|
||||
|
||||
**Inventory by QUANT NAME, never by extension** — large quants ship as
|
||||
`.partNofM` fragments (Q4_K_M = 37.00+37.00+36.97 GiB), so an
|
||||
`endswith(".gguf")` filter makes Q4_K_S look nonexistent. Check both
|
||||
`.partNofM` fragments, so an `endswith(".gguf")` filter makes a whole rung look
|
||||
nonexistent (AGENTS.md carries the worked fragment ledger). Check both
|
||||
mradermacher repos (`…-i1-GGUF` imatrix, `…-GGUF` plain) and the model's own
|
||||
repo; the tree API's `blobs=true` gives `lfs.oid` per file. Gated mirrors
|
||||
return 403 even with a valid token until the account accepts the gate —
|
||||
@@ -150,7 +150,7 @@ recoverable, drift is not.
|
||||
entry in `serverAppConfigs-docs/log.md`.
|
||||
2. `~/.omp/agent/models.yml`: `contextWindow` must equal the REAL per-slot
|
||||
context — llama.cpp logs `n_ctx_slot` per prompt (`--parallel N` divides
|
||||
the pool; e.g. 262144 native / 2 slots = 131072 per slot); vLLM reports
|
||||
the pool; e.g. a 524288 pool over 2 slots = 262144 per slot); vLLM reports
|
||||
`max-model-len` and its concurrency line. Overselling causes silent
|
||||
client-side overflow or a discovered-wall 400. Also `maxTokens` vs the
|
||||
engine's cap, `input` capabilities (text/image), and
|
||||
@@ -252,12 +252,10 @@ small/fast + image roles at the new endpoint; move the vision role WITH the
|
||||
vision tower (one owner per capability). `shared/homepage/services.json`: a
|
||||
card per endpoint, probed from the monitor's own host. Prometheus: only add
|
||||
a job if the runtime actually exposes Prometheus text — and re-measure rather
|
||||
than repeat, because that answer moves. This bullet once said halogen serves
|
||||
`/health`, `/cache`, `/v1/*` "and nothing else"; measured 2026-10-18 on
|
||||
Dankclaw, `curl -s http://127.0.0.1:7770/metrics` returns Prometheus text
|
||||
(`# HELP llamacpp:prompt_tokens_total …`, content-type
|
||||
`text/plain; version=0.0.4`) while an invented path 404s — so the surface has
|
||||
grown and a scrape target is expressible again.
|
||||
than repeat, because that answer moves: halogen's HTTP surface has grown twice
|
||||
since this page was written, and the endpoint list that matters for a scrape
|
||||
target — including whether `/metrics` answers — is [[frank-dragoon]]'s wire
|
||||
contract.
|
||||
|
||||
**4. Cutover, then verify by driving it.** `deploy.sh <target> --service
|
||||
<large> --service <small>` (order matters); per endpoint: `/health`,
|
||||
@@ -265,10 +263,10 @@ grown and a scrape target is expressible again.
|
||||
`reasoning_content`, a `tools` request returns `tool_calls`, an image
|
||||
request is answered (or refused with a named reason), the runtime's own
|
||||
speculation counters move, and the unit is `enabled` and `active`. All three LLM
|
||||
endpoints are **root** units today (`User=dank` + `WantedBy=multi-user.target` —
|
||||
`scripts/frank-archon.service:30,71`, `scripts/frank-dragoon.service:8,31`,
|
||||
`scripts/frank-zealot.service:8,26`), so linger is not what gates them; linger
|
||||
matters for rootless quadlets. Read the engine's boot ledger for a silent clamp. Finally spend one
|
||||
endpoints are **root** units today (`scripts/frank-archon.service`,
|
||||
`scripts/frank-dragoon.service`, `scripts/frank-zealot.service`), so linger is
|
||||
not what gates them; linger matters for rootless quadlets. Read the engine's boot
|
||||
ledger for a silent clamp. Finally spend one
|
||||
real request through the harness per changed role — routing labels lie; the
|
||||
servers' own journals do not.
|
||||
|
||||
@@ -285,4 +283,5 @@ predecessor "stopped" (the port race above).
|
||||
- [[Llama Cpp Serving Sizing]] — pricing the shape once it loads
|
||||
- [[RDNA4 MoE Expert Offload VRAM Budget]] — vLLM-side fit arithmetic
|
||||
- [[LLM Bench Windows and Measurement]] — measuring the new deployment
|
||||
- [[frank-archon]] — the retired tcclaviger vLLM endpoint (2026-09-29 → 10-05); the live Dankserver endpoint is [[frank-archon]]
|
||||
* [[frank-archon]] — the live Dankserver endpoint; the tcclaviger vLLM era
|
||||
(2026-09-29 → 10-05) is a row in its Eras table
|
||||
@@ -147,12 +147,14 @@ cut to GPTQ-w4 for the 3090 is a bench experiment that needs no new
|
||||
hardware; EXL3 (`convert.py` on the 3090, exllama serve) is the
|
||||
quality-per-bit extreme of the same menu.
|
||||
Caveats before any swap: the serving contract (OpenAI server,
|
||||
`reasoning_content`, vision mmproj, compaction-hub window
|
||||
200,704 ×2 slots) must be met by the challenger runtime, and the
|
||||
24 GB card runs ONE server — benching follows the stop-and-mask law.
|
||||
`reasoning_content`, vision mmproj, and the compaction-hub window the endpoint
|
||||
declares in its Current shape — [[frank-zealot]]) must be met by the challenger
|
||||
runtime, and the 24 GB card runs ONE server — benching follows the stop-and-mask
|
||||
law.
|
||||
|
||||
#### Worked example — the frank-archon w4dn8 recipe ported to the 3090
|
||||
(audit 2026-10-08, target `Qwen3.6-35B-A3B`)
|
||||
(audit 2026-10-08, target `Qwen3.6-35B-A3B`; sized at that date's hub shape —
|
||||
the window history is [[frank-zealot]]'s Eras table)
|
||||
**The methodology ports; the encoding must be translated.** llama.cpp
|
||||
decodes no GPTQ — the recipe re-expresses on GGUF-native terms:
|
||||
imatrix on our corpora (replaces the radiance grams; runs the 65 GiB
|
||||
@@ -253,8 +255,6 @@ top-8**, moe_intermediate 512, hidden 2048, MTP ×1, 262k ctx, vocab
|
||||
- Repo: `scripts/convert-swift15-w4dn8.sh`,
|
||||
`scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe`,
|
||||
`scripts/kld-gate-swift15.sh`, `scripts/build-llama-rdna4.sh`,
|
||||
`scripts/build-kairic-edge-gfx1151.sh`, [[frank-dragoon]],
|
||||
[[frank-dragoon]], [[qwen3.8-strix-optimizations]],
|
||||
[[frank-archon]],
|
||||
[[frank-zealot]],
|
||||
[[frank-dragoon]], [[GGUF Compatibility]].
|
||||
`scripts/build-kairic-edge-gfx1151.sh`; endpoints [[frank-archon]],
|
||||
[[frank-dragoon]], [[frank-zealot]]; method [[qwen3.8-strix-optimizations]]
|
||||
and [[GGUF Compatibility]].
|
||||
@@ -108,9 +108,10 @@ Use ROCmFP4 only with the ROCmFPX fork of llama.cpp. Upstream llama.cpp cannot r
|
||||
|
||||
## Choices for This Project
|
||||
|
||||
### Dankserver (RTX 3090, 24 GB, Ampere)
|
||||
### The RTX 3090 (24 GB, Ampere) — frank-zealot's card
|
||||
|
||||
The RTX 3090 has no native FP8 or FP4 tensor cores. Use:
|
||||
The 3090 has no native FP8 or FP4 tensor cores. It has sat in Dankclaw since
|
||||
2026-09-16; [[frank-zealot]]'s Eras table records the move off Dankserver. Use:
|
||||
|
||||
| Ranking | Format | Why |
|
||||
|---------|--------|-----|
|
||||
@@ -121,13 +122,17 @@ The RTX 3090 has no native FP8 or FP4 tensor cores. Use:
|
||||
|
||||
### Dankclaw (Strix Halo gfx1151, RDNA 3.5)
|
||||
|
||||
RDNA 3.5 has no native FP8 tensor cores. The APU shares memory. The active Ling-3.0-flash service runs vLLM-ROCm with the AMD Quark INT4 (W4A16) format (the jcbtc CIRU Strix-native bundle). INT4 weights keep the 124B model inside the 96 GiB frame buffer with a full-precision KV cache.
|
||||
RDNA 3.5 has no native FP8 tensor cores and the APU shares its memory, so the
|
||||
GPU budget is the GTT aperture and every live figure on the box is
|
||||
[[Dankclaw System Specs]]. What serves here today is [[frank-dragoon]]: a
|
||||
vendor `.hgn` checkpoint, a format this page cannot rank because no public
|
||||
quantizer exists for it ([[Quantization Production]]).
|
||||
|
||||
| Ranking | Format | Why |
|
||||
|---------|--------|-----|
|
||||
| 1 | INT4 W4A16 (Quark/vLLM) | active; fits 96 GiB; native ROCm kernels; concurrency |
|
||||
| 2 | MXFP4 (Quark) | same-bit, higher accuracy; beta on gfx1151 (A/B experiment) |
|
||||
| 3 | GGUF Q4_K_M / Q8_0 | works with upstream llama.cpp only |
|
||||
| 1 | `.hgn` (deployed) | the halogen engine's own format; production door is closed |
|
||||
| 2 | GGUF K/I quants, K_P blends | the only self-produced option on this card ([[Quantization Production]]) |
|
||||
| 3 | INT4 W4A16 (Quark) / MXFP4 (Quark) | the retired Ling-3.0-flash vLLM era, measured under the firmware-VGM frame buffer: [[ling-3.0-flash-trial]] |
|
||||
| 4 | FP8 (vLLM) | software emulation; memory savings only; no compute win |
|
||||
|
||||
## See Also
|
||||
|
||||
@@ -11,9 +11,19 @@ timestamp: 2026-08-28
|
||||
|
||||
## Overview
|
||||
|
||||
vLLM uses CPU RAM and swap space to extend GPU memory capacity. This allows larger models to run on GPUs with limited VRAM. vLLM does not offload model weights to CPU. Instead, vLLM moves KV cache data between GPU and CPU.
|
||||
Stock vLLM offloads **KV pages**, not weights: the scheduler moves KV cache between GPU
|
||||
and CPU. That is the upstream behaviour and it is NOT the whole story on our hosts — the
|
||||
tcclaviger/R4D fork ran a real **weight** offload (`--enable-expert-offload`, tiered
|
||||
expert placement, a pinned host pool per rank), which is how a 147 GiB MoE served from a
|
||||
246 GiB box: [[RDNA4 MoE Expert Offload VRAM Budget]] and the tcclaviger era row of
|
||||
[[frank-archon]]. First-hand in-repo evidence outranks a generic upstream claim about our
|
||||
own hosts ([[Model Swap and Rollback]] §10).
|
||||
|
||||
> **Current vLLM note:** Recent vLLM releases removed the `--swap-space` engine argument. CPU KV-cache offloading now uses `--kv-offloading-size`. See the parameters below for details.
|
||||
> **Verified 2026-10-19 against `vllm/config/cache.py` on `main`:** there is no
|
||||
> `swap_space` field — `--swap-space` is gone from current engine args. CPU KV offload is
|
||||
> `--kv-offloading-size`, and it activates **only** together with `--kv-offloading-backend`
|
||||
> (default `native`); both fields live in that one file. This page is the fleet's owner of
|
||||
> that knob contract — other pages link here instead of restating it.
|
||||
|
||||
## How CPU Offloading Works
|
||||
|
||||
@@ -42,9 +52,12 @@ This parameter controls the fraction of GPU memory used for KV cache.
|
||||
|
||||
Lower values leave more GPU memory for model weights. Higher values allow more concurrent requests but increase swap frequency.
|
||||
|
||||
### swap_space (removed) / kv-offloading-size (current)
|
||||
### swap_space (removed) / kv-offloading-size + kv-offloading-backend (current)
|
||||
|
||||
The `--swap-space` argument was removed from current vLLM engine args. Use `--kv-offloading-size` instead. The table below describes the historical `--swap-space` values for reference.
|
||||
`--swap-space` no longer exists in current engine args (verified above); the live pair is
|
||||
`--kv-offloading-size` with `--kv-offloading-backend`, and offload stays off until the size
|
||||
is set. The table below is the historical `--swap-space` sizing guidance, kept as a sizing
|
||||
reference only.
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
@@ -103,6 +116,9 @@ This parameter sets the KV cache data type.
|
||||
|
||||
## Memory Configuration Examples
|
||||
|
||||
All three examples carry the retired `--swap-space` syntax and are kept as sizing
|
||||
references; the live knob pair is named above.
|
||||
|
||||
### Small Model on Single GPU
|
||||
|
||||
```
|
||||
@@ -157,7 +173,7 @@ WARNING scheduler.py: Sequence group 0 is preempted by PreemptionMode.RECOMPUTE
|
||||
If you see frequent preemptions:
|
||||
|
||||
1. Increase `gpu_memory_utilization`
|
||||
2. Increase `swap_space`
|
||||
2. Raise the KV offload budget (`--kv-offloading-size`, with `--kv-offloading-backend`)
|
||||
3. Decrease `max_num_seqs`
|
||||
4. Increase `tensor_parallel_size`
|
||||
5. Increase `max_model_len` to match actual usage
|
||||
@@ -169,7 +185,8 @@ If you see frequent preemptions:
|
||||
- Reduce `max_num_seqs` to limit concurrent requests
|
||||
- Disable CUDA graphs with `--enforce-eager` to save memory
|
||||
- Set `compilation_config.cudagraph_capture_sizes` to smaller values
|
||||
- Use `--kv-cache-memory` to skip memory profiling
|
||||
- Set `--kv-cache-memory-bytes` to pin the KV budget instead of letting the profiler guess
|
||||
(the flag our own `scripts/patches/gpu_worker-kv-cap.patch` reads)
|
||||
- Limit multi-modal inputs with `limit_mm_per_prompt`
|
||||
|
||||
## Environment Variables
|
||||
|
||||
@@ -7,6 +7,12 @@ timestamp: 2026-09-03
|
||||
---
|
||||
|
||||
# Running Qwen3.6 27B FP8 on Dankclaw
|
||||
> **RETIRED - vLLM era (before the 2026-08-17 redo).** Nothing on this page serves
|
||||
> today: no vLLM or ROCm process runs on Dankclaw, and the iGPU endpoint at
|
||||
> `192.168.0.143:7770` is [[frank-dragoon]] (halogen flash-server). The page stays as
|
||||
> the era record of the Qwen3.6-27B-FP8 vLLM bring-up and the Strix Halo ROCm
|
||||
> recipes ([[frank-redo-2026-08-17]]). The hardware table below carries the era's
|
||||
> host figures; live ones are [[Dankclaw System Specs]].
|
||||
|
||||
## Model Information
|
||||
|
||||
@@ -29,6 +35,7 @@ Qwen3.6-27B is a dense vision-language model from the Qwen3.6 series. It uses 27
|
||||
| Vision Encoder | Yes (image-text-to-text) |
|
||||
|
||||
## Hardware: Dankclaw
|
||||
*Era figures (2026-08). The host has changed since - [[Dankclaw System Specs]].*
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
@@ -55,7 +62,11 @@ The FP8 variant requires approximately 16-17 GB. ThinkingCap-FP8 requires ~29 GB
|
||||
|
||||
## Prerequisites
|
||||
|
||||
> **Production note:** Dankclaw's primary service now runs vLLM-ROCm with the Ling-3.0-flash CIRU INT4 model (see `scripts/frank-dragoon.service`). This page documents the alternative vLLM FP8 deployment of Qwen3.6-27B.
|
||||
> **Era note (2026-08, superseded twice):** at writing time Dankclaw's primary
|
||||
> service was the vLLM-ROCm Ling-3.0-flash CIRU INT4 trial ([[ling-3.0-flash-trial]],
|
||||
> retired 2026-08-17), and this page documented the alternative vLLM FP8 deployment
|
||||
> of Qwen3.6-27B. Both paths are dead; `scripts/frank-dragoon.service` today is the
|
||||
> halogen flash-server unit ([[frank-dragoon]]).
|
||||
|
||||
Install vLLM for ROCm. Version 0.24.0+ is required for Qwen3.6 with speculative decoding:
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ vllm serve model \
|
||||
|
||||
- `gpu_memory_utilization`: Set lower (0.8-0.85) to leave room for CPU operations
|
||||
- `max_model_len`: Match actual context needs
|
||||
- The old `--swap-space` argument was removed from current vLLM — `CacheConfig` on `main` carries no `swap_space` field ([source](https://raw.githubusercontent.com/vllm-project/vllm/main/vllm/config/cache.py), read 2026-10-18). Use `--kv-offloading-size` (`kv_offloading_size`, same file) for CPU KV offload
|
||||
- `--swap-space` is retired from current vLLM; the CPU KV-offload knob pair, with the upstream `CacheConfig` verification, is owned by [[RAM CPU Offloading in VLLM]]
|
||||
|
||||
### NUMA Binding
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ vllm serve meta-llama/Llama-3.1-70B-Instruct --tensor-parallel-size 4
|
||||
vllm serve Qwen/Qwen2.5-1.5B-Instruct --quantization fp8
|
||||
|
||||
# With GPU memory control
|
||||
vllm serve Qwen/Qwen2.5-1.5B-Instruct --gpu-memory-utilization 0.8 --swap-space 8
|
||||
vllm serve Qwen/Qwen2.5-1.5B-Instruct --gpu-memory-utilization 0.8 --kv-offloading-size 8 --kv-offloading-backend native
|
||||
```
|
||||
|
||||
### Common API Endpoints
|
||||
@@ -79,7 +79,7 @@ vllm serve Qwen/Qwen2.5-1.5B-Instruct --gpu-memory-utilization 0.8 --swap-space
|
||||
|-----------|-------------|---------------|
|
||||
| tensor_parallel_size | Number of GPUs for model sharding | 1-8 |
|
||||
| gpu_memory_utilization | Fraction of GPU memory for KV cache (default 0.92) | 0.7-0.95 |
|
||||
| swap_space | Removed in current vLLM; use `--kv-offloading-size` | n/a |
|
||||
| swap_space | Retired; live knob pair in [[RAM CPU Offloading in VLLM]] | n/a |
|
||||
| max_model_len | Maximum context length | auto |
|
||||
| max_num_batched_tokens | Max tokens per batch | 2048-8192 |
|
||||
| max_num_seqs | Max sequences per batch | 128-512 |
|
||||
|
||||
@@ -140,7 +140,7 @@ curl http://localhost:8000/v1/chat/completions \
|
||||
| `tensor_parallel_size` | 1 | Number of GPUs for tensor parallelism |
|
||||
| `pipeline_parallel_size` | 1 | Number of GPUs for pipeline parallelism |
|
||||
| `gpu_memory_utilization` | 0.92 | Fraction of GPU memory for KV cache |
|
||||
| `swap_space` | removed | Removed in current vLLM; use `--kv-offloading-size` |
|
||||
| `swap_space` | — | retired; the live CPU-KV-offload knob pair is in [[RAM CPU Offloading in VLLM]] |
|
||||
| `enforce_eager` | false | Use eager mode (no CUDA graphs) |
|
||||
| `max_num_batched_tokens` | 2048 | Max tokens per batch |
|
||||
| `max_num_seqs` | 128 | Max sequences per batch |
|
||||
@@ -167,7 +167,7 @@ curl http://localhost:8000/v1/chat/completions \
|
||||
| Argument | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `gpu_memory_utilization` | 0.92 | Fraction of GPU memory for KV cache |
|
||||
| `swap_space` | removed | Removed in current vLLM; use `--kv-offloading-size` |
|
||||
| `swap_space` | — | retired; the live CPU-KV-offload knob pair is in [[RAM CPU Offloading in VLLM]] |
|
||||
| `kv_cache_dtype` | auto | KV cache data type (auto, fp8, fp8_e5m2) |
|
||||
| `max_model_len` | auto | Maximum context length |
|
||||
| `num_gpu_blocks` | auto | Number of GPU blocks |
|
||||
|
||||
@@ -14,6 +14,10 @@ timestamp: 2026-09-03
|
||||
|
||||
> **SUPERSEDED 2026-08-17.** The Frank redo retired this topology.
|
||||
> See [[frank-redo-2026-08-17]] and [[deployment]] for the active configuration.
|
||||
> The `frank-zealot` below is the 2026-08 service: llama.cpp on the RTX 3090
|
||||
> **while that card sat in Dankserver**. Today's [[frank-zealot]] is a different host
|
||||
> (Dankclaw), payload and runtime build, though the same card class - this crash
|
||||
> signature is not a live fault.
|
||||
|
||||
# Dankserver VRAM and RAM Growth Under Load
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: runbook
|
||||
title: frank-medium Model Swap
|
||||
description: How to swap the checkpoint behind the frank-medium endpoint (Dankserver :7770, vllm-radiance TP=2) — the streamlining toolchain (hf-manifest.py, hf-fetch.sh, swap-frank-medium.sh), the quantization-aware entrypoint contract, the quadlet edit, the deploy, the capability battery, and the rollback path.
|
||||
description: RETIRED-era procedure (frank-medium is a retired endpoint; `:7770` on Dankserver is [[frank-archon]] today and the live swap law is [[Model Swap and Rollback]]) — how a checkpoint swap behind frank-medium worked: the streamlining toolchain (hf-manifest.py, hf-fetch.sh, swap-frank-medium.sh), the quantization-aware entrypoint contract, the quadlet edit, the deploy, the capability battery, and the rollback path.
|
||||
tags:
|
||||
- frank-medium
|
||||
- dankserver
|
||||
@@ -14,12 +14,13 @@ timestamp: 2026-09-20
|
||||
|
||||
# frank-medium Model Swap
|
||||
|
||||
Swapping the checkpoint behind **frank-medium** (Dankserver `:7770`,
|
||||
vllm-radiance TP=2) is a four-step process, and the toolchain below drives all
|
||||
of it. The design goal (2026-09-20, after the MXFP4 → FP8 swap) is that a swap
|
||||
is **one command**: generate a manifest, download it verified, point the
|
||||
quadlet at it, deploy. The entrypoint is quantization-aware, so the only repo
|
||||
change a swap makes is the quadlet's four env vars.
|
||||
**Historical.** frank-medium is a retired endpoint — `:7770` on Dankserver is
|
||||
[[frank-archon]] today, and the live procedure is [[Model Swap and Rollback]].
|
||||
This page records how a swap worked in that era: a four-step process whose design
|
||||
goal (2026-09-20, after the MXFP4 → FP8 swap) was that a swap be **one command** —
|
||||
generate a manifest, download it verified, point the quadlet at it, deploy. The
|
||||
entrypoint was quantization-aware, so the only repo change a swap made was the
|
||||
quadlet's four env vars.
|
||||
|
||||
## Why this exists
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: configuration
|
||||
title: frank-medium vllm-radiance MXFP6 Runbook
|
||||
description: ACTIVE frank-medium runbook — vllm-radiance serving hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6 (ParoQuant MXFP6 W4A8, DFlash2 drafter, unquantized bf16 KV) as ONE tensor-parallel process (TP=2) on Dankserver's two AMD Radeon AI PRO R9700 (gfx1201). Cut to MXFP6 on 2026-09-21 (PR #51's launch6_p kernel, image 0.13.0-e060895); the MXFP4 checkpoint (just1moremodel/Qwen3.8-27B-Uncensored-MXFP4-awq, the 2026-09-21 shape) is the on-disk rollback. Model-swap process: [[frank-medium-model-swap]]. The sections below describe the 2026-09-16 two-replica era.
|
||||
description: RETIRED era runbook (endpoint renamed frank-archon 2026-10-10; this shape is a row in [[frank-archon]]'s Eras table) — vllm-radiance serving hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6 (ParoQuant MXFP6 W4A8, DFlash2 drafter, unquantized bf16 KV) as ONE tensor-parallel process (TP=2) on Dankserver's two AMD Radeon AI PRO R9700 (gfx1201). Cut to MXFP6 on 2026-09-21 (PR #51's launch6_p kernel, image 0.13.0-e060895); the MXFP4 checkpoint (just1moremodel/Qwen3.8-27B-Uncensored-MXFP4-awq, the 2026-09-21 shape) is the on-disk rollback. Model-swap process: [[frank-medium-model-swap]]. The sections below describe the 2026-09-16 two-replica era.
|
||||
tags:
|
||||
- frank-medium
|
||||
- dankserver
|
||||
@@ -19,7 +19,8 @@ timestamp: 2026-09-21
|
||||
|
||||
# frank-medium vllm-radiance MXFP6 Runbook
|
||||
|
||||
The ACTIVE frank-medium deployment: the `ggz14/vllm-radiance-mxfp4:0.13.0-e060895`
|
||||
**RETIRED era (2026-09-21 → 10-10; the endpoint is [[frank-archon]] today and this
|
||||
shape is one row in its Eras table.)** The deployment was the `ggz14/vllm-radiance-mxfp4:0.13.0-e060895`
|
||||
image (built from PR #51's head, tagged locally as
|
||||
`localhost/radiance-vllm:0.13.0-e060895`) plus the `radiance-vllm-mxfp4`
|
||||
patches at e060895, serving `hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6`
|
||||
|
||||
@@ -14,14 +14,10 @@ The pipeline-parallel split and the FastFlowLM NPU endpoint were retired.
|
||||
The old scripts moved to `old/scripts/` in this repository.
|
||||
|
||||
> **HISTORY — the topology below is two fleet generations dead.** Today the
|
||||
> fleet is three endpoints: [[frank-archon]]
|
||||
> (`192.168.0.235:7770`, radiance 1.0.13, 2×R9700 TP=2),
|
||||
> [[frank-dragoon]] (`192.168.0.143:7770`, halogen
|
||||
> flash-server 0.16.2 on the iGPU) and
|
||||
> [[frank-zealot]] (`192.168.0.143:7771`, llama.cpp
|
||||
> PrismML fork build 10754 on the RTX 3090 — the card that sat in Dankserver
|
||||
> until 2026-09-15). No vLLM and no ROCm endpoint serves anything; there is no
|
||||
> pipeline parallelism and no NPU endpoint.
|
||||
> fleet is three endpoints, each owned by its page: [[frank-archon]]
|
||||
> (`192.168.0.235:7770`), [[frank-dragoon]] (`192.168.0.143:7770`) and
|
||||
> [[frank-zealot]] (`192.168.0.143:7771`). No vLLM and no ROCm endpoint serves
|
||||
> anything; there is no pipeline parallelism and no NPU endpoint.
|
||||
|
||||
## New Topology
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
type: reference
|
||||
title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork 10777), Ternary-Bonsai-2-9B PQ2_0-MTP, 2 × 262144, exact f16 KV"
|
||||
title: "frank-zealot on Dankclaw — llama.cpp (PrismML fork 10777), Ternary-Bonsai-2-9B PQ2_0-MTP, 3 × 200704, f16 K + q8_0 V, MTP armed"
|
||||
description: "The one frank-zealot reference: the live shape on Dankclaw's RTX 3090 (:7771) exactly as scripts/frank-zealot.env ships it, every era with its rollback path, the deduplicated laws of this card, the wire contract, and the owning files."
|
||||
tags:
|
||||
- llm
|
||||
@@ -12,10 +12,11 @@ tags:
|
||||
timestamp: 2026-10-18
|
||||
---
|
||||
|
||||
# frank-zealot — llama.cpp (PrismML) Bonsai-2-9B PQ2_0-MTP, 2 × 262,144, exact f16 KV
|
||||
# frank-zealot — llama.cpp (PrismML) Bonsai-2-9B PQ2_0-MTP, 3 × 200,704, f16 K + q8_0 V, MTP armed
|
||||
|
||||
Dankclaw's RTX 3090 endpoint — `http://192.168.0.143:7771`, wire id `frank-zealot`, the fleet's **compaction hub**
|
||||
(widest window it has). One page per endpoint: eras are rows below, not pages. Siblings: [[frank-archon]],
|
||||
(three conversations at 200,704 each, the window the other two endpoints also cap at). One page per
|
||||
endpoint: eras are rows below, not pages. Siblings: [[frank-archon]],
|
||||
[[frank-dragoon]]. Deploy with `./scripts/deploy.sh dankclaw --service frank-zealot`; swap mechanics in
|
||||
[[Model Swap and Rollback]], sizing math in [[Llama Cpp Serving Sizing]]. `scripts/frank-zealot.env` and
|
||||
`scripts/serve-frank-zealot.sh` are the authority for the shape; where any prose disagrees with them, the prose
|
||||
@@ -31,72 +32,87 @@ is the bug.
|
||||
| Runtime | `/home/dank/GitPackages/llama-prism-b10777/llama-server` — PrismML **fork**, `0.2.0-dev (build 10777, commit e3de71522)`; the only loader for this payload |
|
||||
| Trio | `scripts/frank-zealot.service` (root, relinked, `User=dank`, `Restart=on-failure`, `RestartSec=10`, `LimitMEMLOCK=infinity`, `TimeoutStopSec=180`) · `scripts/frank-zealot.env` · `scripts/serve-frank-zealot.sh` |
|
||||
| Address / alias | `BIND_ADDR=0.0.0.0`, `LISTEN_PORT=7771`, `ALIAS=frank-zealot` (a flag, not a fact) |
|
||||
| Flags | `--ctx-size 524288 --parallel 2` ⇒ 262,144 per slot · `--cache-type-k f16 --cache-type-v f16` (exact, **16,384 MiB**) · `--flash-attn on` · `--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`** |
|
||||
| Flags | `--ctx-size 602112 --parallel 3` ⇒ 200,704 per slot · `--cache-type-k f16 --cache-type-v q8_0` (asymmetric, **14,406.00 MiB**) · `--flash-attn on` · `--batch-size 6144 --ubatch-size 1024` · `--n-gpu-layers 99` · `--jinja --reasoning auto --reasoning-format deepseek` · `--metrics` · `--mmproj` · `--log-verbosity 4` · `--presence-penalty 1.0` · **`--spec-type draft-mtp`** with `--spec-draft-n-max 2 --spec-draft-n-min 0 --spec-draft-p-min 0` |
|
||||
|
||||
**Why this shape.** `general.architecture qwen35`: 33 blocks (32 trunk + `blk.32` = the MTP block),
|
||||
`full_attention_interval 4` ⇒ 8 full-attention + 24 Gated DeltaNet, 4 KV heads × 256 dim, `nextn_predict_layers 1`
|
||||
(the head **is in the file**), trained window 262,144, `general.sampling.*` 1.0/0.95/20. Only the 8 full layers bill
|
||||
per token (32,768 B at exact f16 ⇒ 16,384 MiB over the pool) and the DeltaNet layers add ~50 MiB of recurrent state
|
||||
**per slot**: exact KV costs the third conversation and nothing else.
|
||||
per token and the DeltaNet layers add ~50 MiB of recurrent state **per slot**, so the shape is a single budget
|
||||
equation at 25,088 B/token (f16 K 16,384 + q8_0 V 8,704): **14,406.00 MiB** of cache over a 602,112 pool +
|
||||
**2,352.00 MiB** of draft cache (blk.32 at its own f16 rate, billed over the WHOLE pool) + ~7.7 GiB of weights,
|
||||
projector, buffers and states, inside the 24,123 MiB the driver leaves addressable. Exact V (32,768 B/token ⇒
|
||||
18,816 MiB here) buys neither the third conversation nor the drafter; the fully quantized pair (17,408 B/token)
|
||||
buys both and costs the keys their precision.
|
||||
|
||||
**MTP is OFF by measurement, not by absence of a head.** The drafter is *good* — 2026-10-18 measured
|
||||
acceptance 0.798 / 0.617 per position, mean accepted length 2.41, and the publisher's card says depth 2 works
|
||||
best — and it still loses here, because the draft context is built against the *target's* pool: `blk.32` bills
|
||||
4,096 B/position at its default f16 ⇒ 2,048 MiB at 524,288 positions on a trunk that projects 21,234 MiB of the
|
||||
24,123 MiB addressable (that projection predicts 2,889 MiB free; replace it with the measured pair). Exact KV and a
|
||||
drafter cannot share this card. `--spec-draft-type-k`/`--spec-draft-type-v` (default f16, verified in this build's
|
||||
`--help`) would cut the draft to 1,088 MiB — legal on b10777, **never booted beside exact KV**. There is no
|
||||
`--spec-draft-model`: the MTP draft rides the main model's weights. The env carries the recipe
|
||||
(`SPEC_DRAFT_N_MAX=2`, `SPEC_DRAFT_N_MIN=0`, `SPEC_DRAFT_P_MIN=0`); `SPEC_TYPE=none` never sends it.
|
||||
**MTP is ON (Frank, 2026-10-19).** The drafter was always good — 2026-10-18 measured acceptance 0.798 / 0.617 per
|
||||
position, mean accepted length 2.41, and the publisher's card says depth 2 works best — and arming it is a
|
||||
budget decision, not a flag: the draft context is built against the *target's* pool, so `blk.32` bills 4,096
|
||||
B/position (2,352.00 MiB at 602,112) plus 480.97 MiB of its own CUDA0 buffers, and it triples the recurrent
|
||||
state (452.25 MiB for three cells against 150.75 with the drafter off). `CACHE_TYPE_V=q8_0` is what pays that:
|
||||
7,680 B/token cheaper than exact V = **4,410.00 MiB** at this pool. `UBATCH_SIZE=1024` — the rung every measured
|
||||
MTP arm here ran on — contributes only ~406 MiB at THIS shape, not the ~1,306 the 786,432-pool census showed,
|
||||
and `UBATCH_SIZE=2048` beside an armed drafter is still the shape that halved decode (167.2 → 109.2 t/s).
|
||||
`--spec-draft-type-k`/`--spec-draft-type-v` (default f16, verified in this build's `--help`) would cut the draft's bill again and stay at their default; there is no
|
||||
`--spec-draft-model` — the draft rides the main model's weights, so a draft file would load the trunk twice.
|
||||
`SPEC_TYPE=none` is the rollback and strips the whole set.
|
||||
|
||||
**Harness contract** (`~/.omp/agent/models.yml`, provider `llamacpp (dankclaw-3090)`, id `frank-zealot`):
|
||||
`contextWindow: 262144` — **the per-slot window**, because llama.cpp divides `--ctx-size` across slots and answers a
|
||||
`contextWindow: 200704` — **the per-slot window**, because llama.cpp divides `--ctx-size` across slots and answers a
|
||||
longer prompt with a 400, never a truncation — plus `maxTokens: 16384`, `input: [text, image]`,
|
||||
`compat.reasoningContentField: reasoning_content`, `compat.reasoningEffortMap` (**load-bearing**: the template takes
|
||||
only `xhigh|medium|low` and raises on anything else), `extraBody` 1.0 / 0.95 / 20 / `min_p 0`, and the
|
||||
`compactionModel` of [[frank-archon]], [[frank-dragoon]] **and** frank-zealot all pointing here.
|
||||
|
||||
**The exact-KV boot, measured** (2026-10-18 evening, under the unit, verbosity 4). KV **16,384.00 MiB** =
|
||||
32,768 B/token × 524,288 positions, to the MiB — rate derivation and ledger agree with no slop, which is what
|
||||
buries the doc's old "f16 needs 26,112 MiB and cannot boot" figure for good: that was the 27B's 34,816 B/token
|
||||
applied to the 9B. RS **100.50 MiB** (2 cells, one 50.25 MiB cell lighter than the q8_0 era), compute
|
||||
1,408.06 + 1,088.07 CUDA_Host, mmproj worst case **868.32 MiB** charged *after* the engine's own fit check,
|
||||
settled `nvidia-smi` **21,176 used / 2,949 free** of 24,123, `n_slots = 2`, `n_ctx_slot = 262144`,
|
||||
`DropInPaths` empty, no clamp line, no `cudaMalloc` error — within 24 MiB of the pre-flight projection. That is
|
||||
a desktop margin: the ~2 GiB of slack is what an image storm has to live inside.
|
||||
**No rates, by law.** Frank ordered the reslice, not a number: the evidence a swap needs is
|
||||
the seam's fit gate, the deployer's health gate, and the boot ledger below. The previous era's
|
||||
exact-KV boot and its rate floors live in that era's row; those floors were measured while
|
||||
another consumer drove the other slot (56 prompts in three minutes, 96 % GPU-util), the exact
|
||||
condition a bench window exists to exclude.
|
||||
|
||||
**Rates on this shape are FLOORS, not the shape's rates** — measured the same evening greedy with `ignore_eos`
|
||||
at 512 tokens, first request discarded, while another consumer drove the other slot (56 new prompts in three
|
||||
minutes, 96 % GPU-util): the exact condition the bench window exists to exclude, and one contended deep arm
|
||||
even came back truncated (a 512 KB body accepted as `prompt_n = 4`).
|
||||
**The 2026-10-19 boot, measured** — verbosity-4 ledger of `ba69ea5`, started 03:21:56 PDT.
|
||||
Recorded here because projections drift, and nowhere else:
|
||||
|
||||
| Arm | q8_0 3-slot record | f16 2-slot, contended |
|
||||
|---|---|---|
|
||||
| single-stream decode | 168.7 | **134.1 – 141.5** (145.8 and 163.2 in a lighter minute) |
|
||||
| concurrent aggregate | ≈300 across 3 (102.8 each) | **256.6 / 262.4 / 265.1** across 2 (≈128–133 each) |
|
||||
| prefill at ~106k | 2,877 at 106,418 | **3,159.5 at 106,282** |
|
||||
| decode at 106k depth | never measured | **71.5** |
|
||||
| Term | Ledger |
|
||||
|---|---|
|
||||
| Target KV over the 602,112 pool | **14,406.00 MiB** — K (f16) 9,408.00 + V (q8_0) 4,998.00, `200704 cells, 8 layers, 3/3 seqs` |
|
||||
| Draft KV (`blk.32`, its own f16 context) | **2,352.00 MiB** — 1,176.00 + 1,176.00, `200704 cells, 1 layers, 3/3 seqs`: the whole pool, again |
|
||||
| Recurrent state | **452.25 MiB** — `3 cells, 32 layers, 3 seqs, 2 rs_seq`, R (f32) 20.25 + S (f32) 432.00 |
|
||||
| Compute buffers | target 1,665.00 CUDA0 + 424.88 CUDA_Host; draft context 480.97 + 424.86 |
|
||||
| Weights | 2,283.29 CUDA0 + 257.66 CPU-mapped |
|
||||
| Projector | 595.29 model + 248.10 compute + 24.93 CPU (its 868.32 worst case is still charged AFTER the engine's fit check) |
|
||||
| Engine's own verdict | projected 21,639 vs 23,404 free, then **"cannot meet free memory target of 1,867 MiB, need to reduce device memory by 102 MiB"** |
|
||||
| Settled | **22,870 used / 1,255 free**, `n_slots = 3`, `n_ctx_slot = 200704`, `kv_unified = false` |
|
||||
|
||||
What the floors already support: per-conversation concurrency **improved** (two streams hold ~130 t/s each where
|
||||
three held 102.8), endpoint aggregate sits ~12 % under the 3-slot record, prefill beat the record while
|
||||
contended, and single-stream decode is between parity and −20 %. The 1.882× KV byte bill is real; its true size
|
||||
on this card is still unmeasured, so quoting 134 or 163 t/s as "the" number is a guess in either direction —
|
||||
re-run the same arms with the box quiet before a figure is carried anywhere else.
|
||||
This is the first boot on this card that does **not** meet llama.cpp's own free-memory target,
|
||||
and 1,255 MiB settled sits under the ~2 GiB band every previous era held. It boots, `/health`
|
||||
answers, and the seam's gate still passes — it priced 22,428 MiB, conservative by 442 against
|
||||
this ledger. The cheapest rungs back into the band are `CTX_SIZE=401408` (two conversations) or
|
||||
`SPEC_TYPE=none`; both are one line and neither changes the payload.
|
||||
|
||||
**The drafter is engaged, measured on the same boot** (three ordinary requests, not a bench):
|
||||
acceptance 0.706 and 0.763 accepted-per-generated, mean accepted length 2.41 / 2.53,
|
||||
per-position rates (0.882, 0.765) and (0.871, 0.699) — depth 2 earning its keep at 0.3–1.5 MiB
|
||||
of draft state per saved prompt. The three probes that prove a swap: chat → `"zealot alive"`
|
||||
with 448 completion tokens (at `max_tokens: 64` the same request returns EMPTY content with
|
||||
`finish_reason: length` — the thinking-budget trap, not a fault); tools →
|
||||
`finish_reason: tool_calls` with `{"a":24,"b":7}`; vision → a 512 px solid-red PNG answers
|
||||
"Red" at `prompt_tokens` 280.
|
||||
|
||||
**Fallback rungs**, cheapest first — one line of `frank-zealot.env` each, and each must keep the seam's fit gate green:
|
||||
|
||||
| Knob | Effect |
|
||||
| Knob | Effect from the shipped shape |
|
||||
|---|---|
|
||||
| `UBATCH_SIZE=1024` | hands back 1,306 MiB of compute buffers; prefill −17 %; it is also the arm that ran the drafter at three windows (213.5 t/s) |
|
||||
| `CACHE_TYPE_V=q8_0` | exact K + quantized V: KV 16,384 → **12,544 MiB**, freeing 3,840 (llama.cpp has no true fp8 KV dtype — q8_0 is the 8-bit class) |
|
||||
| `CTX_SIZE=401408` | two 200,704 conversations, exact KV at 12,544 MiB (same 3,840 freed) — `contextWindow` MUST follow the per-slot number |
|
||||
| `CTX_SIZE=786432 PARALLEL=3 CACHE_TYPE_K/V=q8_0` | the era below this one: 13,056 MiB of cache, the third conversation back |
|
||||
| `CACHE_TYPE_K/V=q4_0` | 10,240 B/token ⇒ 5,120 MiB (the 27B era measured q4_0 at 16,392 used / 7,733 free at 2 × 200,704) |
|
||||
| `SPEC_TYPE=none` | the drafter off: hands back its 2,352.00 MiB of cache, 480.97 MiB of CUDA0 buffers and the 301.50 MiB of rollback planes it adds to the recurrent state (~3.1 GiB), at the cost of the ~+28 % decode the 2026-10-18 census measured whenever the card has room |
|
||||
| `UBATCH_SIZE=2048` | +4.6 % prefill and only ~406 MiB of compute buffers at THIS shape (the ~1,306 MiB the 786,432-pool census showed does not transfer) — and the rung that HALVED decode beside an armed drafter (167.2 → 109.2 t/s) |
|
||||
| `CTX_SIZE=401408` | two 200,704 conversations: frees 4,802 MiB of cache — `contextWindow` MUST follow the per-slot number |
|
||||
| `CACHE_TYPE_V=f16` | exact V: +4,410 MiB at this pool with the drafter armed, which is a `cudaMalloc` death the seam refuses — legal only once the drafter or a slot is gone |
|
||||
| `CACHE_TYPE_K=q8_0` | the fully quantized pair (17,408 B/token): frees 4,410 MiB and moves the quantization error into the keys |
|
||||
|
||||
## Eras
|
||||
|
||||
| Window | Payload | Runtime | Shape | Replaced because | Rollback |
|
||||
|---|---|---|---|---|---|
|
||||
| 2026-10-18 21:06 → 2026-10-19 | the same 9B MTP file and projector | PrismML fork 10777 | 2 × 262,144 (pool 524,288), exact f16 K+V = 16,384.00 MiB, `SPEC_TYPE=none`, batch 4096 / ubatch 2048; boot ledger KV 16,384.00 / RS 100.50 / compute 1,408.06 + 1,088.07 CUDA_Host / mmproj worst case 868.32 charged after the fit check, settled **21,176 used / 2,949 free**, `n_ctx_slot = 262144`, within 24 MiB of the projection; floors 134.1–141.5 t/s single-stream, 256.6–265.1 aggregate over two, 3,159.5 t/s prefill at 106,282, 71.5 t/s decode at 106k depth (contended — floors, not the shape's rates) | Frank ordered the third conversation back AND the drafter armed, and exact V funds neither: it bills 18,816 MiB at the pool that pays for the third slot, leaving nothing for the draft's 2,352 | one line each — `CTX_SIZE=524288 PARALLEL=2 CACHE_TYPE_V=f16 BATCH_SIZE=4096 UBATCH_SIZE=2048 SPEC_TYPE=none` — and redeploy; no payload move |
|
||||
| 2026-10-18 13:19 → same evening (the boot `68afc6d` replaced) | the same 9B MTP file and projector | PrismML fork 10777 | 3 × 262,144 (pool 786,432), q8_0 K+V = 13,056 MiB, settled 17,956 / 6,169 MiB; 168.7 t/s decode (three runs 168.4–168.7), ~300 t/s over three streams (102.8 each), 2,877 t/s prefill at 106,418 | Frank ordered unquantized KV, and that pool cannot survive it: 24,576 MiB of f16 cache > 24,123 addressable | one env line — `CTX_SIZE=786432 PARALLEL=3 CACHE_TYPE_K/V=q8_0` — and redeploy; no payload move |
|
||||
| 2026-10-18, 24 min (arm under the unit) | `Bonsai2-PTQ1_0/Ternary-Bonsai-2-27B-PTQ1_0.gguf` 5,946,648,928 B + hardlinked Q8 mmproj | PrismML fork 10777 | 2 × 200,704, batch/ubatch 3072/1536 | Frank scratched the 27B plan — ~48 t/s decode vs 76.6 for PQ2_0 on the same weights | `/home/dank/models/Bonsai2-PTQ1_0/` + `scripts/download-frank-zealot-bonsai2-ptq1.sh` |
|
||||
| 2026-10-17 → 10-18 | Ternary-Bonsai-2-**27B** PQ2_0 7,206,168,928 B `3907dc16…` + Q8_0 mmproj 629,246,976 B | PrismML fork **10754** (`~/GitPackages/llama-prism/`) | 2 × 200,704 (pool 401,408), q8_0 K+V = 13,328 MiB, settled 22,664 / 1,461; 76.6 t/s decode, 109.4 aggregate over two streams, 691.7 t/s prefill at 166,027; MTP impossible — no head in the file | the 9B bills half the KV per token (8 full layers, not 16), which funded three slots at the native window and beat it on every rate | `/home/dank/models/Bonsai2-PQ2_0/` + the 10754 fork tree, `scripts/download-frank-zealot-bonsai2.sh`, revert the swap commit |
|
||||
@@ -136,6 +152,10 @@ re-run the same arms with the box quiet before a figure is carried anywhere else
|
||||
`tests/frank_zealot_unit.bats`'s fit gate can see a boot that would die at `cudaMalloc`, so every retune keeps it
|
||||
green — and the first verbosity-4 ledger plus settled `nvidia-smi` of a new dtype belongs on this page, because
|
||||
projections drift.
|
||||
The same trap one knob down: the ubatch 2048 → 1024 step handed back **~1,306 MiB** at the 786,432 / q8_0-pair
|
||||
arm and **406 MiB** at the 602,112 / f16-K + q8_0-V boot (target compute 2,496.13 → 2,089.88, with CUDA0 going
|
||||
UP 257 while CUDA_Host went down 663). Changing the dtype or the pool moves the compute buffers too, so a
|
||||
credit measured at one shape is not a budget line at the next.
|
||||
The two boots that priced non-KV: 2 × 262,144 **without** the projector — 20,190 used, KV 12,800 ⇒ non-KV
|
||||
**7,390 MiB**; 3 × 196,608 **with** it — 22,972 used, KV 14,113 ⇒ non-KV **8,859 MiB**. The difference prices the
|
||||
vision tower at **~1.4 GiB for a 918 MB file**, because the encoder's activation and compute buffers ride with it.
|
||||
@@ -160,7 +180,7 @@ re-run the same arms with the box quiet before a figure is carried anywhere else
|
||||
|
||||
## Contract
|
||||
|
||||
- `/health` → `{"status":"ok"}`; `/props` reports `n_ctx` and `presence_penalty`; `/slots` shows occupancy; a third
|
||||
- `/health` → `{"status":"ok"}`; `/props` reports `n_ctx` and `presence_penalty`; `/slots` shows occupancy; a fourth
|
||||
concurrent conversation **waits** for a slot — the slot count is the concurrency contract.
|
||||
- `/metrics` answers 200 with 15 `llamacpp:*` families **only with `--metrics`**; without it llama.cpp answers **501**, which Prometheus reads as DOWN while `/health` stays 200. The scrape target is `host.containers.internal:7771/metrics` and the oldest retained sample of `up{job="frank-zealot"}` (2026-10-11) is already 0 — this target was never successfully scraped, across every payload era. `METRICS=0` is the documented opt-out and re-breaks it; the counters cost nothing on the request path.
|
||||
- Reasoning arrives in **`reasoning_content`** (`--reasoning-format deepseek`) and **counts against `max_tokens`**: a short budget yields empty `content` with `finish_reason: "length"`, which reads as a broken model, not a mid-thought one. The slot prefix cache is live (`cached_tokens` in `usage`).
|
||||
@@ -168,9 +188,9 @@ re-run the same arms with the box quiet before a figure is carried anywhere else
|
||||
(1.0 / 0.95 / 20). `presence_penalty` is the one recipe value with no GGUF metadata key, so it must reach argv;
|
||||
the serve script uses `${PRESENCE_PENALTY=1.0}`, deliberately **not** `:=`, because `""` is the documented opt-out.
|
||||
- Vision: only the OpenAI **`image_url`** content part reaches the model — the legacy top-level `images` field bumps `usage.prompt_tokens` while the model invents an answer. Probe with a **≥ 256 px** solid color (a postage stamp answers "video") and budget **≥ 512** tokens; a 512 px image bills ~280 prompt tokens.
|
||||
- **Q8_0 mmproj, not BF16**: the publisher warns the BF16 tower "can crash llama-server when several image requests run at once", which is exactly the `--parallel 2` shape. `MMPROJ_FILE=` (explicitly empty) is the text-only opt-out and returns ~1.4 GiB; a path at a missing file is a boot **refusal**, never silent blindness.
|
||||
- **Q8_0 mmproj, not BF16**: the publisher warns the BF16 tower "can crash llama-server when several image requests run at once", which is exactly the `--parallel 3` shape. `MMPROJ_FILE=` (explicitly empty) is the text-only opt-out and returns ~1.4 GiB; a path at a missing file is a boot **refusal**, never silent blindness.
|
||||
- Tools: native via `--jinja`; `finish_reason: tool_calls` with valid JSON arguments.
|
||||
- Two non-faults: a `400` naming 262,144 is one conversation outgrowing its **slot**, not pool exhaustion; and
|
||||
- Two non-faults: a `400` naming 200,704 is one conversation outgrowing its **slot**, not pool exhaustion; and
|
||||
the raw `/completion` path is a **dead instrument on this fork build** — a greedy `ignore_eos` run answers
|
||||
**500 "The model produced output that does not match the expected Content-only format"**, because build 10777
|
||||
guards the raw path the moment reasoning markup leaks (older builds only logged `failed to parse reasoning
|
||||
|
||||
@@ -19,8 +19,8 @@ each of those owns exactly that one thing. The incidents behind the laws are in
|
||||
|---|---|
|
||||
| [[frank-medium-batch-ubatch]] | llama-bench sweep of --batch-size against --ubatch-size on frank-medium (2x Radeon AI PRO R9700, layer split) at the… |
|
||||
| [[frank-medium-llamacpp-dflash2]] | The former ACTIVE frank-medium deployment since 2026-09-16 (model swapped 2026-09-19) — mainline llama.cpp on… |
|
||||
| [[frank-medium-model-swap]] | How to swap the checkpoint behind the frank-medium endpoint (Dankserver :7770, vllm-radiance TP=2) — the streamlining… |
|
||||
| [[frank-medium-radiance-mxfp4]] | ACTIVE frank-medium runbook — vllm-radiance serving hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6 (ParoQuant MXFP6 W4A8,… |
|
||||
| [[frank-medium-model-swap]] | RETIRED-era swap procedure for the frank-medium endpoint (`:7770` is now [[frank-archon]]) — the… |
|
||||
| [[frank-medium-radiance-mxfp4]] | RETIRED frank-medium era runbook — vllm-radiance serving hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6 (ParoQuant MXFP6 W4A8,… |
|
||||
| [[frank-medium-rocmfpx]] | charlie12345/ROCmFPX llama.cpp serving kingjones777/Qwen3.8-27B-ROCmFPX-Q8_0-AGENT-GGUF as frank-medium on Dankserver… |
|
||||
| [[frank-npu]] | The retired FastFlowLM endpoint on Dankclaw's XDNA2 NPU — what it served, why it died on 2026-09-11 with the… |
|
||||
| [[frank-redo-2026-08-17]] | This page records the full redo of the inference fleet on 2026-08-17. |
|
||||
|
||||
@@ -15,6 +15,8 @@ This page was the plan for a vLLM-ROCm deployment of
|
||||
Dankclaw. The deployment never shipped. The actual Ling work used the
|
||||
llama.cpp Q6_K fork trial instead: [[ling-3.0-flash-trial]].
|
||||
|
||||
The trial service (`frank-dragoon-ling`) is retired and disabled under the
|
||||
boot policy (2026-08-17). `frank-dragoon` is the Qwen3.8-27B PP
|
||||
head, not the Ling path. See git history for the full decision table.
|
||||
The trial service (`frank-dragoon-ling`) was retired and disabled under the boot
|
||||
policy on 2026-08-17, in the same redo that renamed `frank-dragoon` to the
|
||||
Qwen3.8-27B PP head ([[frank-redo-2026-08-17]]). Two fleet generations later the
|
||||
name belongs to the halogen flash-server endpoint ([[frank-dragoon]]). See git
|
||||
history for the full decision table.
|
||||
@@ -26,8 +26,8 @@ that endpoint's page, not a page.
|
||||
|---|---|
|
||||
| [[frank-medium-batch-ubatch]] | llama-bench sweep of --batch-size against --ubatch-size on frank-medium (2x Radeon AI PRO R9700, layer split) at the… |
|
||||
| [[frank-medium-llamacpp-dflash2]] | The former ACTIVE frank-medium deployment since 2026-09-16 (model swapped 2026-09-19) — mainline llama.cpp on… |
|
||||
| [[frank-medium-model-swap]] | How to swap the checkpoint behind the frank-medium endpoint (Dankserver :7770, vllm-radiance TP=2) — the streamlining… |
|
||||
| [[frank-medium-radiance-mxfp4]] | ACTIVE frank-medium runbook — vllm-radiance serving hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6 (ParoQuant MXFP6 W4A8,… |
|
||||
| [[frank-medium-model-swap]] | RETIRED-era swap procedure for the frank-medium endpoint (`:7770` is now [[frank-archon]]) — the… |
|
||||
| [[frank-medium-radiance-mxfp4]] | RETIRED frank-medium era runbook — vllm-radiance serving hugypufy/Swift-Qwen3.8-27B-PARO-MXFP6 (ParoQuant MXFP6 W4A8,… |
|
||||
| [[frank-medium-rocmfpx]] | charlie12345/ROCmFPX llama.cpp serving kingjones777/Qwen3.8-27B-ROCmFPX-Q8_0-AGENT-GGUF as frank-medium on Dankserver… |
|
||||
| [[frank-npu]] | The retired FastFlowLM endpoint on Dankclaw's XDNA2 NPU — what it served, why it died on 2026-09-11 with the… |
|
||||
| [[frank-redo-2026-08-17]] | This page records the full redo of the inference fleet on 2026-08-17. |
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
# rollback). It replaces the FreeToken seam of
|
||||
# 2026-10-05 (git tag frank-archon-freetoken-pr587-20261005) and pins what
|
||||
# flags alone do not say: the served window clears the harness worst case,
|
||||
# the KV pool backs seqs x max-model-len at the declared cache dtype (an
|
||||
# engine HARD limit, not a ratio), the per-card VRAM pair stays inside the
|
||||
# the pinned-host ledger has a ttm pages cap floor, the serve script ENFORCES
|
||||
# the retired per-card budget knobs are ABSENT (the pinned engine refuses
|
||||
# them at ExecStart), the pinned-host ledger has a ttm pages cap floor, the
|
||||
# serve script ENFORCES
|
||||
# it and RAISES the knob itself when a reboot reset it (the cap's default is
|
||||
# half of RAM), the served window is what models.yml must
|
||||
# quote, the host-networking contract (no --publish), the image gate on a
|
||||
@@ -135,31 +135,37 @@ nosudo_bin() {
|
||||
grep -q '^FRANK_ARCHON_MAX_MODEL_LEN=200000$' "$ENV_FILE"
|
||||
ml=$(awk -F= '/^FRANK_ARCHON_MAX_MODEL_LEN=/{print $2}' "$ENV_FILE")
|
||||
[ "$ml" -ge 177356 ]
|
||||
# Concurrency: 3 slots x the served window is the capacity contract, and
|
||||
# the fused TP all-reduce is declared over seqs x (1 + spec) = 9 rows,
|
||||
# Concurrency: 2 slots x the served window is the capacity contract at
|
||||
# 1.8.1. The budget resolver caps the cache at 556,348 fp8 tokens — 2.8
|
||||
# sessions at 200,000 — so three concurrent full-height chats answer 400,
|
||||
# and the shipped CLI has no KV-size flag to raise it (removed at 1.3.0).
|
||||
# The fused TP all-reduce is declared over seqs x (1 + spec) = 6 rows,
|
||||
# under the 25-row bound at which the wht6 wire stays bit-exact. The
|
||||
# "seqs 2 cannot boot" law belongs to the retired tcclaviger vLLM fork
|
||||
# (torch.compile query_start_loc constraint, 2026-10-02); radiance booted
|
||||
# seqs 2 in the fp8-native era and the 2026-10-06 sweep had seqs 2 and 3
|
||||
# tied inside the +-3% noise floor — slots buy concurrency, not speed.
|
||||
grep -q '^FRANK_ARCHON_MAX_SEQS=3$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_MAX_SEQS=2$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_TP=2$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_TP_WIRE=wht6$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_SPEC_TOKENS=2$' "$ENV_FILE"
|
||||
# expert_tiered placement: vram_weights + the pinned host pool serve the
|
||||
# per-rank resident set TOGETHER, so cutting the VRAM slab to pay for the
|
||||
# KV pool moves the pool with it. KV is QUANTIZED fp8 (the engine takes
|
||||
# only bf16|fp8 — no per-tensor K/V split), the PLE n-gram table is pinned
|
||||
# in host RAM per Frank's law.
|
||||
# expert_tiered placement: the pinned host pool plus whatever expert VRAM the
|
||||
# budget resolver leaves serve the per-rank resident set TOGETHER, so the pool
|
||||
# size is also the lever on which experts stay on the card. KV is QUANTIZED
|
||||
# fp8 (the engine takes only bf16|fp8 — no per-tensor K/V split), the PLE
|
||||
# n-gram table is pinned in host RAM per Frank's law. The per-card VRAM pair
|
||||
# is NOT asserted here: 1.3.0 removed it and the resolver derives the split,
|
||||
# so the pool's token count is a boot-ledger line — verified live by the
|
||||
# capacity leg of scripts/probe-frank-archon-kv.py at every cutover.
|
||||
grep -q '^FRANK_ARCHON_PLACEMENT=expert_tiered$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_HOST_POOL_MIB=44928$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_VRAM_WEIGHTS_MIB=[1-9]' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_VRAM_KV_MIB=[1-9]' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_KV_DTYPE=fp8$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_NGRAM_PLACEMENT=ram$' "$ENV_FILE"
|
||||
# Prefix cache tiers (publisher pattern; disk tier sized for 2 slots).
|
||||
grep -q '^FRANK_ARCHON_PREFIX_HOST_MIB=8192$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_PREFIX_DISK_MIB=65536$' "$ENV_FILE"
|
||||
# Prefix cache: pinned host RAM only (the disk tier was removed), and the
|
||||
# step-sharing cap is stated explicitly — auto vs off is the difference
|
||||
# between decode waiting one chunk and waiting a whole step.
|
||||
grep -q '^FRANK_ARCHON_PREFIX_CACHE_MIB=8192$' "$ENV_FILE"
|
||||
grep -q '^FRANK_ARCHON_BUSY_PREFILL_CHUNK=auto$' "$ENV_FILE"
|
||||
# The pinned ledger (2 x pool + 47.7 GiB PLE + prefix host) must be
|
||||
# under the ttm pages cap, and the env states the floor the serve
|
||||
# script enforces (>= 140,000 MiB — the kernel's own default cap, half of
|
||||
@@ -170,7 +176,7 @@ nosudo_bin() {
|
||||
ttmt=$(awk -F= '/^FRANK_ARCHON_TTM_TARGET_MIB=/{print $2}' "$ENV_FILE")
|
||||
[ "$ttmt" -gt "$ttmm" ]
|
||||
pool=$(awk -F= '/^FRANK_ARCHON_HOST_POOL_MIB=/{print $2}' "$ENV_FILE")
|
||||
ph=$(awk -F= '/^FRANK_ARCHON_PREFIX_HOST_MIB=/{print $2}' "$ENV_FILE")
|
||||
ph=$(awk -F= '/^FRANK_ARCHON_PREFIX_CACHE_MIB=/{print $2}' "$ENV_FILE")
|
||||
# 2 pools + PLE 47.7 GiB (48,824 MiB) + prefix host, rounded up.
|
||||
ledger=$(( 2 * pool + 48824 + ph + 2048 ))
|
||||
[ "$ttmm" -ge "$ledger" ]
|
||||
@@ -179,47 +185,21 @@ nosudo_bin() {
|
||||
[ "$(awk -F= '/^FRANK_ARCHON_RAD_MIN_MIB=/{print $2}' "$ENV_FILE")" -gt 0 ]
|
||||
}
|
||||
|
||||
@test "the KV pool backs seqs x max-model-len at the declared cache dtype" {
|
||||
# The engine's own hard limit: a request longer than the pool's token
|
||||
# count is "accepted by the API and then refused as too large to ever fit
|
||||
# -- this is a hard limit, not a backing ratio". Rates are measured, not
|
||||
# derived: per rank, kv_attn (13 layers) pages 26.00 KiB per 4 tokens at
|
||||
# fp8 and 52.00 KiB at bf16, while kv_qsa_bkey (13 layers) pages 3.25 KiB
|
||||
# at BOTH — the QSA block key is an index, not attention state, and does
|
||||
# not follow --kv-cache-dtype — so the rate is 7,488 B/token at fp8 and
|
||||
# 14,144 at bf16 (1.89x, not 2x). The dtype-independent terms also live in
|
||||
# the pool: ~110 MiB a sequence of recurrent state (kv_gdn_state 54 MiB x 2
|
||||
# copies + conv/tail/ple) and 448 MiB of prefix checkpoints (8 x 55.99).
|
||||
# Verified against BOTH live boots: fp8 3543-330-448 = 2765 MiB -> 387,240
|
||||
# tokens (ledger 386,904); bf16 6912-220-448 = 6244 MiB -> 462,946
|
||||
# (ledger 462,752). Both within 0.1%.
|
||||
kv=$(awk -F= '/^FRANK_ARCHON_VRAM_KV_MIB=/{print $2}' "$ENV_FILE")
|
||||
seqs=$(awk -F= '/^FRANK_ARCHON_MAX_SEQS=/{print $2}' "$ENV_FILE")
|
||||
maxlen=$(awk -F= '/^FRANK_ARCHON_MAX_MODEL_LEN=/{print $2}' "$ENV_FILE")
|
||||
case "$(awk -F= '/^FRANK_ARCHON_KV_DTYPE=/{print $2}' "$ENV_FILE")" in
|
||||
fp8) bpt=7488 ;;
|
||||
bf16) bpt=14144 ;;
|
||||
*) fail "unknown FRANK_ARCHON_KV_DTYPE" ;;
|
||||
esac
|
||||
tokens=$(( (kv - 110 * seqs - 448) * 1048576 / bpt ))
|
||||
[ "$tokens" -ge $(( seqs * maxlen )) ]
|
||||
}
|
||||
|
||||
@test "the per-card VRAM pair stays inside the measured card ceiling" {
|
||||
# The engine's OWN budget table, not a hand-picked reserve. Boot of
|
||||
# 2026-10-13 at the pair 24000/5120: card 32,624 MiB (31.86 GiB), budgets
|
||||
# claimed 29,120, `3.42 GiB is neither budgeted nor accounted — the
|
||||
# driver's context, the activation arena and --gpu-headroom-mib come out of
|
||||
# it; anything past those is throughput on the floor`, and `card 0 settled
|
||||
# at 2.01 GiB free against 209.19 MiB (the 96.00 MiB --gpu-headroom-mib and
|
||||
# 113.19 MiB kept for the static region)`. Claimable reserve above the
|
||||
# engine's floor: 2,058 - 209 = 1,849 MiB, so the ceiling is 30,969 MiB.
|
||||
# The 2026-10-11 bound of 29,120 ("keep >= 1 GiB free", card read as 31,856
|
||||
# MiB) was MY reserve, which the engine never asked for; the shipped pair
|
||||
# spends the difference and lands ~458 MiB above the floor.
|
||||
w=$(awk -F= '/^FRANK_ARCHON_VRAM_WEIGHTS_MIB=/{print $2}' "$ENV_FILE")
|
||||
kv=$(awk -F= '/^FRANK_ARCHON_VRAM_KV_MIB=/{print $2}' "$ENV_FILE")
|
||||
[ $(( w + kv )) -le 30969 ]
|
||||
@test "the retired budget knobs are absent from the env and the argv" {
|
||||
# Not unused leftovers: the pinned engine REFUSES to start on any of them
|
||||
# (--vram-weights-mib/--vram-kv-mib went away at 1.3.0, the prefix-cache disk
|
||||
# tier outright), and a unit that refuses at ExecStart sits in
|
||||
# `activating (auto-restart)` while the port still looks alive from whatever
|
||||
# held it last. A stale line here is an outage, so it is asserted away.
|
||||
for key in FRANK_ARCHON_VRAM_WEIGHTS_MIB FRANK_ARCHON_VRAM_KV_MIB \
|
||||
FRANK_ARCHON_PREFIX_HOST_MIB FRANK_ARCHON_PREFIX_DISK_MIB; do
|
||||
! grep -q "^${key}=" "$ENV_FILE"
|
||||
! grep -q "$key" "$SERVE"
|
||||
done
|
||||
for flag in --vram-weights-mib --vram-kv-mib --prefix-cache-host-mib \
|
||||
--prefix-cache-dir --prefix-cache-disk-mib; do
|
||||
! grep -q -- "$flag" "$SERVE"
|
||||
done
|
||||
}
|
||||
|
||||
@test "the serve script wires the engine flags from the env" {
|
||||
@@ -232,11 +212,13 @@ nosudo_bin() {
|
||||
grep -q -- '--max-model-len "$FRANK_ARCHON_MAX_MODEL_LEN"' "$SERVE"
|
||||
grep -q -- '--placement "$FRANK_ARCHON_PLACEMENT"' "$SERVE"
|
||||
grep -q -- '--host-pool-mib "$FRANK_ARCHON_HOST_POOL_MIB"' "$SERVE"
|
||||
grep -q -- '--vram-weights-mib "$FRANK_ARCHON_VRAM_WEIGHTS_MIB"' "$SERVE"
|
||||
grep -q -- '--vram-kv-mib "$FRANK_ARCHON_VRAM_KV_MIB"' "$SERVE"
|
||||
grep -q -- '--kv-cache-dtype "$FRANK_ARCHON_KV_DTYPE"' "$SERVE"
|
||||
grep -q -- '--prefix-cache-host-mib "$FRANK_ARCHON_PREFIX_HOST_MIB"' "$SERVE"
|
||||
grep -q -- '--prefix-cache-disk-mib "$FRANK_ARCHON_PREFIX_DISK_MIB"' "$SERVE"
|
||||
grep -q -- '--prefix-cache-mib "$FRANK_ARCHON_PREFIX_CACHE_MIB"' "$SERVE"
|
||||
grep -q -- '--busy-prefill-chunk "$FRANK_ARCHON_BUSY_PREFILL_CHUNK"' "$SERVE"
|
||||
# Interleave is opt-in: it reaches argv only through the guarded array, so a
|
||||
# 0 in the env leaves the engine's own default in place.
|
||||
grep -q -- 'PREFILL_ARGS=(--prefill-interleave "$FRANK_ARCHON_PREFILL_INTERLEAVE")' "$SERVE"
|
||||
grep -qF -- '"${PREFILL_ARGS[@]}"' "$SERVE"
|
||||
grep -q -- '--ngram-placement "$FRANK_ARCHON_NGRAM_PLACEMENT"' "$SERVE"
|
||||
grep -q -- '--num-speculative-tokens "$FRANK_ARCHON_SPEC_TOKENS"' "$SERVE"
|
||||
# The boot-ladder escape hatch is opt-in: unset renders NO extra argv.
|
||||
@@ -261,7 +243,10 @@ nosudo_bin() {
|
||||
grep -q -- '--device /dev/kfd' "$SERVE"
|
||||
grep -q -- '--device /dev/dri' "$SERVE"
|
||||
grep -q -- '-v "$FRANK_ARCHON_MODEL_DIR":/models:ro' "$SERVE"
|
||||
grep -q -- '-v "$FRANK_ARCHON_STATE_DIR":/data' "$SERVE"
|
||||
grep -q -- '-v "$FRANK_ARCHON_STATE_DIR/tune":/opt/radiance/share/radiance/tune' "$SERVE"
|
||||
# No /data mount: the prefix cache lives in pinned host RAM now, so the only
|
||||
# host state the container writes is the rad-tune cache above.
|
||||
! grep -q -- ':/data ' "$SERVE"
|
||||
grep -q -- '-v "$FRANK_ARCHON_TEMPLATE":/templates/template.jinja:ro' "$SERVE"
|
||||
}
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
# 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 ONLY loader that registers the payload's custom
|
||||
# tensor type 142 and honours its prism.hadamard.* metadata: TWO 262,144-token
|
||||
# conversations (--ctx-size 524288 --parallel 2) over an UNQUANTIZED f16 KV
|
||||
# cache. Exact KV bills 32,768 B/token here, so the pool is what pays for it —
|
||||
# the same dtype at the old 786,432 pool is 24,576 MiB against the 24,123 MiB
|
||||
# the driver leaves addressable, and the slot count is the term that moved. The
|
||||
# publisher's own Q8_0 vision projector rides along (the BF16 one can crash on
|
||||
# concurrent images), and MTP stays OFF: the head is present, but exact KV
|
||||
# leaves no room for a draft context that bills f16 KV over the whole pool. Log
|
||||
# verbosity 4 ships so the VRAM-fit forensics land in the journal.
|
||||
# tensor type 142 and honours its prism.hadamard.* metadata: THREE 200,704-token
|
||||
# conversations (--ctx-size 602112 --parallel 3) over an f16 K + q8_0 V cache —
|
||||
# 25,088 B/token, 14,406.00 MiB at this pool — with the MTP drafter ARMED
|
||||
# (--spec-type draft-mtp). The asymmetry IS the shape: exact V (32,768 B/token)
|
||||
# leaves nothing for a draft context that bills its own f16 KV over the WHOLE
|
||||
# pool (4,096 B/position, 2,352.00 MiB here), and the 786,432 pool this shape
|
||||
# replaced cannot carry a drafter at any dtype inside the 24,123 MiB the driver
|
||||
# leaves addressable. The publisher's own Q8_0 vision projector rides along (the
|
||||
# BF16 one can crash on concurrent images). Log
|
||||
# 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.
|
||||
|
||||
@@ -143,7 +143,7 @@ run_stage() {
|
||||
done
|
||||
}
|
||||
|
||||
@test "two conversations share the pool at a 262144 window each" {
|
||||
@test "three conversations share the pool at a 200704 window each" {
|
||||
serve_argv
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(argv_value --alias)" = "frank-zealot" ]
|
||||
@@ -152,30 +152,36 @@ 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. Exact KV costs the SLOT COUNT, not the window: every
|
||||
# conversation still gets the model's TRAINED 262,144, and the harness
|
||||
# contextWindow must quote that number — the pool and the slot count are
|
||||
# neither, and a longer prompt is a 400, never a truncation.
|
||||
[ "$(argv_value --parallel)" -eq 2 ]
|
||||
[ "$(argv_value --ctx-size)" -eq 524288 ]
|
||||
local pool slots
|
||||
# the per-slot window: harness contextWindow, Prometheus dashboards and every
|
||||
# consumer's compaction trigger read THAT number, and a longer prompt is a
|
||||
# 400, never a truncation. Two invariants the pool/slot split has to keep:
|
||||
# it divides evenly (llama.cpp floors it otherwise) and a slot never exceeds
|
||||
# the model's TRAINED window (there is no YaRN here, so the engine would be
|
||||
# quoting a window it cannot fill).
|
||||
[ "$(argv_value --parallel)" -eq 3 ]
|
||||
[ "$(argv_value --ctx-size)" -eq 602112 ]
|
||||
local pool slots window
|
||||
pool="$(argv_value --ctx-size)"; slots="$(argv_value --parallel)"
|
||||
[ $((pool % slots)) -eq 0 ]
|
||||
[ $((pool / slots)) -eq 262144 ]
|
||||
window=$((pool / slots))
|
||||
[ "$window" -eq 200704 ]
|
||||
[ "$window" -le 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 f16/f16: exact attention state, and the
|
||||
# slot count is what makes it affordable — this payload bills 8,192 elements
|
||||
# per token per cache (8 full-attention layers × 4 KV heads × 256 dim), so
|
||||
# exact KV is 32,768 B/token = 16,384.00 MiB at the 524,288 pool, where the
|
||||
# q8_0 pair this file used to ship billed 17,408 B/token = 13,056.00 MiB at
|
||||
# the old 786,432 pool (ledger-exact both times). The 24 DeltaNet layers
|
||||
# file, not two. The shipped pair is ASYMMETRIC — f16 K (the engine's own
|
||||
# default) plus q8_0 V: this payload bills 8,192 elements per token per cache
|
||||
# (8 full-attention layers × 4 KV heads × 256 dim), so K costs 16,384 B/token
|
||||
# and V 8,704 (q8_0 is 1.0625 B/element; there is no true fp8 KV dtype in
|
||||
# this build) = 25,088 B/token = 14,406.00 MiB at the 602,112 pool. Exact V
|
||||
# would bill 32,768 B/token and the all-q8_0 pair this file used to ship
|
||||
# billed 17,408 — the halfway rung is what pays for the third slot AND the
|
||||
# armed drafter (the fit gate below prices both). The 24 DeltaNet layers
|
||||
# bill a fixed recurrent state per slot (~50.25 MiB a cell), which is why
|
||||
# pool arithmetic alone under-prices the shape. flash-attn is not
|
||||
# decoration: the quantized-V fallback is only legal with it, so it stays
|
||||
# explicit and a dtype retune cannot silently fall back.
|
||||
# decoration: quantized V is only legal with it, so it stays explicit and a
|
||||
# dtype retune cannot silently fall back.
|
||||
local env_k env_v
|
||||
env_k="$(sed -nE 's/^CACHE_TYPE_K=(.+)$/\1/p' "$ENV_FILE")"
|
||||
env_v="$(sed -nE 's/^CACHE_TYPE_V=(.+)$/\1/p' "$ENV_FILE")"
|
||||
@@ -192,30 +198,47 @@ run_stage() {
|
||||
# The fit is arithmetic off the payload geometry plus the non-KV terms
|
||||
# measured off the boot ledger (weights 2,036.71 + compute buffers 1,409.02
|
||||
# + projector 595 + 248.10 + CUDA context ≈ 460 = 4,749 MiB, dtype- and
|
||||
# slot-independent at this ubatch), plus one ~51 MiB recurrent-state cell
|
||||
# per slot. 24,123 MiB is what the driver leaves addressable (nvidia-smi
|
||||
# reports it as used + free; its total column reads 24,576).
|
||||
local env_k env_v pool slots rate_k rate_v kv total
|
||||
# slot-independent at ubatch 2048), plus one ~51 MiB recurrent-state cell per
|
||||
# slot. That constant is deliberately NOT credited with the ~1,306 MiB the
|
||||
# shipped ubatch 1024 hands back: the gate stays conservative.
|
||||
# An ARMED drafter is billed, because it is the term this shape pays
|
||||
# CACHE_TYPE_V=q8_0 to afford: the draft context is built against the
|
||||
# TARGET's whole pool at its own f16 rate (measured 4,096 B/position at
|
||||
# blk.32) plus buffers and its own recurrent cells — the measured spec-ON
|
||||
# against spec-OFF delta at the 786,432 / ubatch-1024 arm was 3,524 MiB
|
||||
# beside 3,072 MiB of draft KV, i.e. ~452 MiB, doubled here for the third
|
||||
# slot's extra cells. 24,123 MiB is what the driver leaves addressable
|
||||
# (nvidia-smi reports it as used + free; its total column reads 24,576).
|
||||
local env_k env_v pool slots rate_k rate_v kv total spec f16 exact_pool exact_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")"
|
||||
pool="$(sed -nE 's/^CTX_SIZE=([0-9]+)$/\1/p' "$ENV_FILE")"
|
||||
slots="$(sed -nE 's/^PARALLEL=([0-9]+)$/\1/p' "$ENV_FILE")"
|
||||
spec="$(sed -nE 's/^SPEC_TYPE=(.+)$/\1/p' "$ENV_FILE")"
|
||||
rate_k="$(kv_bytes_per_token "$env_k")"
|
||||
rate_v="$(kv_bytes_per_token "$env_v")"
|
||||
f16="$(kv_bytes_per_token f16)"
|
||||
kv="$(( pool * (rate_k + rate_v) / 1048576 ))"
|
||||
total="$(( kv + 4749 + 51 * slots ))"
|
||||
if [ "$spec" != "none" ]; then
|
||||
total="$(( total + pool * 4096 / 1048576 + 768 ))"
|
||||
fi
|
||||
# The shipped arm has to leave real room: the projector's worst case is
|
||||
# charged AFTER the engine's own fit check, so a projection that lands on
|
||||
# the ceiling is a crash waiting for the first image request.
|
||||
[ "$total" -le $(( 24123 - 1024 )) ]
|
||||
|
||||
# And the gate must be able to REFUSE: the same exact KV at the pool this
|
||||
# shape left behind is 24,576 MiB of cache alone — a boot that dies before
|
||||
# /health ever answers. Proving the gate bites is what makes passing one
|
||||
# meaningful.
|
||||
local over
|
||||
over="$(( 786432 * (rate_k + rate_v) / 1048576 + 4749 + 51 * 3 ))"
|
||||
[ "$over" -gt 24123 ]
|
||||
# And the gate must be able to REFUSE, or passing above means nothing. Two
|
||||
# boots this shape can walk straight into: the retired dtype pair at the
|
||||
# retired pool (exact f16 at 786,432 = 24,576 MiB of cache alone, which died
|
||||
# at cudaMalloc), and the shipped pool with V moved back to f16 while the
|
||||
# drafter stays armed — 18,816 MiB of cache plus the draft's 2,352. Both die
|
||||
# before /health ever answers, and no health gate can see either.
|
||||
exact_pool="$(( 786432 * (rate_k + f16) / 1048576 + 4749 + 51 * 3 ))"
|
||||
[ "$exact_pool" -gt 24123 ]
|
||||
exact_v="$(( pool * (rate_k + f16) / 1048576 + 4749 + 51 * slots
|
||||
+ pool * 4096 / 1048576 + 768 ))"
|
||||
[ "$exact_v" -gt 24123 ]
|
||||
}
|
||||
|
||||
@test "log verbosity follows the env file" {
|
||||
@@ -277,48 +300,54 @@ run_stage() {
|
||||
argv_value --ubatch-size | grep -qE '^[0-9]+$'
|
||||
}
|
||||
|
||||
@test "speculation is off: no drafter flag reaches the argv" {
|
||||
# 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 × 524,288 = 2,048 MiB plus
|
||||
# buffers — on a trunk that already projects 21,234 MiB with exact KV. The
|
||||
# MTP arms in frank-zealot.env are all q8_0-era measurements; the doors back
|
||||
# to the drafter are one window per conversation, or KV back to q8_0 with
|
||||
# UBATCH_SIZE=1024 (213.5 t/s). The flags move as a set:
|
||||
# SPEC_TYPE=none strips --spec-type, --spec-draft-n-max AND
|
||||
# --spec-draft-model together, so a no-spec boot cannot carry half a spec
|
||||
# pair (the ROCmFPX-era baseline arm segfaulted at load doing that).
|
||||
@test "the armed drafter ships the whole MTP set and no second copy of the trunk" {
|
||||
# Frank, 2026-10-19: the drafter is ON. The head is IN this file
|
||||
# (qwen35.nextn_predict_layers = 1, blk.32.nextn.*) and it accepted well
|
||||
# when it was measured off (0.798/0.617 per position, mean accepted length
|
||||
# 2.41); what it now pays for is CACHE_TYPE_V=q8_0 plus UBATCH_SIZE=1024,
|
||||
# which is why the dtype pair and the batch pair are pinned next door.
|
||||
# The observable contract is the SET: --spec-type plus every --spec-draft-*
|
||||
# the env names, and NO --spec-draft-model — the MTP draft rides the MAIN
|
||||
# model's weights, so a draft file would load the trunk a second time.
|
||||
local n_max
|
||||
n_max="$(sed -nE 's/^SPEC_DRAFT_N_MAX=([0-9]+)$/\1/p' "$ENV_FILE")"
|
||||
serve_argv
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(argv_value --spec-type)" = "draft-mtp" ]
|
||||
[ "$(argv_value --spec-draft-n-max)" = "$n_max" ]
|
||||
[ "$(argv_value --spec-draft-n-min)" = "0" ]
|
||||
[ "$(argv_value --spec-draft-p-min)" = "0" ]
|
||||
! grep -qxF -- '--spec-draft-model' <<<"$output"
|
||||
}
|
||||
|
||||
@test "the rollback knob strips the entire spec set" {
|
||||
# SPEC_TYPE=none is the documented retreat (and the shape this endpoint
|
||||
# shipped before 2026-10-19). The law it exists to obey: the flags move as a
|
||||
# SET — a no-spec boot that keeps --spec-draft-n-max, or a baseline arm that
|
||||
# hands over --model-draft with no --spec-type, has segfaulted at load with
|
||||
# no diagnostic. So the rollback is proven by ABSENCE of all of them.
|
||||
serve_argv
|
||||
sed -i 's/^SPEC_TYPE=.*/SPEC_TYPE=none/' "$STAGE/test.env"
|
||||
run_stage
|
||||
[ "$status" -eq 0 ]
|
||||
! grep -qxF -- '--spec-type' <<<"$output"
|
||||
! grep -qxF -- '--spec-draft-n-max' <<<"$output"
|
||||
! grep -qxF -- '--spec-draft-n-min' <<<"$output"
|
||||
! grep -qxF -- '--spec-draft-p-min' <<<"$output"
|
||||
! grep -qxF -- '--spec-draft-model' <<<"$output"
|
||||
grep -qE '^SPEC_TYPE=none$' "$ENV_FILE"
|
||||
}
|
||||
|
||||
@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.
|
||||
@test "an external drafter still reaches the engine as a complete set" {
|
||||
# The other arm the ROCmFPX-era crash was about: a named draft FILE must
|
||||
# arrive alongside --spec-type and the depth recipe, never alone. This
|
||||
# endpoint does not use it (the head is in the trunk), so the knob ships
|
||||
# empty and only this test sets it.
|
||||
serve_argv
|
||||
{
|
||||
echo 'SPEC_TYPE=draft-mtp'
|
||||
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)" = "2" ]
|
||||
! grep -qxF -- '--spec-draft-model' <<<"$output"
|
||||
|
||||
# And an external drafter still reaches the engine as a complete set — the
|
||||
# pair the ROCmFPX-era crash was about.
|
||||
echo "SPEC_DRAFT_FILE=$MODEL_DIR/$DRAFT_STUB" >> "$STAGE/test.env"
|
||||
run_stage
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(argv_value --spec-draft-model)" = "$MODEL_DIR/$DRAFT_STUB" ]
|
||||
[ "$(argv_value --spec-type)" = "draft-mtp" ]
|
||||
}
|
||||
|
||||
@test "vision ships by default and MMPROJ_FILE empty opts out of the image path" {
|
||||
|
||||
Reference in new issue
Block a user