# frank-primary.env — tcclaviger RDNA4 vLLM production config (dankserver :7770). # # 2026-10-01 cutover to the BF16-KV no-spec incumbent (the overnight redo # campaign's verdict, Frank's call): same digest-pinned engine and payload as # the 2026-09-29 cutover, but the KV cache is UNQUANTIZED (auto = bf16) and # speculative decode is GONE. Git tag frank-primary-fp8-mtp-20261001 is the # last pre-cutover commit; the old shape (fp8 KV + MTP4) is in history and # restorable from the tag. # # Every number here was QUALIFIED by boot probe on 2026-10-01 (not inherited): # * MAXLEN 241664 x SEQS 3 (724,992 tokens) at bf16 KV is the DEPLOYED # shape (2026-10-01 night ladder): KV_BYTES 10,184,000,000 PER RANK, # engine measured "KV cache size: 732,359 tokens; Maximum concurrency # for 241,664 tokens per request: 3.03x". 262144 and 245760 both OOMed # at graph capture (76 MiB short / 0 bytes free); 237568 passed at # 718,606 tokens and the +4096 bonus step passed and won. # * KV_BYTES is PER RANK. The old "7.2 GiB ceiling, 9/16 GiB OOM" law was # the MTP4 WORKSPACE: the spec-era pool measured 729,545 tokens because # the drafter reserved ~2.6 GiB/rank beside the pool. No-spec bf16 takes # 11.05 GiB/rank and boots clean — the ceiling was spec's, not VRAM's. # * bf16 KV doubles the attention-KV bytes/token (measured 14,047 vs # 7,926) and costs ~14% decode vs fp8 at 4064 ctx (49.25 -> 36.42 with # the gather win) — traded deliberately for clean cache + 262k windows. # * GATHER 32x64 is the night's biggest lever: R4D_LRU_GATHER_CHUNKS=32 / # R4D_LRU_GATHER_LANES=64 measured +18.7% decode vs default geometry, # and the win grows with depth (39.5@166 -> 54.1@1087 -> 49.3@4064 fp8 # era). Do not drop it. # * MNBT 1024 is Frank's lock: MNBT 512 doubles TTFT at 4064 ctx (2.46 s # vs 1.36 s); 1024-4096 are interchangeable on decode; 12288 costs 2.5% # and arena. 1024 keeps the workspace floor so the arena stays max. # * PREFETCH IS ABSENT BY LAW: expert-cache telemetry measured the # prefetcher at 36% precision (36,720 slabs prefetched, 13,369 served) — # roughly half the gather traffic was waste. Never re-add # --expert-prefetch without a fresh A/B. # * TunableOp stays OFF: CLAV_TUNABLEOP_SWEEP silently skips under expert # offload unless CLAV_TUNABLEOP_WITH_OFFLOAD=1, and forcing it corrupts # numerics. # # HIP reads bare env vars TEMP, HOST, GPU_MAX_HEAP_SIZE — every key here is # prefixed FRANK_PRIMARY_ so none can leak. # # --- runtime ---------------------------------------------------------------- # Digest-pinned: the floating :latest tag moved once already. Local alias # qwen-fp8-runtime:tuned-20260929 (image ID 3322dbbbc96d) points at the same # bytes in rootless storage. FRANK_PRIMARY_IMAGE=docker.io/tcclaviger/vllm@sha256:da4029666668b90a0386c1ed23ea370c79916ad4e1a6ad1dd9db098b9fa069ea # --- payload ---------------------------------------------------------------- FRANK_PRIMARY_MODEL_DIR=/home/dank/models/Qwen3.8-Flash-Next-Uncensored-FP8 FRANK_PRIMARY_MODEL_SHARDS=131 # --- identity --------------------------------------------------------------- # The harness models.yml id stays frank-primary (provider 192.168.0.235:7770). FRANK_PRIMARY_CONTAINER=frank-primary-qwen FRANK_PRIMARY_SERVED_NAME=frank-primary FRANK_PRIMARY_PUBLISH=7770:7770 # Containers removed before every start: the retired R9V identity and the ad # hoc experiment identity (both hold the GPUs / name registrations). FRANK_PRIMARY_LEGACY_CONTAINERS="frank-primary-r9v qwen-fp8" # --- qualified engine shape (2026-10-01, final: 3 windows + max arena) -------- # SEQS 3 x MAXLEN 262144 at fp8 KV (Frank's call, 2026-10-01): three contexts # of the model's FULL native window, with the KV pool pinned to the FLOOR the # contract needs so that everything else goes to the expert ARENA — the # remainder is what the fork's planner gives the decode-critical expert cache. # 3 x 262144 = 786,432 tokens; measured fp8 rate 7,698 B/token (from the # 4-slot boot: 8,311,013,376 B -> 1,079,582 tokens, and the 5-slot boot: # 10,388,766,720 B -> 1,350,182 tokens) => 6.054e9 B is the floor. Pinned at # 6,100,000,000 => ~792k tokens, a ~5.9k-token margin, and ~2.2 GiB returned # to the arena versus the 4-slot shape. # History in this shape: 5 x 262144 booted (KV 1,350,182 / 5.15x) but left 0 # bytes free and a real request (995-token prompt, max_tokens=261149) killed # the worker; 4 x 262144 (KV 1,079,582 / 4.12x) was the runtime-survivable # stopgap. Six full-length contexts never fit (6 x 241664 asked 10.70 GiB # against 9.18 GiB free). # MNBT 2048 (Frank's call): the arena sweep measured it at the same 6,499-slot # ceiling as <=4096, so the bigger batch costs the arena nothing. FRANK_PRIMARY_MAXLEN=262144 FRANK_PRIMARY_SEQS=3 FRANK_PRIMARY_KV_BYTES=6100000000 FRANK_PRIMARY_KV_DTYPE=fp8 FRANK_PRIMARY_MNBT=2048 FRANK_PRIMARY_GATHER_CHUNKS=32 FRANK_PRIMARY_GATHER_LANES=64 FRANK_PRIMARY_UTIL=0.98 # --- client contract: reasoning split, tools, preserve_thinking ---------------- # Without --reasoning-parser the engine leaves the thinking text INLINE in # `content` with a dangling `` and a null `reasoning` field (measured # 2026-10-01: content = 'We need answer...Brief.\n\n\n391') — the # malformed thinking block clients choke on. The qwen3 parser splits it # properly. The GLM rollback (d91379d) had dropped all three of these; the # working values are the ones ecaad36 shipped for this model. FRANK_PRIMARY_REASONING_PARSER=qwen3 # Tools: --enable-auto-tool-choice + the qwen3_xml parser is what makes # tool_calls work here (without it, tool_choice auto returns 400). FRANK_PRIMARY_TOOL_PARSER=qwen3_xml # preserve_thinking keeps prior thinking blocks in the rendered prompt across # turns; enable_thinking makes thinking the server-side default. Verified the # payload's tokenizer_config template consumes both keys. FRANK_PRIMARY_CHAT_TEMPLATE_KWARGS='{"enable_thinking": true, "preserve_thinking": true}'