Files
esh-pfi-infrastructure/stacks/gemma4-charrp/compose.yaml
T
vh 91bda3c480 fv-ml1: complete the cutover — rename, renumber, DNS, and the LiteLLM repoint
The box is physically at Fountain Valley, renamed, renumbered onto 10.251/16,
and serving inference again. This lands the repo half of that.

Host: hostname ana-ml2 -> fv-ml1, pinned to 10.251.50.54 by a dnsmasq
reservation so the address the runbook, DNS and LiteLLM all assume is the
address it actually has. Its headscale node is renamed too.

The sweep ran from scripts/fv-ml1-rename-sweep.sh, whose allowlist is the
reason this diff touches current-state files and not the record. Dated
persistent-memory entries, archival-memory and incident notes still say
ana-ml2 in 31 and 62 places respectively, because that is what the box was
when those things happened. Rewriting them would make the history lie.

LiteLLM was the load-bearing piece and needed more than the api_base sed the
runbook describes. Twenty api_base entries repointed, but a grep-and-verify
pass also caught a LIVE pass_through_endpoints target for the scalar-judge
reward route still on the old address -- an api_base-only substitution would
have left it dead. Four prose references describing current state were
repointed as well; one historical note recording where a hand-test was run
is deliberately left pointing at 10.250.50.54.

Two facts in the server tables were wrong and are corrected here. The site is
Fountain Valley, not Anaheim. And the box has FOUR RTX PRO 6000 Blackwell
Max-Q, not two -- verified by nvidia-smi -L and independently by PCI
enumeration of four GB202GL devices. That is 391 GB of VRAM rather than 196,
which changes what fits on it.

DNS: fv-ml1, fv-ml1-bmc and fv-gw added under the fv site via the piggyback
approach, scriberr re-homed, and the ana-ml2 records removed. Applied to all
three resolvers. The BMC record carries a warning that its 802.1q VLAN tag
must stay disabled -- it shipped tagging VLAN 250 into an untagged port,
which made it invisible to every network-side diagnostic and is the reason
it appeared dead through several cable changes.

Verified end to end: summarizer and sec both answer through the Anaheim
gateway across the mesh to FV seats on different ports.
2026-09-12 22:00:50 -07:00

138 lines
6.2 KiB
YAML

