Files
serverAppConfigs/serverAppConfigs-docs/LLM/frank-archon-radiance-qwen38-native-fp8.md
T
Frank e7f0d93c86 docs(vault): fleet-wide fact re-verification pass (67 pages)
The docs-fact census wave re-read the live hosts and re-keyed every page
it could: runtime/payload identities, ports, sizes, ledger figures, unit
state, and the retirement banners that drifted as the fleet moved. The
wave was interrupted mid-run, so this commit carries only what passed the
integrity gates: every touched page parses (front matter or H1+status
convention intact, balanced fences, no truncated tables, trailing
newline), the docs_completeness and link-integrity seams are green, and
bats is 384/384.

Numbers that this session proved on live hosts are keyed to the measured
value, not the page's earlier claim.
2026-10-18 13:15:43 -07:00

309 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# frank-archon — Radiance (StillDeadcode), Qwen3.8-Flash-Next-FP8 NATIVE, 2× R9700 TP=2
**Status: RETIRED 2026-10-05 → 2026-10-08** — replaced by the Swift1.5 w4dn8
conversion ([[frank-archon-radiance-swift15-w4dn8]]), itself retired 2026-10-15;
the payload live today (2026-10-18) is the publisher iq4r container,
[[frank-archon-radiance-qwen38-iq4r]] (`scripts/frank-archon.env:110-111`). ·
**Host:** Dankserver (192.168.0.235) · **Port:** 7770 · **GPU:** 2× Radeon AI PRO R9700 (gfx1201), TP=2
The FreeToken PR #587 endpoint was replaced by the **radiance engine** (C++/HIP,
libr4d kernels for gfx1201, no Python in the serving path) driving the
digest-pinned image `stilldeadcode/radiance@sha256:4c3f484564bb2872784e4…`
(v1.0.7 line, 287 MiB, `libr4d@0.5.0`) as a plain `podman run` through
`scripts/serve-frank-archon.sh` under the **root systemd unit**
(`frank-archon.service`, `User=dank`, `LimitMEMLOCK=infinity`). Same port,
same served alias (`frank-archon`), same container identity
(`frank-archon` = the unit name). Rollback: git tag
`frank-archon-freetoken-pr587-20261005` (FreeToken trio + Dockerfile; the
Dockerfile lives in the tag as `scripts/docker/frank-primary-freetoken.Dockerfile`
— `scripts/docker/` is not in the tree today), image
`localhost/frank-primary-freetoken:pr587-77a7201` (that is the tag on the host —
the 2026-10-10 rename never retagged it), payload
`/home/dank/models/Qwen3.8-Flash-Next-Uncensored-FP8`, which is **no longer on
disk** (`ls /home/dank/models/` on Dankserver 2026-10-18: `StillDeadcode`, `rad`,
`tpl.jinja` only).
## Why radiance
- **TP=2 is real here.** FreeToken's TP=2 dies in the `pynccl.cu` ROCm JIT
(`cuda_runtime.h` unresolvable for gfx1201 — probed 2026-10-06); the
second R9700 idled. Radiance's TP wire is libr4d's own one-shot
all-reduce (no RCCL), legal on this board since the 2026-09-20 swap
(ProArt B850, x8/x8 on CPU root ports, `Routing+`/`ReqEn+` everywhere).
- **Native FP8 end to end** (Frank's call): the official
`Qwen/Qwen3.8-Flash-Next-FP8` checkpoint keeps its modelopt block-FP8
encoding — no requantisation, no calibration run, no 4-bit codebook.
- **Every tier is explicit and measured**, not an estimator: placement
`expert_tiered` with a pinned host pool, `--ngram-placement ram` for the
47.7 GiB PLE table, `--expert-vs-cache-ratio 0.82` (the publisher's
measured 99 %-hit point; 0.87 already collapses to 0 %), fp8 KV.
## The payload: native FP8, reshaped then converted (one recipe rule)
- **Raw checkpoint**: `/home/dank/models/Qwen/Qwen3.8-Flash-Next-FP8`
(ungated repo, 172.8 GB, 131 shards + index + tokenizer + vision
configs). Fetched by `scripts/download-frank-archon.sh` (vendored
`hfget.sh`, curl+jq, no HF token needed) and **verified byte-for-byte**:
all 131 shards sha256-match the Hub LFS oids (2026-10-05, log
`/home/dank/logs/verify-qwen38-official-fp8.log`).
- **The container**: `/home/dank/models/rad/qwen4exp-fp8-native.rad`
(177,109 MiB measured = 173.0 GiB; floor
`FRANK_ARCHON_RAD_MIN_MIB=177000` in the env gates a
truncated convert). Built by `scripts/build-frank-archon-rad.sh`:
1. stage `/home/dank/models/stage-qwen4exp-fp8` — `config.json` with
**`quantization_config` dropped** + relative symlinks into the raw
checkout;
2. `stack-qwen38-official-fp8.py` reshapes the staging dir — two
transforms, both value-preserving (script docstring carries the full
argument): the plugin maps the STACKED expert layout
(`mlp.experts.gate_up_proj` `[512,1280,2560]` + `_scale_inv` at the
same index) while the official repo ships per-expert unfused tensors,
so staging byte-stacks all 49 stacks' (48 trunk + MTP) 512 experts
with NO requantisation; and the PLE table ships as 128 **scaleless**
E4M3 shards + one bf16 scalar (1.9931793212890625e-04) while the
plugin's fp8 branch wants the codes beside a ONE-ELEMENT bf16 scale
plane — so staging decodes to a single bf16 `shard_0` with the
checkpoint's own scale (e4m3 decode and the scale are both exact in
bf16);
3. `rad-convert` with ONE `--quant` rule — pure copy for everything else
(every weight keeps the checkpoint's block-FP8 planes: `X.weight`
E4M3 codes + `X.weight_scale_inv` bf16 `[128×128]` scales) — and the
rtn rule re-encodes the bf16 PLE table at the same fixed scale, an
exact round-trip (nearest-code distance 2⁻³ vs the bf16 rounding
perturbation 2⁻⁹): the container gets the checkpoint's own PLE codes
plus the one-element scale plane (`blk.1.ple_ngram.scale`, view {1});
4. THREE LAWS the live runs proved (each cost a full verify+declare
cycle; all fixed in the two scripts, 2026-10-05/06):
- **Scale-name law**: `scale_name()` appends `_scale_inv` to the
TENSOR NAME verbatim — a stacked tensor named
`…experts.gate_up_proj` (no `.weight` suffix) has its scale plane
at `…experts.gate_up_proj_scale_inv`, NOT `…weight_scale_inv`.
- **File-shadow law**: the reader opens every index-named file in
SORTED order and emplaces EVERY tensor each file physically
holds, first-wins (`checkpoint.cpp add_shard`). The raw
model-000NN shards carry tensors NAMED exactly what the merged
PLE shard is named, so the merged file MUST sort first
(`00-ple.merged.safetensors`) or a raw fp8 shard wins the name
and the plan reads one shard as the codes.
- **Symlink-through law**: staging symlinks the raw index into the
stage; `open(path,"w")` writes THROUGH it and corrupts the raw
checkpoint (raw verify then fails 143/144). The stack script
writes index/config temp+`os.replace` — an atomic swap replaces
the symlink itself; the build script COPIES the index and
symlinks only the shards.
5. `rad-info` summary + size floor.
- **Why the staging dir is mandatory** (the descriptor law, spec §2.4):
the plugin is selected by the exact `(arch_id, quant descriptor)` pair
with no fallback. A raw official checkpoint carries
`quantization_config.quant_method: "fp8"` → descriptor `"fp8"` → **no
shipped plugin claims `(qwen4exp, "fp8")`** — raw serve AND unmodified
convert both refuse. The shipped `qwen4exp_fp8` plugin claims
`(qwen4exp, "")` (recipe-built containers). `rad_convert.cpp` writes
`w.set_quant(meta.quant)` into the header, so the staged config's empty
quant method flows through as the container descriptor while **every
weight stays FP8 data**. Native precision is untouched; only the
selection key differs.
- `--plan-only` first: the convert's declare phase is the engine's own,
so a plan that resolves every op will boot. The convert runs card-less
(declare = CPU-side layout queries); add `--device` only if a plugin
load ever demands it.
## The shape (`scripts/frank-archon.env`, every value commented there)
| Knob | Value | Why |
|---|---|---|
| Image | `stilldeadcode/radiance@sha256:4c3f4845…` | digest-pinned INDEX digest (RepoDigests also carry the per-arch manifest `1c582005…`; the image ID `69d05724…` is the config digest, NOT a manifest digest — pinning it sends `podman run` to the network); entrypoint = engine, tools (`rad-convert`/`rad-info`/`rad-tune`/`rad-kbench`) on PATH |
| Container | `frank-archon` | identity = service name across runtimes; legacy list retires `frank-archon-qwen frank-archon-r9v qwen-fp8 glm-probe` |
| `--tp 2 --tp-wire wht6` | both R9700s | fused WHT-6 lossy wire: +5.6–6.3 % prefill, ~−1.3 % 8-stream decode at TP2; `--tp-wire-min-kb` stays engine default (128 KiB) |
| `--max-num-seqs 2` | first boot | Frank: "parallelism = 2 to start". Raise only after a real-traffic look at slot occupancy + the ledger's KV headroom line. The fused all-reduce row count is `seqs × (1 + spec) = 8` |
| `--max-model-len 200000` | served window | native 262,144; the publisher's production value and what `models.yml` quotes. Longer prompt = refused, never truncated |
| `--placement expert_tiered --host-pool-mib 40960` | pinned expert pool **per rank** | native FP8 experts are 112.8 GiB total (56.4 GiB/rank slice) vs ~29 GiB at the 4-bit publisher shape — the pool is the price of native precision. Resize from the boot ledger's coverage line, never arithmetic |
| `--gpu-headroom-mib 96 --expert-vs-cache-ratio 0.82` | publisher values | 0.82 = measured 99 % expert-cache hit; 0.87 → 0 % (the cliff) |
| `--kv-cache-dtype fp8` | **7.32 KiB/token** | 12 QSA full-attention layers, 2 KV heads × 256 dim, K+V; a 200K-token sequence is 1.43 GiB. KV is sized from the VRAM the ratio leaves |
| `--num-speculative-tokens 3` | MTP head | the checkpoint carries `mtp.*` (3,101 tensors, `mtp_num_hidden_layers: 1`); the plugin's probe returns draft depth 3 — the publisher's operating point |
| `--max-num-batched-tokens 2048` | prefill chunk | A/B-verified optimum (1024/2048/4096 ladder, see "Tuning levers") |
| `--prefix-cache-host-mib 4096 --prefix-cache-dir /data/kvcache/frank-archon --prefix-cache-disk-mib 65536` | 3 tiers | host tier pinned (see the ledger), disk tier in `/home/dank/radiance-state` (mounted rw at `/data`; `/` is ext4, mapped-tier-eligible); a session snapshot moves with its blocks |
| `--ngram-placement ram` | **PLE pinned to host RAM** | Frank's pin law. The PLE n-gram table is 51.2 GB (320,001,536 rows × 160 dims = 29 % of all parameters) served via io_uring reads; `ram` **refuses the boot** if memlock is too small |
| `--override-chat-template /templates/template.jinja` | froggeric v22.5 | vendored `scripts/qwen38-froggeric-v22.5.jinja` (28,234 B, `qwen3.8-froggeric-v22.5`); rad-convert bakes the checkpoint's own template, this overrides it, and the engine derives the reasoning/tool-call reply format from it |
**Pinned-RAM ledger** (root unit + `LimitMEMLOCK=infinity` exist for this):
2 × 40 GiB expert pools + 47.7 GiB PLE + 4 GiB prefix host tier + ~2 GiB
spare ≈ **134 GiB**. `ttm.pages_limit` (this kernel has NO `pages_max` —
the two-knob story was stale) caps GPU-mappable pinned pages (default = half
of RAM): the pre-cutover cap was **123 GiB** — BELOW the ledger — so the
serve script's TTM preflight (`FRANK_ARCHON_TTM_MIN_MIB=140000`) refuses to
start until it is raised. DONE 2026-10-05 pre-cutover: sysfs write of
**52,428,800 pages = 200 GiB** (`/sys/module/ttm/parameters/pages_limit`;
verified read-back; raising at runtime is harmless and allocates nothing).
PERSISTENCE: the sysfs write does not survive reboot, and the kernel-cmdline
`ttm.pages_limit=52428800` was never added — the unit now raises the knob at
every boot instead (`scripts/frank-archon.service:44`,
`ExecStartPre=+…/scripts/ttm-preflight.sh --raise`).
**Per-rank VRAM** at TP2: expert slice 56.4 GiB total across VRAM + host
pool; trunk ~3.75 GiB/rank; token embedding vocab-parallel (~1.18 GiB/rank
— the bf16 table is 2.43 GiB, sharding hands the other half to KV); lm_head
row-sharded; recurrent state 54 MiB/copy/card (2 copies with speculation);
KV from the `0.82`-side remainder of claimed VRAM at 96 MiB headroom.
## The engine contract (all verified in source, not assumed)
- **`/health` == `/ping`**: 200 JSON (`model_id, in_flight, queue_depth`)
while serving, 503 only while stopping — **the listener binds AFTER the
full load**, so a bare `curl -fsS http://127.0.0.1:7770/health` gate is
honest (the deployer loops on it; the FreeToken-era `"status":"ok"` body
grep is gone). Boot budget 1800 s (publisher `start_period` 15 m);
measured 2026-10-06: health 200 ~2-3 min after start (~120 s warm) —
the big terms are the 125.26 GiB page-in (38.1 s at 3.5 GB/s) and the
host-pool pin (PLE 47.68 GiB in 43.9 s).
- **Sampling** (oai.cpp): `temperature, top_p, top_k, min_p, typical_p,
presence_penalty, frequency_penalty, repetition_penalty, repeat_last_n,
chat_template_kwargs, reasoning_effort, enable_thinking,
preserve_thinking` accepted; **unknown params are refused (400)** —
models.yml's extraBody set is fully inside it; nothing unverified rides
the wire. `max_tokens` is clamped, not refused; `seed` absent = pick
one; `response_format` supported; **`logprobs` is refused** (harness
never sends it).
- **Reasoning**: `--reasoning-format` defaults to `auto` → thinking in
`reasoning_content` (models.yml `reasoningContentField` matches). Tool
calls: the qwen3.8 dialect → `message.tool_calls` +
`finish_reason: tool_calls`. No parser flags anywhere — parsers are
auto.
- **Vision**: the official checkpoint carries the 27-layer vision tower;
the engine runs the Qwen-VL processor (patching, pad-token expansion,
3-part rotary are core). `models.yml` keeps `input: [text, image]`;
`--mm-max-patches` available.
- **`/metrics`**: vLLM-compatible names + `radiance:*` families — the
Dankclaw Prometheus job re-scrapes 192.168.0.235:7770 (plain LAN routing
reaches it; the netavark published-port quirk is same-host only).
`/stats`, `/sessions`, `/dashboard` for live internals; the repo ships
`deploy/grafana/radiance.json` if a dashboard is wanted.
- **`n` > 1 requests**: `n` defaults to `max(8, max-num-seqs)`; stop
strings ≤ 64 × 4096 B. `model` field not enforced.
## Deploy + verify
```bash
# one-time (already done 2026-10-05): payload fetch + sha256 verify + convert
ssh dankserver 'bash /home/dank/frank-archon-build/build-frank-archon-rad.sh --plan-only' # declare + plan, no bytes
ssh dankserver 'bash /home/dank/frank-archon-build/build-frank-archon-rad.sh' # full convert + rad-info
./scripts/deploy.sh dankserver --service frank-archon
```
Post-cutover checklist (each is observable, not assumed):
1. Unit: `is-active` active, `is-enabled` enabled, container answers
`/health` with 200 JSON.
2. Boot ledger (engine stdout → `/home/dank/logs/frank_archon.log`):
P2P probe per rank pair, TP=2 shard placement, expert pool coverage
line ("backs N % of the worst case … M full-length sessions"), KV pool
bytes, PLE pinned-RAM line, drafter depth 3, prefix tiers.
3. MTP live: a completion's `timings` object shows draft/accept counts;
`radiance:*spec*` metrics in `/metrics`.
4. Froggeric template: a probe prompt renders the v22.5 shape
(tool-call + thinking turn both exercise it).
5. Vision: a ≥256 px solid-color PNG answers the exact color through an
`image_url` content part.
6. Prometheus target `frank-archon` green (not never-green).
## Cutover measured (2026-10-06, all values observed live)
- Container: 1,857,127,997,440 B = 177,109 MiB = 173.0 GiB. Plan-only
priced 172.76 GiB — the DATA planes; the written container adds a
metadata tail. Floor gates must be set from the real file (the first
deploy refused on 178000 > 177109; floor is 177000).
- Boot: 125.26 GiB read in 38.1 s (3.5 GB/s), PLE pinned
"47.68 GiB in 43.9 s" (the 2 B scale plane with it), each rank's
mover holds 25.46 GiB static+experts (19.15 GiB resident experts,
79 %), 16,820 pinned pool slots, health 200 ~2-3 min after start
(~120 s warm). No 7-minute vLLM-style boot.
- KV ledger: kv_attn 99,956 blocks = 2.48 GiB (399,824 tokens),
kv_qsa_bkey 317.24 MiB, kv_gdn_state 4 slots × 54 MiB, PLE conv
states < 1 MiB; linear checkpoints 56.37 MiB × 8 slots (3 of 8
card-backed at boot, 5 withdrawn under the 208 MiB floor).
- MTP on real traffic (temp 0.7, 220-token ask): draft_n 225 /
draft_n_accepted 119 = 52.9 % acceptance, decode 23.8 t/s. ONE
sample — the shape's real number needs live-traffic measurement.
- Contracts: `reasoning_content` field (matches models.yml), usage
metering separates reasoning_tokens; vision — a 512² solid-red PNG
through `image_url` answers `Red` (279 prompt tokens; NOTE the
32-token budget is eaten by thinking — give vision probes ≥256
max_tokens); native `tool_calls` with finish_reason tool_calls.
- Boot defects fixed the same day: the SSD prefix tier opened only
after the serve script mkdir -p'd the full path (the engine's
create is NOT recursive: "cannot create /data/kvcache/..."); the
converter's scale-name law, the PLE file-shadow and the
symlink-through-raw-index bug are in the payload section above.
- Prometheus: `/metrics` answers 200 with vLLM-compatible families
(verified from Dankclaw); the `frank-archon` job needs a
prometheus-service deploy to go green (config pushed, reload
deferred behind a live parallel writer's dirty tree).
## Tuning levers (in order of likely use)
- **`FRANK_ARCHON_MAX_SEQS`**: raise 2 → 3/4 only when the ledger's KV
headroom line and real slot occupancy agree; each slot buys a
recurrent-state copy (54 MiB/card) and shrinks the per-request KV share.
- **`FRANK_ARCHON_HOST_POOL_MIB`**: the ledger's coverage line is the
oracle — it prints what the pool backs at the worst case and how many
full-length sessions fit. RAM budget: pools + PLE + prefix ≤ ~140 GiB of
the host's 246.
- **`FRANK_ARCHON_EXPERT_VS_CACHE`**: 0.82 is the measured 99 %-hit
point; 0.87 zeroes it. Move only with a measured hit-rate reason.
- **`FRANK_ARCHON_TP_WIRE`**: `wht6` vs `exact` — `exact` trades the
+5.6 % prefill for bit-exact reductions; the fused one-shot forms are
declared over the seqs × (1+spec) row bound.
- **`FRANK_ARCHON_EXTRA_ARGS`**: the boot-ladder escape hatch, expanded
unquoted.
- **`rad-tune -m <rad> --tp 2 --num-speculative-tokens 3`**: benchmarks
the tunable space at this model's shapes on this machine, writes
`$RADIANCE_HOME/tune/<machine>.tune` which the engine's selector reads at
boot. Declare at the shapes you serve (`--tp 2
--num-speculative-tokens 3`), or you measure a program the serve never
runs. `$RADIANCE_HOME` is `/opt/radiance/share/radiance` — in the IMAGE — so a
`--rm` scratch container's cache dies with it: the serve script
bind-mounts `$FRANK_ARCHON_STATE_DIR/tune` (`/home/dank/radiance-state/tune`)
at `/opt/radiance/share/radiance/tune`, so the cache (36 GEMM rows,
`gfx1201-x3-AMD_Radeon_Graphics.tune`, generated 2026-10-06 in a
stopped-service window) survives and is read at every production boot.
The two attention requests have no shape recipe (`attn_paged`,
`attn_paged_gate_quant` — "see tools/opshapes.h") and are not tuned.
`RADIANCE_TUNE` pins a kernel for an A/B — an instrument, not a setting.
- **`FRANK_ARCHON_MAX_BATCHED_TOKENS` (MNBT)**: A/B'd 2026-10-06 in a
stopped-service window (real-text probes: 20,417-token cold prefill,
1,985-token warm prefill, 256-token decode). The prefill ladder is
NON-MONOTONIC with an interior optimum at 2048: 1024 → 2048 → 4096 =
231 → 460 → 448 t/s cold, 221 → 412 → 427 t/s warm. Decode is flat within
noise (24.0 / 25.1 / 24.3 t/s); draft acceptance 60.1 / 67.1 / 63.3 %.
**2048 wins** (peak prefill + peak acceptance) — it stays the production
value. 1024 halves prefill (the batch too small to saturate the card);
4096 is a hair off 2048 (the chunk past the kernel's sweet spot).
- **Diagnostics**: `rad-info` (container: encodings, recipe, profile),
`--debug-graph` (what declared vs what issued), `rad-kbench` (kernel
correctness/speed vs libref), `/stats` + `/sessions` (live), and the
publisher's `scripts/` harnesses (`fnpf`, `fnconc`, `cstep2`, `ident`,
`mctools`) — note `mcserve/fnstop`-family scripts `pkill` the engine
binary by name; on this host the service is a systemd unit, so use
`sudo systemctl stop frank-archon` instead.
## Rollback
| Layer | Path |
|---|---|
| FreeToken (one card, native FP8 safetensors) | `git checkout frank-archon-freetoken-pr587-20261005 -- scripts/frank-archon.env scripts/frank-archon.service scripts/serve-frank-archon.sh scripts/docker/ tests/frank_archon_unit.bats` + redeploy; the image in storage is `localhost/frank-primary-freetoken:pr587-77a7201`, and the payload `/home/dank/models/Qwen3.8-Flash-Next-Uncensored-FP8` must be re-fetched — it is gone from the host |
| tcclaviger vLLM fork (TP=2, shape G) | git tag `frank-archon-vllm-shapeG-20261003` (trio + env lore; image still in dank's rootless storage) |
| 4-bit StillDeadcode publisher shape | `qwen3.8-next-flash-fp8-iq4r-moe.rad` — **this became the live payload on 2026-10-15** ([[frank-archon-radiance-qwen38-iq4r]]), deployed with `--host-pool-mib 44928` (`scripts/frank-archon.env:184`), not the 12,288 MiB first sketched here |
The FreeToken-era orcarouter payload has since been deleted from the host
(`ls /home/dank/models/` on Dankserver, 2026-10-18) — there is nothing left to
trash, and the FreeToken rollback needs a re-fetch.
## Links
- [[LLM/index]] — fleet index
- [[frank-archon-freetoken-pr587]] — the retired FreeToken era
- [[dankserver-qwen38-fp8-expert-offload-tuning]] — the retired tcclaviger vLLM era
- [[Dual-R9700-Cooperation]] — the board swap that made TP=2 legal here
- [[RDNA4 Container Serving]] — rootless-podman GPU laws on these hosts
- `serverAppConfigs-docs/Architecture/Testing Policy.md` — the seam contract