Files
club-3090/models/gemma-4-31b/vllm/compose/dual/qat-awq-int4/base.yml
noonghunna 510a76c885 fix(launch): runtime-agnostic GPU pinning via UUIDs — CDI/NixOS support (#610)
--gpus selection was NVIDIA_VISIBLE_DEVICES-only, which CDI runtimes
(NixOS hardware.nvidia-container-toolkit, nvidia-ctk cdi) IGNORE — mog's
3x3090 rig got all cards exposed and vLLM took the wrong two. Index-based
CUDA_VISIBLE_DEVICES can't fix it either: the classic runtime renumbers
the exposed set in-container.

UUIDs solve both at once:
- launch.sh --gpus N,M resolves indices -> GPU UUIDs (nvidia-smi -i) and
  exports BOTH NVIDIA_VISIBLE_DEVICES and CUDA_VISIBLE_DEVICES as UUIDs.
  Classic runtime: exposes-by-UUID; CUDA mask agrees (renumbering-proof).
  CDI: exposure comes from device_ids (nvidia.com/gpu=all) and the CUDA
  UUID mask is what pins the cards. Index fallback if the query fails.
- 26 composes gain a bare `- CUDA_VISIBLE_DEVICES` passthrough (unset ->
  absent -> zero change for existing users); stale commented hints removed.
- preflight: selector consumers are host-index-based -> new
  _preflight_selector_normalize maps GPU-xxxx tokens back to indices at
  the single choke point (the first live boot caught this: preflight
  rejected the UUID selector).
- new guard test-compose-gpu-mask-passthrough.sh: every compose with the
  NVIDIA env line must pass CUDA_VISIBLE_DEVICES through + launch.sh must
  keep the UUID resolution.
- docs/HARDWARE.md: "Pinning specific GPUs (and CDI / NixOS runtimes)" —
  the runtime table, the CDI deploy-block swap, the in-container
  renumbering gotcha, manual recipes.

LIVE-VERIFIED on this rig (classic runtime, 2x3090):
  launch.sh --variant vllm/minimal --gpus 1
  -> host GPU0 = 1 MiB (empty), GPU1 = 20.7 GB (model), both env vars
  UUID-set in-container. CDI leg validation goes to mog (NixOS reporter).

Follow-up (tracked on #610): estate ESTATE_GPUS UUID-ization for CDI
multi-instance rigs; post-boot visible-GPU-count sanity check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EfF565T9eSLaqGzidyJ1Pm
2026-07-06 22:59:51 +00:00

175 lines
9.7 KiB
YAML
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.

# ===========================================================================
# Profile (at-a-glance):
# Model: Gemma 4 31B — cyankiwi QAT-AWQ INT4 (compressed-tensors, lm_head bf16)
# Topology: Dual 3090 (TP=2, NVLink auto-detected via NVLINK_MODE)
# Drafter: none — MTP DISABLED on v0.24.0 (see the ⏸️ MTP caveat block below)
# KV: bf16 (engine-default; overlay-free — no #40391, no cliff)
# Vision: yes
# Max ctx: 229376 (~224K) — bf16 KV pool ceiling on 2× 3090 @ util 0.95 (est. ~237K tok;
# ship below it for a healthy VRAM margin). Overridable via MAX_MODEL_LEN.
# Genesis: N/A — Genesis is Qwen3-Next-specific
# Status: ⚠️ Production w/ caveats
# Caveats: (1) MTP (speculative decoding) DISABLED on vLLM v0.24.0 — Gemma-4 MTP×tool-calling
# is broken upstream (vLLM #39043; MTP fix #42006 closed-unmerged); plain chat + tools
# (without MTP) work. Re-enable when a stable vLLM ships the fix — see block below.
# (2) Context ceiling ~224K (bf16 KV is ~2×/token vs int8-PTH's 262K) — the overlay-free
# trade for retiring the 31b's vllm-gemma-stable dependence. int8-PTH+#40391 (262K)
# returns FREE when PR #40391 merges upstream.
# Quality: rebench-full 2026-07-02 @0.95/229376: verify-full 9/9 (tools+streaming-tool-calls+
# reasoning clean, MTP-off) · verify-stress all 5 ceiling rungs to 210K (91%, VRAM
# margin 1162MB>1024 ✓) · bench decode ~59 TPS (CV 0.1%, TTFT 69ms) · soak PASS
# (0 err · 0 MiB growth · 0/100 silent · p50 58.7 · 99.6% retention). 8-pack deferred.
# Best for: The Gemma-4-31B dual on v0.24.0 — OVERLAY-FREE (stock vllm-stable), vision + tools. ⭐
# ---------------------------------------------------------------------------
# Gemma-4-31B (cyankiwi QAT-AWQ-INT4) on 2× RTX 3090 TP=2, stock vLLM v0.24.0.
#
# WHY cyankiwi qat-AWQ-int4 (not autoround-int4): on v0.24.0, gemma4.py ties the
# lm_head through its quant method, and AutoRound (which quantizes the tied
# lm_head) raises NotImplementedError at model init. cyankiwi's compressed-tensors
# checkpoint keeps lm_head EXCLUDED from quant (bf16), so the tie dispatches to the
# unquantized path → boots clean. QAT-int4 is also ≥ AutoRound-int4 fidelity (§4a).
#
# WHY bf16 KV (this is the whole point of the v0.24.0 consolidation):
# bf16 is the engine-default KV — it needs NO overlay. Gemma-4's sliding_window=1024
# windows most layers, so bf16 KV is cheap enough to reach ~224250K on 2× 3090.
# int8-PTH would give the full 262K but needs the #40391 page-align overlay, which is
# NOT native in v0.24.0 (it boots but SILENTLY craters recall past ~32K — verified
# 2026-07-01, both cyankiwi + w4a16 checkpoints; PR #40391 is open/conflicting upstream).
# So the overlay-free 31b ships bf16; int8-PTH returns free when #40391 merges.
# (fp8 KV is dead on Gemma-4 Ampere: sm_86 lacks the fp8e4nv kernel; TurboQuant is
# arch-blocked — heterogeneous head dims force TRITON_ATTN, which rejects TQ dtypes.)
#
# ──────────────────────────────────────────────────────────────────────────
# ⏸️ MTP (speculative decoding) DISABLED on v0.24.0 — re-enable when upstream lands.
# Why: Gemma-4 MTP × tool-calling is broken on stock v0.24.0 — with spec-decode ON,
# tool calls emit <pad> / leak raw <|tool_call> tokens instead of parsed calls.
# (Plain chat is fine; tools WITHOUT MTP are fine — A/B-verified on this rig 2026-07-01:
# MTP-off → tools PASS, MTP-on → tools FAIL.) Root cause = upstream vLLM #39043; the
# MTP fix (#42006) is closed-unmerged and NOT in v0.24.0.
# Re-enable trigger: a stable vLLM release carrying the #39043 / #42006 fixes. Check:
# gh api repos/vllm-project/vllm/issues/39043 --jq '.state, .state_reason'
# gh api repos/vllm-project/vllm/pulls/42006 --jq '.state, .merged_at'
# Then add the two --speculative-config lines + re-run a streaming multi-tool test
# (must return all args, no leak) before shipping:
# - --speculative-config
# - '{"model":"/root/.cache/huggingface/gemma-4-31b-it-assistant","num_speculative_tokens":${SPEC_N_MAX:-3}}'
# ──────────────────────────────────────────────────────────────────────────
#
# Companion composes:
# single (TP=1) — Gemma-4-31B is DUAL-ONLY on 24 GB (weights fit a single card but leave
# only a few-K ctx). Needs ≥32 GB for single-card long-context.
#
# Hardware metadata (parsed by scripts/preflight.sh):
# Requires-min-vram-gb: 24
# Engine-profile: vllm-stable
# Requires-min-gpu-count: 2
# Tensor-parallel: 2
services:
vllm-gemma-4-31b-qat-awq-int4:
image: ${VLLM_IMAGE:-vllm/vllm-openai:v0.24.0}
container_name: "${ESTATE_CONTAINER:-vllm-gemma-4-31b-qat-awq-int4}"
restart: ${CLUB3090_RESTART:-unless-stopped}
ports:
- "${ESTATE_PORT:-${PORT:-8032}}:8000"
volumes:
- ${MODEL_DIR:-../../../../../../models-cache}:/root/.cache/huggingface
# torch.compile + Triton kernel caches — first boot warms (~5-7 min);
# subsequent boots reuse cached graphs (~3 min).
- ../../../cache/torch_compile_qatawq:/root/.cache/vllm/torch_compile_cache
- ../../../cache/triton_qatawq:/root/.triton/cache
# NVLink auto-detection — runs inside container at boot.
- ../../../../../../scripts/detect_nvlink.sh:/etc/club3090/detect_nvlink.sh:ro
# NO overlay mounts — overlay-free on stock v0.24.0 (bf16 KV needs no patch).
environment:
- NVIDIA_VISIBLE_DEVICES=${ESTATE_GPUS:-${NVIDIA_VISIBLE_DEVICES:-all}}
# CUDA-level GPU mask — the runtime-agnostic half of GPU selection
# (#610): launch.sh --gpus exports GPU UUIDs here. CDI runtimes (NixOS,
# nvidia-ctk cdi) IGNORE NVIDIA_VISIBLE_DEVICES, so this mask is what
# actually pins cards there; on the classic runtime the UUID form is
# renumbering-proof. Unset → absent (no masking).
- CUDA_VISIBLE_DEVICES
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-}
- HF_HUB_OFFLINE=${HF_HUB_OFFLINE:-${OFFLINE:-0}}
- TRANSFORMERS_OFFLINE=${TRANSFORMERS_OFFLINE:-${OFFLINE:-0}}
- VLLM_WORKER_MULTIPROC_METHOD=spawn
- NVLINK_MODE=${NVLINK_MODE:-auto}
- NCCL_CUMEM_ENABLE=0
- NCCL_P2P_DISABLE=1
- VLLM_NO_USAGE_STATS=1
- OMP_NUM_THREADS=1
- PYTORCH_CUDA_ALLOC_CONF=${PYTORCH_CUDA_ALLOC_CONF:-expandable_segments:True,max_split_size_mb:512}
- VLLM_ALLOW_LONG_MAX_MODEL_LEN=1
- TRITON_CACHE_DIR=/root/.triton/cache
shm_size: "16gb"
ipc: host
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
entrypoint:
- /bin/bash
- -c
- |
# Overlay-free on v0.24.0 — no boot-time patch apply. Just NVLink autodetect + serve.
source /etc/club3090/detect_nvlink.sh
if [ "$${_NVLINK_ENABLED:-0}" = "1" ]; then
exec vllm serve "$$@"
else
exec vllm serve --disable-custom-all-reduce "$$@"
fi
- --
command:
- --override-generation-config
- '{"temperature":${TEMP:-${TEMPERATURE:-1.0}},"top_p":${TOP_P:-0.95},"top_k":${TOP_K:-64},"min_p":${MIN_P:-0.0},"repetition_penalty":${REPEAT_PENALTY:-1.0}}'
- --host
- 0.0.0.0
- --port
- "8000"
- --model
- /root/.cache/huggingface/gemma-4-31b-qat-awq-int4
- --served-model-name
- gemma-4-31b
- gemma-4-31b-qat-awq-int4
- --tensor-parallel-size
- "${TP:-2}"
- --pipeline-parallel-size
- "${PP:-1}"
# ---- KV format: bf16 (engine-default, overlay-free). `auto` resolves to the
# model dtype (bf16). int8-PTH is NOT used here (needs #40391, unmerged →
# silent recall cliff on v0.24.0). Ada/Blackwell users MAY override
# KV_DTYPE=fp8_per_token_head; sm_86 Ampere cannot (no fp8e4nv kernel).
- --kv-cache-dtype
- "${KV_DTYPE:-auto}"
# ---- max-model-len: bf16 KV pool ceiling on 2× 3090 @ util 0.95 is ~237K tok;
# ship at 224K for VRAM-margin headroom (verify-stress flagged a thin margin
# at 91% fill on the tighter 0.97/245K config). Raise MAX_MODEL_LEN toward
# ~237K only if you also lower it below the measured pool at boot.
- --max-model-len
- "${MAX_MODEL_LEN:-${CTX:-229376}}"
- --gpu-memory-utilization
- "${GPU_MEMORY_UTILIZATION:-0.95}"
# bf16 KV is ~2×/token vs int8-PTH → tighter pool. --max-num-seqs is a CAP:
# 2 = one un-preempted long-ctx agent (or 2 short) · 1 = single long request
# ⚠️ vision + near-max ctx: drop max-num-seqs/mem-util (thin headroom near 224K).
- --max-num-seqs
- "${MAX_NUM_SEQS:-2}"
# Vision tower: max_tokens_per_mm_item=2496 must fit in batched tokens.
- --max-num-batched-tokens
- "4096"
- --trust-remote-code
# Tool-call support — native gemma4 ParserEngine (#45588) on v0.24.0. Streaming
# multi-tool validated clean (all args, no <|tool_call> leak) 2026-07-01.
- --enable-auto-tool-choice
- --tool-call-parser
- gemma4
# Route Gemma-4's <|channel>thought…<channel|> reasoning into reasoning_content.
- --reasoning-parser
- gemma4
- --chat-template
- /vllm-workspace/examples/tool_chat_template_gemma4.jinja
# ⏸️ MTP disabled — see the caveat block in the header. Do NOT add --speculative-config
# on v0.24.0 (breaks tool-calling); restore it only when vLLM #39043/#42006 land.