Adds docs/pfi/llm-seat-catalog.md, the durable hand-curated record of what each
seat IS -- lineage, provenance, model-card facts, quantization, speculative
decoding, licenses, and measured warm tok/s + deep-prefill depth results with
their harness and date. It complements the auto-generated
fv-ml1-gpu-seat-inventory.md (live placement/KV/concurrency): the two split by
volatility, and the catalog defers to the inventory for any live number.
Adds scripts/seat-bench.py so the catalog's speed/depth numbers are reproducible
(warm decode tok/s, n=3, conc=1, median; deep prefill at ~0.97x max-model-len
with an allocator-log OOM scan). Serial by design -- concurrent deep prefills
would confound both OOM and tok/s.
Captures the 2026-09-14 measurements: all six generative seats prefill to ~255K
(coder ~16K) with zero OOM/CUBLAS/illegal-memory; warm decode 62.7-337.3 tok/s;
per-seat VRAM. seat-inventory.py now cross-links the catalog in its footer.
Full seat rebalance across GPU0/GPU1 (flash on GPU2 and empty GPU3 untouched),
operator-directed. Every target seat now serves native 262,144 context with
concurrency in the requested 1.2-2.5x band, verified from live boot logs:
cyberprev (sec) 262144 @ 1.37x depth-probed CLEAN to 259,722 tokens
flash-next (gen) 262144 @ 1.31x (untouched, already in band)
gen-small (NEW) 262144 @ 2.56x MTP k=3 measured 69.6% accept / 3.09 len
char-rp 262144 @ 1.22x (was 1.02x; util 0.40->0.52)
char-rp-fast 262144 @ 2.04x (util cap 0.30->0.24, pinned KV unchanged)
- gen-small: NEW seat, Qwen3.6-35B-A3B (3B active MoE), llmfan46 Heretic
(MPOA) NVFP4 experts-only, already on disk at qwen36-35b-a3b-heretic-nvfp4.
There is no general Qwen3.8 A3B (3.8 MoEs are Flash-Next and the 2.4T), so
this is the 3.6 fallback the operator specified. GPU0, :8026, MTP k=3, coherent
and MTP-verified before wiring. gen-small / gen-small-reasoning gateway aliases.
- coder: 8192 @ 13.75x -> 16384 @ 4.70x (util 0.06->0.055). Context doubled,
waste cut. Not the exact 2-3x target: the 1.5B weight+overhead floor (~4.2 GiB)
sits just under the util knob's resolution, so hitting <=3x reliably needs a
--kv-cache-memory byte pin (compose change) rather than the util fraction.
- cyberprev raised 163840 -> 262144: depth-probed with non-repeating prompts to
259,722 tokens, clean (no OOM, memory flat). Unlike mog-sec (same base arch,
capped at 163840 for depth crashes), this checkpoint holds native depth.
- Gateway (operator calls): summarizer + classifier -> gen-small; new
classifier-large -> gen-large (flash) for the accuracy tier; summarizer-large
stays on flash. All verified end-to-end.
- GPU1 hit its ceiling raising char-rp; resolved by trimming char-rp-fast's
reservation cap (its KV is pinned, so concurrency held at 2.04x) rather than
moving a utility seat -- the shared GPU_ID on reward/embed/rerank made a
single-seat move messier than the in-GPU rebalance.
Seat inventory regenerated from the live containers.
Live-read regeneration after promoting cyberprev into sec/sec-reasoning and
retiring mog-sec. GPU0 now carries cyberprev alone; sec/sec-reasoning aliases
resolve to :8025.
The seat documentation must stay current, and a hand-written document cannot.
The LiteLLM config described char-rp as a 31B model on a host and GPU it had not
been on since 2026-08-24 -- three weeks of silent drift in a file that read as
authoritative, and the reason a seat spent that period serving a model nobody
intended. Anything typed here drifts the same way; anything read off the running
containers cannot.
scripts/seat-inventory.py derives the whole document from the host:
- placement and VRAM from nvidia-smi compute-apps, mapped to containers through
/proc/<pid>/cgroup -- nvidia-smi reports the vLLM engine child while docker
reports the container pid, so matching them directly silently yields nothing
- weights and KV tokens parsed from each engine's own startup log, not derived
arithmetically, with concurrency computed as KV tokens over context
- architecture, layer and expert counts, and the exact quantization group scheme
(W4A4 vs W4A16 distinguished) from each model's config.json
- speculative-decoding method and k from the container argv, which is how the
three incompatible methods on this box became visible
- lineage from the .PROVENANCE.txt SIBLING files -- they sit beside the model
directory, not inside it, which is why an earlier pass wrongly reported two
fully-documented seats as having no provenance
- gateway aliases resolved from the LiteLLM config on ana-docker
--check compares the committed document against the live box and exits non-zero
when they diverge, ignoring only the generation timestamp. Suitable for CI or a
scheduled drift alarm; read-only throughout, safe against production.
Also commits the KV_CACHE_BYTES override added to the MTP campaign runner, which
asserts the flag exists in the derived argv and aborts rather than running a
campaign that silently ignored it.
Read from docker inspect, nvidia-smi and each model's own config.json rather
than from compose files or the gateway config, so the document records what is
deployed rather than what was intended.
Findings worth naming:
- char-rp drift. The LiteLLM config documents char-rp as G4-MeroMero-v2-31B on
ana-ml2 GPU 0; :8016 actually serves gemma4-26b-a4b-it-nvfp4, a 26B 30-layer
128-expert stock Gemma-4, on fv-ml1 GPU 1. Host renaming explains part of it,
but 31B MeroMero-v2 and 26B stock gemma4 are different models and the comment
block carries sampler defaults keyed to the model it names.
- Two gateway aliases are dead: erp-tune-v2 (:8098) and gemma4-26b-a4b-it-base
(:8099). Nothing is listening on either; callers get a connection failure
rather than a clear retirement error.
- Three different speculative-decoding methods coexist -- dflash k=7 with a
separate drafter on mog-sec, qwen3_5_mtp k=3 on gen, mtp k=3 on flash-next.
The method string is architecture-specific; copying a --speculative-config
between seats without changing it will not work.
- Three seats run the floating :latest tag, so what they would pull on recreate
is not what is running and was never recorded.
- Seven aliases resolve to vllm-gen on :8015, which is why GPU 0 shows draw
during ordinary OpenWebUI use -- its auto-titling fires summarizer per turn.
Also commits the flash-next-seat MTP k=3 change made today: compose gains the
speculative-config with its measured rationale, and .env.example documents the
KV-cache reduction MTP requires (14 GiB OOMs, 10 GiB verified in production).