# gemma4-charrp — google/gemma-4-26B-A4B-it NVFP4 char-rp seat on fv-ml1 GPU0.
#
# Straight-across replacement for `meromero-charrp` (operator, 2026-08-24): same
# port, same served-model-names, so every gateway route and consumer is
# unchanged. The outgoing G4-MeroMero-v2-31B-NVFP4A16 stack is retained stopped
# for rollback — see stacks/meromero-charrp/.
#
# WHY THE SWAP: the seat's requirements now include chain-of-thought, which makes
# throughput MORE critical rather than less — the user waits through the whole
# reasoning block before the first visible token. The incumbent was the DENSE
# 31B at ~40.7 tok/s @32K; this is the 26B-A4B MoE at ~114 tok/s @32K
# (brokkr-smithy-dev's measurements). Same Gemma-4 family, so the parser and
# chat-template machinery below carries over unchanged.
#
# ⚠ THIS IS THE QUANT, NOT THE TUNING BASE. The BF16 weights live at
# /tank/aimodels/gemma4-26b-a4b-it-bf16 and are for QLoRA — 48.10 GiB of BF16
# weights CANNOT be served here: GPU0 is shared with vllm-gen, which holds
# ~45.6 GiB of the 94.97 GiB card, leaving ~49 GiB. Weights alone would eat all
# of it and the engine would die at KV-cache allocation. Do not "simplify" this
# to the BF16 path.
#
# ⚠ SCHEME IS W4A4, NOT W4A16. The RedHatAI quant declares 4-bit
# input_activations (compressed-tensors, format nvfp4-pack-quantized). The
# incumbent was NVFP4**A16**. Faster, and NOT a like-for-like quality
# comparison on the activation axis — say so when benching the two.
#
# Tunables in .env.
name: gemma4-charrp
services:
vllm-gemma4-charrp:
image: ${GEMMA4_IMAGE:-vllm/vllm-openai:latest}
container_name: ${GEMMA4_CONTAINER:-vllm-gemma4-charrp}
restart: unless-stopped
ipc: host
ports:
- "${GEMMA4_PORT:-8016}:8000"
volumes:
- /tank/aimodels:/tank/aimodels
environment:
- PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
- VLLM_API_KEY=${API_KEY:-}
command:
- ${GEMMA4_MODEL:-/tank/aimodels/gemma4-26b-a4b-it-nvfp4}
- --quantization
- compressed-tensors
# UNCHANGED FROM THE OUTGOING SEAT, ON PURPOSE. Both names are live
# LiteLLM routes at http://10.251.50.54:8016/v1 — `char-rp` ->
# hosted_vllm/char-rp and `char-rp-reasoning` -> hosted_vllm/char-rp-thinking.
# They are two aliases onto ONE seat, not two seats. Renaming either
# breaks the gateway silently.
- --served-model-name
- char-rp
- char-rp-thinking
# Tool-calling: Gemma-4 emits its OWN native syntax
# (<|tool_call>call:name{...}<tool_call|>), NOT the qwen3_coder XML the
# other seats use. Without these two flags any tools-bearing request 400s
# outright. Architecture-level, so it carries over from MeroMero unchanged.
- --tool-call-parser
- gemma4
- --enable-auto-tool-choice
# The gemma4 REASONING parser absorbs the <|channel>...<channel|> thought
# markers; without it they leak into `content` verbatim on the post-tool
# turn (upstream vllm #45834).
- --reasoning-parser
- gemma4
# MANDATORY COMPANION TO THE REASONING PARSER. The parser reads
# enable_thinking from chat_template_kwargs and DEFAULTS IT TO TRUE. True
# makes is_reasoning_end() return False at a new turn, which pre-initialises
# the engine to REASONING — so ALL plain RP prose lands in
# reasoning_content with a NULL content, breaking every char-rp consumer.
# Do not remove. Thinking is still reachable per-request via
# chat_template_kwargs {"enable_thinking": true}, which is what the
# `char-rp-reasoning` alias exercises.
- --default-chat-template-kwargs
- '{"enable_thinking": false}'
# TEMPLATE PINNED EXPLICITLY, AND IT IS A NO-OP FOR THE DEFAULT MODEL.
# The A4 build ships this exact file — verified byte-identical to upstream
# google/gemma-4-26B-A4B-it once trailing newlines are normalised (390 vs
# 389 lines, same hash) — so pinning it changes nothing about what is
# served and permanently removes a real class of bug.
#
# ⚠ WHY IT IS PINNED AT ALL: the A16 control build at
# /tank/aimodels/gemma4-26b-a4b-it-nvfp4a16 ships a STALE 266-line
# template. Upstream and A4 open the thinking path with
# `{%- set enable_thinking = enable_thinking | default(false) -%}`; the
# A16 one has no such set, and its tokenizer_config response_schema lacks
# the `thinking` property. Serving it with its own template would render a
# DIFFERENT PROMPT, turning a one-axis activation-precision control into a
# two-axis comparison — a result that would look like a finding.
# Safe to force across both builds because the tokenizers are identical:
# vocab 262,144 entries, added_tokens identical.
#
# ⚠ IF GEMMA4_MODEL EVER POINTS AT A DIFFERENT CHECKPOINT, THIS DEFAULT
# MUST MOVE WITH IT. A pinned template is only correct for the checkpoint
# it came from. That is the inverse of the mistake the outgoing MeroMero
# seat warned about, where a hand-patched template was assumed to transfer.
- --chat-template
- ${GEMMA4_CHAT_TEMPLATE:-/tank/aimodels/gemma4-26b-a4b-it-nvfp4/chat_template.jinja}
- --max-model-len
- "${GEMMA4_MAX_MODEL_LEN:-262144}"
- --max-num-seqs
- "${GEMMA4_MAX_NUM_SEQS:-32}"
- --gpu-memory-utilization
- "${GEMMA4_GPU_MEM_UTIL:-0.47}"
- --kv-cache-dtype
- fp8
- --trust-remote-code
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids:
- "${GEMMA4_GPU_ID:-0}"
capabilities:
- gpu
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 600s
networks:
- tnet
labels:
- homepage.group=AI - Inference
- homepage.name=char-rp (Gemma-4 26B-A4B NVFP4, MoE)
- homepage.icon=mdi-drama-masks
- homepage.description=gemma-4-26B-A4B-it NVFP4 MoE prose+CoT seat, 256K (fv-ml1 GPU0)
- homepage.href=http://10.251.50.54:${GEMMA4_PORT:-8016}/docs
networks:
tnet:
name: traefik-net
external: true