The 2026-10-09 wire aliases become the service names everywhere: systemd units, serve scripts, env files and FRANK_* variables, deploy-plan/deploy-remote service keys, container names, Prometheus job names + alert rules, Grafana dashboard queries, bats seams, runbook filenames, homepage/AGENTS lore, and historical prose. Rollback tags renamed in lockstep. Residue in the tree: zero. Retired frank-medium/small/npu names are history and stay. Known casualty: scripts/patches/*.patch context lines were rewritten; those retired-era vLLM-fork patches no longer apply byte-exact.
9.8 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| reference | frank-dragoon on Dankclaw — vLLM TheRock, MiMo-V2.6-Distill-Qwen-9B BF16 + cross-model MTP (RETIRED) | The RETIRED frank-dragoon runbook (2026-10-01, one day) — the digest-pinned kyuz0 TheRock gfx1151 vLLM image serving XiaomiMiMo MiMo-V2.6-Distill-Qwen-9B in full BF16 on the Strix Halo iGPU. Kept for the measured record: the roof math, the cross-model MTP drafter, and the autoresearch finding that spec decode is NET-NEGATIVE on real chat+reasoning traffic here (6.19 t/s with MTP-2 vs 9.01 t/s without). Rolled back to frank-dragoon-gufo-qwen38-27b by Frank the same night. |
|
2026-10-01 |
frank-dragoon — vLLM MiMo-V2.6-Distill-Qwen-9B BF16 (cross-model MTP)
RETIRED 2026-10-01 night, one day active. Frank's call after the autoresearch loop: battery decode 6.19 t/s (MTP-2) / 9.01 t/s (no draft) against gufo's ~15 t/s on the 27B — and every ≥6-bit quant path in vLLM 0.30 is dead on gfx1151 (quark int8 rejected; all FP8 kernels CDNA-only; torchao config unwired). Back to frank-dragoon-gufo-qwen38-27b (Q4_K_XL, 1 x 262144). The measured content below stands as the record.
Identity
- Endpoint:
frank-dragoonat192.168.0.143:7770(Dankclaw). - Runtime: vLLM 0.30.0 in the digest-pinned
docker.io/kyuz0/vllm-therock-gfx1151image (image ID2d24a8c5a3fd, digestsha256:98c1f3ac9833…— ROCm 10.0.0 / torch 2.11.0 / TheRock, Triton Gated DeltaNet kernels). The official vLLM images are CUDA-only; this fork is the fleet's gfx1151 path. Plainpodman rundriven byscripts/serve-frank-dragoon.shunder a root unit (scripts/frank-dragoon.service,User=dank,Restart=always,ExecStop=podman stop -t 120,TimeoutStopSec=180> the stop grace). - Model:
XiaomiMiMo/MiMo-V2.6-Distill-Qwen-9Bin full BF16 (17.53 GiB, 4 shards) — the same checkpoint frank-zealot serves as an INT8 W8A16 AutoRound conversion on the 3090, here unquantized. - Draft:
Qwen/Qwen3.5-9B(18 GiB on disk; only the 15mtp.*tensors are loaded into the draft pass).
Why this shape (the roof math)
Decode speed for a dense model on Strix Halo is bandwidth-bound: BF16 weights x one pass per token against the LPDDR5x aperture. Measured 2026-10-01 on this host: 13.1 t/s is ~94% of the roof — the payload, not a misconfiguration. The same INT8 payload reaches 83.9 t/s on the 3090 because it moves ~12 GiB through ~936 GB/s. The only way past a bandwidth wall is generating more than one token per weight pass: speculative decode is the performance feature of this endpoint, not a garnish.
The memory side is the opposite of a squeeze: KV grows ONLY on the 8 full
attention layers (32 KiB/token bf16: 8 x 2 x 4 heads x 256 dim x 2 B);
the 24 Gated DeltaNet layers hold a FIXED ~25 MiB state per running
sequence. --max-model-len 262144 (the native window) with
--max-num-seqs 4 at --gpu-memory-utilization 0.45 boots clean — the
four-session contract the gufo shape ran, with the pool shared by the
scheduler instead of carved per session.
Spec method: cross-model MTP — measured, not assumed
| arm | prefill | decode | acceptance |
|---|---|---|---|
| BF16 base | 17,876 t/s | 13.1 t/s | — |
| BF16 + DFlash 8 tok | 7,976 t/s | 17.4 t/s | pos0 59% pos1 26% (token rate 14.8%) |
| BF16 + MTP 2 tok | 7,380 t/s | 19.8 t/s | pos0 71% pos1 44% (token rate 57.4%) |
One session, temperature 0, 28,647-token prefill / 512-token decode, same
client for all three arms (2026-10-01 bench window, frank-dragoon stopped per
Benchmarking Policy). MTP wins (+51% vs +33%) and needs only 2 draft
tokens — acceptance past position 1 is 44% and falling; DFlash's own curve
decays 59/26/14/7, so 8 proposals are mostly wasted verification.
num_speculative_tokens beyond 2 is UNMEASURED; do not raise it without a
fresh A/B. Both spec arms pay ~55–59% of prefill (the draft passes share
the compute queues) — this endpoint's thinking-heavy traffic spends its wall
clock in decode, which is the regime where that trade wins.
Why a Qwen head accepts on a MiMo target: MiMo-V2.6 is a distillation of
Qwen3.5-9B that preserves the hidden-state geometry — identical
4096-hidden/248320-vocab/32-layer/interval-4 structure, and the 71%/44%
per-position acceptance is the direct measurement that the distillation
preserved more than geometry. MiMo itself ships ZERO mtp.* tensors
(verified in both the BF16 base and the INT8 conversion) — which is exactly
why frank-zealot's SGLang NEXTN draft is a 0.00-acceptance ghost: SGLang
reads the head from the TARGET checkpoint, vLLM reads the draft from a
separate path. That difference is the whole trick:
--speculative-config '{"method":"mtp","model":"/models/Qwen3.5-9B","num_speculative_tokens":2}'
The alternative drafter (z-lab/Qwen3.5-9B-DFlash, 6 layers, target_layer_ids
[1,5,…,29]) stays fetched on disk; flip FRANK_DRAGOON_SPEC=dflash and repoint
FRANK_DRAGOON_SPEC_DIR to use it. Its card's "vLLM PR #40898 unmerged" claim
is FALSE — vllm/v1/spec_decode/dflash.py ships in 0.30.
The numerics trap (greedy text is NOT preserved by spec decode)
Measured: temperature-0 completions DIFFER across arms (sha 1318aeb6 base,
0208b45d dflash, 20c41c38 mtp). Batched verification changes the bf16
reduction order, argmax near-ties diverge, and the divergence cascades.
vLLM spec decode is distribution-preserving, not bitwise-greedy-preserving.
The gufo-era "greedy == autoregressive text" law does not carry over; do not
chase text drift between spec and no-spec arms as a regression.
Payload
- Target: 4 safetensors shards (17.53 GiB) + config/tokenizer under
/home/dank/models/MiMo-V2.6-Distill-Qwen-9B-BF16/, each shard byte-count + LFS sha256-verified byold/download-frank-dragoon-mimo.sh(--verify-onlyre-checks; no token on the curl command line). - MTP source:
old/download-frank-dragoon-qwen35.sh→/home/dank/models/Qwen3.5-9B/(4 shards behindmodel.safetensors.index.json; the shards carry the odd HF asset namesmodel.safetensors-0000N-of-00004.safetensors— the index resolves them, the engine boots from exactly this tree). - DFlash drafter:
old/download-frank-dragoon-dflash.sh→/home/dank/models/Qwen3.5-9B-DFlash/. - Payload status 2026-10-02: all three trees moved to
/home/dank/.AGENTTRASH/20261002-retired-llm-payloads/(59 GiB staged with the other retired frank payloads) and the fetch trio moved toold/— the endpoint is two runtimes past this one, and re-fetch is cheaper than disk. - The serve script gates the image by REPO DIGEST (rootless podman resolves
tags from the network; the pin exists to boot exactly these bytes
offline), then config + index + exact shard count, then the spec draft
(either weight layout: single
model.safetensorsor a sharded index), then/dev/kfd+ whole/dev/dri(never individual renderD nodes — Dankserver 2026-09-29 lesson).
Client contract
- Alias:
frank-dragoon(theidat/v1/models). - Reasoning:
--reasoning-parser qwen3; vLLM emits the thinking text inreasoning, NOT llama.cpp/gufo'sreasoning_content— the harnessmodels.ymlcompat block for this provider setsreasoningContentField: "reasoning". Miss that and thinking silently vanishes from transcripts. - Tools:
--enable-auto-tool-choice --tool-call-parser qwen3_coder. - Vision: the checkpoint carries the
qwen3_5_visiontower but the serve script passes--language-model-only— the endpoint stays text-only by decision; dropping the flag brings vision back. - Sampling: request-side; the harness sends the Qwen3.5-family recipe
(temp 0.7, top_p 0.8) via
compat.extraBody, andchat_template_kwargs.{enable_thinking,preserve_thinking}— thechat_template_kwargsform is what vLLM reads; a top-levelpreserve_thinkingis ignored there. - Env hygiene: every tunable is
FRANK_DRAGOON_-prefixed because HIP reads bareTEMP/HOST/GPU_MAX_HEAP_SIZEand the serve script exports the env file withset -a.
Ops
- Boot ~200–240 s warm; health 503 while loading (do not kill it).
- The serve script sweeps stale container identities before starting:
frank-dragoon-mimoitself and the bench-window containervllm-mimo. - Container death →
podman waitreturns non-zero from ExecStart →Restart=alwaysself-heals (a clean exit-0 drain also restarts, the frank-archon lesson). - Bench windows:
systemctl stop frank-dragoonis enough (mask fails on the relinked unit; the deploy restores). - First-deploy facts (2026-10-01, deploy #3): healthy after 230 s;
boot log
GPU KV cache size: 819,332 tokens, Maximum concurrency for 262,144 tokens per request: 3.13x— four sessions share the pool (the scheduler, not a carve). Live battery green: aliasfrank-dragoon, reasoning split intoreasoning, tool_calls with correct args, and/metricsacceptance pos0 69% pos1 44% (54/86 draft tokens accepted), matching the bench window.
Rollback
Git tag frank-dragoon-gufo-q4kxl-20261001 — gufo v0.1.1 (built from
source, scripts/build-gufo.sh stays on disk) + the Q4_K_XL Qwen3.8-27B
payload + DFlash2 drafter, all still on the host. Restoring the tag's
unit/env/serve trio and redeploying returns the 27B endpoint.
Citations
- Model: https://huggingface.co/XiaomiMiMo/MiMo-V2.6-Distill-Qwen-9B
- Draft source: https://huggingface.co/Qwen/Qwen3.5-9B
- DFlash drafter: https://huggingface.co/z-lab/Qwen3.5-9B-DFlash
- Engine image: https://hub.docker.com/r/kyuz0/vllm-therock-gfx1151
- Serve script:
scripts/serve-frank-dragoon.sh; tunables:scripts/frank-dragoon.env - Predecessor: frank-dragoon-gufo-qwen38-27b; sibling on the same arch: frank-zealot-sglang-mimo-v26