feat(fv-ml1): rebalance GPUs 0/1 by usage; contexts to published maximums; gen-small default output cap

Layout: GPU 0 = cyberprev (KV 16 -> 15 GiB, 1.29x), erp-seat (moved from
GPU 1, KV 8.49 -> 5.5 GiB, 1.32x), voices (ctx 8,192 -> 40,960, its native
maximum, KV 6 GiB, 1.07x), coder (moved from GPU 1, ctx 16,384 -> 32,768,
the card maximum, 2.35x), parakeet-nemo. GPU 1 = gen-small (moved from
GPU 0, 5.5 GiB, 1.76x), meromero-rp (22 GiB, 1.32x), intern-decision.

Sizing rule: KV >= 1.25x max context rounded up to 0.5 GiB, checked against
7 days of LiteLLM traffic and the engines' own KV-usage logs. Each card keeps
spare memory for its bursty tenant (parakeet's cap on GPU 0, intern-decision's
32k call on GPU 1). YaRN extensions are not enabled: they do not fit, and the
Qwen cards warn static YaRN degrades short prompts.

gen-small gets --override-generation-config max_new_tokens 32768. It applies
only to requests that omit max_tokens (vLLM get_max_tokens) and answers 9
runaway ~260k-token replies in 7 days; the longest normal reply was 6,357.

Applied with playbooks/fv-ml1-rebalance-20261005.yaml (boot-check guard and
health/KV verification per seat); all nine seats healthy, gateway smoke
tests pass.
This commit is contained in:
vh
2026-10-05 22:51:17 -07:00
parent 6851ecbd0b
commit c35f6510bf
15 changed files with 319 additions and 25 deletions
+15 -5
View File
@@ -124,7 +124,7 @@ _As of 2026-10-05 ~2035 PT. The newest subsection is first; older subsections ca
- **Scheduled / to verify:** 10-05 0815 + 0900 infra-hermes jobs (sfsrv-ana `pre-pbs4` snapshot delete after its 04:00 backup; drop `pve/root-pre-pve9` on pfi-pve + esh-pve) — outcome not checked in this session, read `scripts/ops-log tail`; esh-nas-pve `nvme/ROOT@pre-pve9` destroy ~10-10 (ours); 10-11 0540 first weekly sudo-io archiver run (check it), 10-11 0900 infra-hermes deletes the two one-off PBS VM vzdumps. Never `zpool upgrade` any pool yet. - **Scheduled / to verify:** 10-05 0815 + 0900 infra-hermes jobs (sfsrv-ana `pre-pbs4` snapshot delete after its 04:00 backup; drop `pve/root-pre-pve9` on pfi-pve + esh-pve) — outcome not checked in this session, read `scripts/ops-log tail`; esh-nas-pve `nvme/ROOT@pre-pve9` destroy ~10-10 (ours); 10-11 0540 first weekly sudo-io archiver run (check it), 10-11 0900 infra-hermes deletes the two one-off PBS VM vzdumps. Never `zpool upgrade` any pool yet.
- **Worldtree memory gate 20261004T143011Z FAILED** (one run filed the forbidden `u-negative-ephemeral` fact; an esh-pve reboot overlapped run 1). Whether the 2026-10-05 0730 run repeated it was not checked here; it is a regression check with worldtree-dev, not a gate. - **Worldtree memory gate 20261004T143011Z FAILED** (one run filed the forbidden `u-negative-ephemeral` fact; an esh-pve reboot overlapped run 1). Whether the 2026-10-05 0730 run repeated it was not checked here; it is a regression check with worldtree-dev, not a gate.
- **Small clean-ups, ours, untracked:** stale LiteLLM aliases `erp-tune-v2` and `gemma4-26b-a4b-it-base`; ana-docker's Traefik (`readTimeout` 10m) and CrowdSec (`use_wal`, bouncer `updateMaxFailure: 3`) configs live only on the host, not in `stacks/`; TEI weights revision unpinned on esh-ml1 + nh3-ml1; a stray nginx `.bak` on pfi-tacticalrmm. - **Small clean-ups, ours, untracked:** stale LiteLLM aliases `erp-tune-v2` and `gemma4-26b-a4b-it-base`; ana-docker's Traefik (`readTimeout` 10m) and CrowdSec (`use_wal`, bouncer `updateMaxFailure: 3`) configs live only on the host, not in `stacks/`; TEI weights revision unpinned on esh-ml1 + nh3-ml1; a stray nginx `.bak` on pfi-tacticalrmm.
- **fv-ml1 KV pins APPLIED 2026-10-05 2131–2139 (Prime's call; `playbooks/fv-ml1-kv-pin-20261005.yaml`, elway all green).** Diagnosis: on GPU 1, `vllm-meromero-rp` sized KV from util 0.52, and vLLM's profile is corrupted by co-booting neighbours (non-torch 8.11 → 1.74 → −0.56 GiB; KV 20.37 → 26.74 → 29.04 GiB). The 0234 Docker-move restart cost GPU 1 ~2.5 GB (Beszel step), so intern-decision's 32k calls were ~2.4 GB short. GPU 0 has no step: it CREEPS for 1–2 days after a boot and plateaued at ~60 MiB free (Beszel 10-03→10-05). The 10-01 "~1.05 GB free" was a just-after-restart read. Changes: meromero KV pinned **22 GiB** (346,436 tokens, 1.32×), util 0.52 → 0.47 (boot check only); coder pinned **1,104,674,816 B** (77,056 tokens, unchanged); gen-small KV **8 → 5.5 GiB** (461,215 tokens, 1.76×) so parakeet-nemo STAYS on GPU 0. **GPU 3 stays clear for ad-hoc work (Prime).** Result: GPU 1 Free 3,981 → **11,801** MiB (~5.4 GB spare at intern-decision's 32k peak); GPU 0 Free 495 → 5,039 right after boot, projected plateau ~2.6 GB, which covers parakeet's 3,840 MiB cap with ~0.9 GB to spare. ⚠ **Verify the GPU 0 plateau ~10-07/08** from Beszel (`stats.g.0.mu` × 1.024). Downtime: char-rp ~2 min, coder ~1 min, gen-small ~4 min (57 s compile miss). .env backups `*.env.bak-20261005-kvpin`. **Park 75 (TTS stack → fv-ml1) DROPPED** by Prime 2026-10-05 2115 (comment 63). - **fv-ml1 GPU 0/1 REBALANCED 2026-10-05 2226–2249 (Prime: "accept all that" + "context up to published maximums if at all possible"; `playbooks/fv-ml1-rebalance-20261005.yaml`, elway all green, gateway smoke tests pass).** It follows the 2131 KV pins (meromero 22 GiB, coder pinned, gen-small 5.5 GiB). Why: on a shared card util-sized KV is a co-boot lottery, and GPU 0 creeps to ~60 MiB free after a boot. Rule: KV ≥ 1.25× the seat's max context, rounded up to 0.5 GiB. Two exceptions: meromero and gen-small keep Prime's numbers, and voices gets 1.07× (short prose traffic). Usage basis: 7 days of LiteLLM logs (gen-small 22,396 calls; sec 400; char-rp-fast 55; char-rp 14) plus the engines' own KV logs. **Layout:** GPU 0 = cyberprev, erp-seat, voices, coder, parakeet-nemo (4,036 MiB free right after; ~2.3 GB spare at parakeet's cap; cyberprev may still creep). GPU 1 = gen-small, meromero-rp, intern-decision (10,864 free; ~3.3 GB spare at the 32k peak once gen-small settles). Contexts raised: coder 16,384 → **32,768** (card max), voices 8,192 → **40,960** (native max). YaRN extensions were NOT enabled (gen-small/cyberprev to ~1M, voices to 131k). They don't fit, and Qwen's own cards warn static YaRN hurts short prompts. intern-decision stays at 32,768 (card default 8,192; base config 262k; ~0.21 MiB/token of VRAM). **gen-small default output cap 32,768** (`--override-generation-config`; an explicit max_tokens still wins, verified in vLLM `get_max_tokens`). It answers 9 runaway ~260k-token replies in 7 days (albok-nemi ×6, all-agents-local ×3, all from nh3-dev); albok-dev told 2250. ⚠ **Verify both cards' plateaus ~10-07/08** from Beszel (`stats.g.<n>.mu` × 1.024). .env backups `*.env.bak-20261005-rebal`. Earlier the same night: KV pins 2131–2139 (`playbooks/fv-ml1-kv-pin-20261005.yaml`; diagnosis in commit 269c80f). Park 75 dropped by Prime 2115.
- **Open, Prime's calls (unchanged from 10-03):** QNAP LCD visual check (`services/qnap-lcd/`); NH3 Mac mini (`Mac-mini`, 00:c5:85:13:e0:92, 10.100.10.121, asleep since 10-03 ~1407) wake fix on site: `sudo pmset -a sleep 0 disksleep 0 displaysleep 10 womp 1 autorestart 1 powernap 0`, ideally Ethernet; Booth unreachable from 10.100.10.166 (Prime's Wi-Fi MacBook; resume only on his word; lead: SSID client isolation / proxy-ARP); Uptime Kuma has nothing on Miranda's chain (svos :8770, hermes-gateway); mask `openipmi.service` on esh-nas-pve; run `playbooks/pve-nag-patch.yaml` on esh-pve-2 when it is plugged back in. - **Open, Prime's calls (unchanged from 10-03):** QNAP LCD visual check (`services/qnap-lcd/`); NH3 Mac mini (`Mac-mini`, 00:c5:85:13:e0:92, 10.100.10.121, asleep since 10-03 ~1407) wake fix on site: `sudo pmset -a sleep 0 disksleep 0 displaysleep 10 womp 1 autorestart 1 powernap 0`, ideally Ethernet; Booth unreachable from 10.100.10.166 (Prime's Wi-Fi MacBook; resume only on his word; lead: SSID client isolation / proxy-ARP); Uptime Kuma has nothing on Miranda's chain (svos :8770, hermes-gateway); mask `openipmi.service` on esh-nas-pve; run `playbooks/pve-nag-patch.yaml` on esh-pve-2 when it is plugged back in.
### Parakeet speech seat: unified-en under NeMo, LIVE (2026-10-01) ### Parakeet speech seat: unified-en under NeMo, LIVE (2026-10-01)
@@ -152,11 +152,21 @@ _As of 2026-10-05 ~2035 PT. The newest subsection is first; older subsections ca
- **Legacy archive:** DESTROY it whole by **2026-10-30**, or at retirement-done, or on a subject-erasure request, whichever comes first. The runbook is in the detail file. - **Legacy archive:** DESTROY it whole by **2026-10-30**, or at retirement-done, or on a subject-erasure request, whichever comes first. The runbook is in the detail file.
- **TODO:** re-sweep both api logs after real traffic. After the b193 push, remove the retired config keys (list: detail file, "Post-b193 config retirements"). - **TODO:** re-sweep both api logs after real traffic. After the b193 push, remove the retired config keys (list: detail file, "Post-b193 config retirements").
### fv-ml1 GPU layout (as of 2026-10-05) ### fv-ml1 GPU layout (as of 2026-10-05 2249)
- **GPU 0:** cyberprev (KV 16 GiB pinned), gen-small (KV 5.5 GiB pinned since 2026-10-05), voices (KV 1.5 GiB), parakeet-nemo (rest 2,108 MiB, cap 3,840). Plateau target ~2.6 GB free (verify ~10-07/08). | GPU | seat | max ctx | KV | KV tokens | concurrency |
- **GPU 1:** vllm-coder, erp-seat, meromero-rp (all KV byte-pinned since 2026-10-05), plus intern-decision (cap 14.4 GiB, 32k tokens, peak 15,220). Free 11,801 MiB at rest → ~5.4 GB spare at its peak. |---|---|---|---|---|---|
- **GPU 2:** `vllm-flash-next` alone (gen / gen-large and 6 other aliases, util 0.96, ~96.4 GB). Live since 2026-09-14; henge park 49 is stale. | 0 | cyberprev (sec) | 262,144 | 15 GiB | 336,935 | 1.29× |
| 0 | erp-seat (char-rp-fast) | 262,144 | 5.5 GiB | 346,436 | 1.32× |
| 0 | voices | 40,960 | 6 GiB (bf16) | 43,680 | 1.07× |
| 0 | coder (coder-fast) | 32,768 | 1.03 GiB | 77,056 | 2.35× |
| 0 | parakeet-nemo | audio | rest 2,108 MiB, cap 3,840 | — | — |
| 1 | gen-small (+summarizer/classifier) | 262,144 | 5.5 GiB | 461,215 | 1.76× (default output cap 32,768) |
| 1 | meromero-rp (char-rp) | 262,144 | 22 GiB | 346,436 | 1.32× |
| 1 | intern-decision | 32,768/call | cap 14.4 GiB, peak 15,220 | — | — |
| 2 | flash-next (gen, gen-large, +6) | 262,144 | — | 344,155 | 1.31× |
Every vLLM seat on a shared card (GPU 0, GPU 1) has byte-pinned KV (`--kv-cache-memory`), so util only gates the boot check and is set just above each footprint. flash-next is alone on GPU 2 and stays util-sized (0.96).
- **GPU 3:** clear for ad-hoc work (Prime, 2026-10-05); no always-resident tenant goes here. On-demand tenants: Blender, Scriberr (0 idle, ~5.5 GB per job), and MIA auto-rig (one-shot `scripts/mia-run`, peak 3.4 GB, added 2026-10-01 for dread-dev). When a full-size seat claims GPU 3, Scriberr steps aside to **irv-ml1's A6000**, not back to GPU 1. - **GPU 3:** clear for ad-hoc work (Prime, 2026-10-05); no always-resident tenant goes here. On-demand tenants: Blender, Scriberr (0 idle, ~5.5 GB per job), and MIA auto-rig (one-shot `scripts/mia-run`, peak 3.4 GB, added 2026-10-01 for dread-dev). When a full-size seat claims GPU 3, Scriberr steps aside to **irv-ml1's A6000**, not back to GPU 1.
### intern-decision (replaced SemIf on 2026-09-30) ### intern-decision (replaced SemIf on 2026-09-30)
+223
View File
@@ -0,0 +1,223 @@
# Rebalance fv-ml1 GPUs 0 and 1 (2026-10-05, Prime: "accept all that", plus "all models need
# context lengths up to their published maximums if at all possible"). GPU 2 (flash-next) and
# GPU 3 (clear for ad-hoc work) are not touched; meromero-rp and intern-decision are not touched.
#
# seat before after
# gen-small GPU 0, KV 5.5 GiB, ctx 262,144 GPU 1, same KV/ctx, default output cap 32,768
# erp-seat GPU 1, KV 8.49 GiB, util 0.24 GPU 0, KV 5.5 GiB, util 0.25
# coder GPU 1, ctx 16,384, util 0.055 GPU 0, ctx 32,768 (card maximum), util 0.06
# voices GPU 0, ctx 8,192, KV 1.5 GiB GPU 0, ctx 40,960 (config/card native), KV 6 GiB, util 0.16
# cyberprev GPU 0, KV 16 GiB GPU 0, KV 15 GiB
#
# Order keeps every boot check satisfied and never stacks a seat onto a card without room:
# stop erp + coder (frees GPU 1) -> gen-small to GPU 1 -> coder to GPU 0 -> voices in place ->
# erp to GPU 0 -> cyberprev in place. Each seat must come back healthy with the expected KV/ctx
# line before the next is touched; elway halts on the first failure.
# scripts/elway infra-ops@10.251.50.54 --playbook playbooks/fv-ml1-rebalance-20261005.yaml
# Rollback per seat: restore its .env.bak-20261005-rebal (+ compose.yaml from git), then
# `docker compose up -d --no-deps <service>` in the stack dir, mind the order above.
vars:
base: /opt/docker/compose
gens_dir: /opt/docker/compose/gen-small-seat
erp_dir: /opt/docker/compose/erp-seat
vllm_dir: /opt/docker/compose/vllm
voices_dir: /opt/docker/compose/voices-seat
cyber_dir: /opt/docker/compose/cyberprev-seat
check: /tmp/rebal-gpucheck.sh
wait: /tmp/rebal-wait-healthy.sh
steps:
- name: upload helpers
upload:
src: scripts/oneoff/fv-ml1-gpucheck.sh
dest: "{{ check }}"
mode: "0755"
- name: upload wait helper
upload:
src: scripts/oneoff/wait-healthy.sh
dest: "{{ wait }}"
mode: "0755"
- name: back up the five .env files once
sudo: true
shell: for d in {{ gens_dir }} {{ erp_dir }} {{ vllm_dir }} {{ voices_dir }} {{ cyber_dir }}; do cp -p $d/.env $d/.env.bak-20261005-rebal; done
creates: "{{ cyber_dir }}/.env.bak-20261005-rebal"
# ---------- 1. free GPU 1: stop erp + coder (both near idle: 55 and 4 gateway calls/7d) ----------
- name: stop erp-seat (until it is recreated on GPU 0)
shell: docker stop vllm-erp-seat
when: "! docker inspect vllm-erp-seat | grep -q 5905580032"
- name: stop coder (until it is recreated on GPU 0)
shell: docker stop vllm-coder
when: "! docker inspect vllm-coder | grep -q '\"32768\"'"
# ---------- 2. gen-small -> GPU 1, default output cap ----------
- name: gen-small .env GPU 0 -> 1
sudo: true
shell: sed -i 's/^GEN_SMALL_GPU_ID=.*/GEN_SMALL_GPU_ID=1/' {{ gens_dir }}/.env
when: "! sudo -n grep -qx 'GEN_SMALL_GPU_ID=1' {{ gens_dir }}/.env"
- name: gen-small upload compose (adds --override-generation-config)
sudo: true
upload:
src: stacks/gen-small-seat/compose.yaml
dest: "{{ gens_dir }}/compose.yaml"
mode: "0644"
owner: "root:root"
- name: gen-small dry-parse; rendered config carries the cap and GPU 1
sudo: true
shell: cd {{ gens_dir }} && docker compose config -q && docker compose config | grep -q max_new_tokens && docker compose config | grep -A3 device_ids | grep -q '"1"'
changed_when: "false"
- name: gen-small boot check on GPU 1 (util 0.33)
shell: "{{ check }} 1 0.33"
when: "! docker inspect vllm-gen-small | grep -q max_new_tokens"
changed_when: "false"
- name: gen-small recreate on GPU 1
sudo: true
shell: cd {{ gens_dir }} && docker compose up -d --no-deps vllm-gen-small
when: "! docker inspect vllm-gen-small | grep -q max_new_tokens"
- name: gen-small healthy
shell: "{{ wait }} vllm-gen-small 1500"
changed_when: "false"
- name: gen-small KV line and output cap in the log
shell: |
docker logs vllm-gen-small 2>&1 | grep -E 'GPU KV cache size|max_tokens' | tail -3
docker logs vllm-gen-small 2>&1 | grep -q 'reserved 5.5 GiB memory for KV Cache'
changed_when: "false"
# ---------- 3. coder -> GPU 0, context 32,768 ----------
- name: coder .env GPU 0, ctx 32768, util 0.06
sudo: true
shell: sed -i -e 's/^CODER_GPU_ID=.*/CODER_GPU_ID=0/' -e 's/^CODER_MAX_MODEL_LEN=.*/CODER_MAX_MODEL_LEN=32768/' -e 's/^CODER_GPU_MEM_UTIL=.*/CODER_GPU_MEM_UTIL=0.06/' {{ vllm_dir }}/.env
when: "! grep -qx 'CODER_MAX_MODEL_LEN=32768' {{ vllm_dir }}/.env"
- name: coder dry-parse
sudo: true
shell: cd {{ vllm_dir }} && docker compose config -q && docker compose config | grep -q '"32768"'
changed_when: "false"
- name: coder boot check on GPU 0 (util 0.06)
shell: "{{ check }} 0 0.06"
when: "! docker inspect vllm-coder | grep -q '\"32768\"'"
changed_when: "false"
- name: coder recreate on GPU 0
sudo: true
shell: cd {{ vllm_dir }} && docker compose up -d --no-deps vllm-coder
when: "! docker inspect vllm-coder | grep -q '\"32768\"'"
- name: coder healthy
shell: "{{ wait }} vllm-coder 600"
changed_when: "false"
- name: coder KV line at 32,768
shell: |
docker logs vllm-coder 2>&1 | grep -E 'GPU KV cache size|Maximum concurrency' | tail -2
docker logs vllm-coder 2>&1 | grep -q 'GPU KV cache size: 77,056 tokens'
changed_when: "false"
# ---------- 4. voices in place: context 40,960, KV 6 GiB ----------
- name: voices .env ctx 40960, KV 6 GiB, util 0.16
sudo: true
shell: |
f={{ voices_dir }}/.env
sed -i -e 's/^VOICES_KV_CACHE_MEMORY=.*/VOICES_KV_CACHE_MEMORY=6442450944/' -e 's/^VOICES_GPU_MEM_UTIL=.*/VOICES_GPU_MEM_UTIL=0.16/' $f
grep -q '^VOICES_MAX_MODEL_LEN=' $f || sed -i '$a VOICES_MAX_MODEL_LEN=40960' $f
when: "! grep -qx 'VOICES_MAX_MODEL_LEN=40960' {{ voices_dir }}/.env"
- name: voices upload compose (defaults follow the .env)
sudo: true
upload:
src: stacks/voices-seat/compose.yaml
dest: "{{ voices_dir }}/compose.yaml"
mode: "0644"
owner: "infra-ops:infra-ops"
- name: voices dry-parse
sudo: true
shell: cd {{ voices_dir }} && docker compose config -q && docker compose config | grep -q '"40960"' && docker compose config | grep -q 6442450944
changed_when: "false"
- name: voices boot check on GPU 0 (util 0.16, own memory counts)
shell: "{{ check }} 0 0.16 vllm-voices"
when: "! docker inspect vllm-voices | grep -q 6442450944"
changed_when: "false"
- name: voices recreate
sudo: true
shell: cd {{ voices_dir }} && docker compose up -d --no-deps vllm-voices
when: "! docker inspect vllm-voices | grep -q 6442450944"
- name: voices healthy
shell: "{{ wait }} vllm-voices 1200"
changed_when: "false"
- name: voices KV line at 40,960
shell: |
docker logs vllm-voices 2>&1 | grep -E 'GPU KV cache size' | tail -1
docker logs vllm-voices 2>&1 | grep -q 'reserved 6.0 GiB memory for KV Cache'
changed_when: "false"
# ---------- 5. erp-seat -> GPU 0, KV 5.5 GiB ----------
- name: erp .env GPU 0, KV 5.5 GiB, util 0.25
sudo: true
shell: sed -i -e 's/^ERP_GPU_ID=.*/ERP_GPU_ID=0/' -e 's/^ERP_KV_CACHE_MEMORY=.*/ERP_KV_CACHE_MEMORY=5905580032/' -e 's/^ERP_GPU_MEM_UTIL=.*/ERP_GPU_MEM_UTIL=0.25/' {{ erp_dir }}/.env
when: "! grep -qx 'ERP_KV_CACHE_MEMORY=5905580032' {{ erp_dir }}/.env"
- name: erp upload compose (label says GPU0)
sudo: true
upload:
src: stacks/erp-seat/compose.yaml
dest: "{{ erp_dir }}/compose.yaml"
mode: "0644"
owner: "lkraven:lkraven"
- name: erp dry-parse
sudo: true
shell: cd {{ erp_dir }} && docker compose config -q && docker compose config | grep -q 5905580032
changed_when: "false"
- name: erp boot check on GPU 0 (util 0.25)
shell: "{{ check }} 0 0.25"
when: "! docker inspect vllm-erp-seat | grep -q 5905580032"
changed_when: "false"
- name: erp recreate on GPU 0
sudo: true
shell: cd {{ erp_dir }} && docker compose up -d --no-deps vllm-erp-seat
when: "! docker inspect vllm-erp-seat | grep -q 5905580032"
- name: erp healthy
shell: "{{ wait }} vllm-erp-seat 1200"
changed_when: "false"
- name: erp KV line
shell: |
docker logs vllm-erp-seat 2>&1 | grep -E 'GPU KV cache size' | tail -1
docker logs vllm-erp-seat 2>&1 | grep -q 'reserved 5.5 GiB memory for KV Cache'
changed_when: "false"
# ---------- 6. cyberprev in place: KV 16 -> 15 GiB ----------
- name: cyberprev .env KV 15 GiB
sudo: true
shell: sed -i 's/^CYBER_KV_CACHE_MEMORY=.*/CYBER_KV_CACHE_MEMORY=16106127360/' {{ cyber_dir }}/.env
when: "! sudo -n grep -qx 'CYBER_KV_CACHE_MEMORY=16106127360' {{ cyber_dir }}/.env"
- name: cyberprev dry-parse
sudo: true
shell: cd {{ cyber_dir }} && docker compose config -q && docker compose config | grep -q 16106127360
changed_when: "false"
- name: cyberprev boot check on GPU 0 (util 0.40, own memory counts)
shell: "{{ check }} 0 0.40 vllm-cyberprev"
when: "! docker inspect vllm-cyberprev | grep -q 16106127360"
changed_when: "false"
- name: cyberprev recreate
sudo: true
shell: cd {{ cyber_dir }} && docker compose up -d --no-deps vllm-cyberprev
when: "! docker inspect vllm-cyberprev | grep -q 16106127360"
- name: cyberprev healthy
shell: "{{ wait }} vllm-cyberprev 1800"
changed_when: "false"
- name: cyberprev KV line
shell: |
docker logs vllm-cyberprev 2>&1 | grep -E 'GPU KV cache size' | tail -1
docker logs vllm-cyberprev 2>&1 | grep -q 'reserved 15.0 GiB memory for KV Cache'
changed_when: "false"
verify:
- name: per-GPU free memory
shell: nvidia-smi --query-gpu=index,memory.used,memory.free --format=csv,noheader
changed_when: "false"
- name: every fv-ml1 GPU seat healthy
shell: for c in vllm-cyberprev vllm-gen-small vllm-voices parakeet-nemo intern-decision vllm-meromero-rp vllm-erp-seat vllm-coder vllm-flash-next; do docker ps -q --filter name=^$c$ --filter health=healthy | grep -q . || { echo "NOT healthy $c"; exit 1; }; done
changed_when: "false"
- name: .env owners and modes unchanged
sudo: true
shell: |
test "$(stat -c '%U %a' {{ gens_dir }}/.env)" = "root 600" &&
test "$(stat -c '%U %a' {{ cyber_dir }}/.env)" = "root 600" &&
test "$(stat -c '%U %a' {{ erp_dir }}/.env)" = "lkraven 644" &&
test "$(stat -c '%U %a' {{ vllm_dir }}/.env)" = "lkraven 644" &&
test "$(stat -c '%U %a' {{ voices_dir }}/.env)" = "infra-ops 664"
changed_when: "false"
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
# fv-ml1-gpucheck.sh — the vLLM boot-check arithmetic, run before (re)creating a seat.
#
# fv-ml1-gpucheck.sh <gpu-index> <util> [container]
#
# vLLM refuses to start unless the card's free memory >= util x total (97,249 MiB on these
# cards). Passes when nvidia-smi Free on <gpu-index>, plus <container>'s own memory ON THAT
# CARD (it is freed when the container is recreated), clears util x 97,249 + 512 MiB.
# A container that is moving here from another card counts nothing, so leave it off.
# Uploaded and run by playbooks/fv-ml1-rebalance-20261005.yaml.
set -u
gpu=$1 util=$2 own_c=${3:-}
free=$(nvidia-smi -i "$gpu" --query-gpu=memory.free --format=csv,noheader,nounits)
own=0
if [ -n "$own_c" ]; then
uuid=$(nvidia-smi -i "$gpu" --query-gpu=uuid --format=csv,noheader)
apps=$(nvidia-smi --query-compute-apps=pid,gpu_uuid,used_memory --format=csv,noheader,nounits)
for p in $(docker top "$own_c" -eo pid 2>/dev/null | tail -n +2); do
m=$(printf '%s\n' "$apps" | awk -F', ' -v p="$p" -v u="$uuid" '$1==p && $2==u {print $3}')
own=$((own + ${m:-0}))
done
fi
need=$(awk -v u="$util" 'BEGIN{printf "%d", u*97249+512}')
echo "GPU$gpu free=$free own=$own need=$need"
[ $((free + own)) -ge "$need" ]
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# wait-healthy.sh <container> <max-seconds> — poll docker health every 10 s until it reads
# healthy; exit 1 at the deadline, or at once if the container exits or starts restarting.
# "(unhealthy)" alone does not fail it: a seat whose boot outlasts its healthcheck
# start_period reads unhealthy and then recovers. Prints the elapsed time either way.
set -u
c=$1 max=$2 t=0
while [ "$t" -lt "$max" ]; do
st=$(docker ps -a --filter "name=^${c}$" --format '{{.Status}}')
case "$st" in
*"(healthy)"*) echo "$c healthy after ${t}s"; exit 0 ;;
Exited*|Restarting*|Dead*) echo "$c FAILED after ${t}s: $st"; exit 1 ;;
esac
sleep 10; t=$((t + 10))
done
echo "$c NOT healthy after ${max}s: $st"; exit 1
+3 -2
View File
@@ -31,13 +31,14 @@ CYBER_SERVED_NAME_THINK=cyberprev-27b-thinking
# 0.40 of the card, sharing GPU 0 with mog-sec. KV pinned in bytes (8 GiB) so the # 0.40 of the card, sharing GPU 0 with mog-sec. KV pinned in bytes (8 GiB) so the
# figure is reproducible regardless of what else is resident at start time. # figure is reproducible regardless of what else is resident at start time.
CYBER_GPU_MEM_UTIL=0.40 CYBER_GPU_MEM_UTIL=0.40
CYBER_KV_CACHE_MEMORY=8589934592 # 2026-10-05 rebalance: 15 GiB (~337k tokens, 1.29x at 262,144). Live max use seen: 57% of 16 GiB.
CYBER_KV_CACHE_MEMORY=16106127360
# ⚠ 163840, NOT native 262144. Inherited from mog-sec, which crashed five times # ⚠ 163840, NOT native 262144. Inherited from mog-sec, which crashed five times
# learning that the KV pool's capacity and the card's processing depth are different # learning that the KV pool's capacity and the card's processing depth are different
# numbers. Raising this requires a deep-prefill probe with a NON-REPEATING prompt on # numbers. Raising this requires a deep-prefill probe with a NON-REPEATING prompt on
# THIS checkpoint — a repeated prompt hashes to cached blocks and never prefills deep. # THIS checkpoint — a repeated prompt hashes to cached blocks and never prefills deep.
CYBER_MAX_MODEL_LEN=163840 CYBER_MAX_MODEL_LEN=262144
CYBER_MAX_NUM_SEQS=16 CYBER_MAX_NUM_SEQS=16
CYBER_MAX_NUM_BATCHED_TOKENS=4096 CYBER_MAX_NUM_BATCHED_TOKENS=4096
CYBER_KV_CACHE_DTYPE=fp8 CYBER_KV_CACHE_DTYPE=fp8
+4 -3
View File
@@ -4,12 +4,13 @@ ERP_MODEL=/tank/aimodels/G4-MeroMero-26B-A4B-it-uncensored-heretic-NVFP4A16
ERP_SERVED_NAME=G4-MeroMero-26B-A4B-it-uncensored-heretic-NVFP4A16 ERP_SERVED_NAME=G4-MeroMero-26B-A4B-it-uncensored-heretic-NVFP4A16
ERP_CHAT_TEMPLATE=/tank/aimodels/G4-MeroMero-26B-A4B-it-uncensored-heretic-NVFP4A16/chat_template.jinja ERP_CHAT_TEMPLATE=/tank/aimodels/G4-MeroMero-26B-A4B-it-uncensored-heretic-NVFP4A16/chat_template.jinja
ERP_PORT=8021 ERP_PORT=8021
ERP_GPU_ID=1 # 2026-10-05 rebalance: GPU 0, KV pinned 5.5 GiB (~346k tokens, 1.32x); util = boot check only.
ERP_GPU_ID=0
# 0.35 x 97.9 GiB = 34 GiB. GPU1 had ~47 GiB free on 2026-09-08 (scriberr/embed/rerank/coder/reward resident). # 0.35 x 97.9 GiB = 34 GiB. GPU1 had ~47 GiB free on 2026-09-08 (scriberr/embed/rerank/coder/reward resident).
ERP_GPU_MEM_UTIL=0.30 ERP_GPU_MEM_UTIL=0.25
ERP_MAX_MODEL_LEN=262144 ERP_MAX_MODEL_LEN=262144
ERP_MAX_NUM_SEQS=32 ERP_MAX_NUM_SEQS=32
API_KEY= API_KEY=
# 8.49 GiB -> 534,649 KV tokens -> 2.04x a 262,144 context (operator's KV = 2x rule). # 8.49 GiB -> 534,649 KV tokens -> 2.04x a 262,144 context (operator's KV = 2x rule).
ERP_KV_CACHE_MEMORY=9114000000 ERP_KV_CACHE_MEMORY=5905580032
ERP_MOE_BACKEND=auto ERP_MOE_BACKEND=auto
+2 -2
View File
@@ -1,4 +1,4 @@
# erp-seat — ERP-tune seat on ana-ml2 (GPU1, `:8021`) # erp-seat — ERP-tune seat on fv-ml1 (ex ana-ml2; GPU0 since 2026-10-05, `:8021`)
Serves the latest gated ERP LoRA merge as an **NVFP4A16** (weight-only) compressed-tensors Serves the latest gated ERP LoRA merge as an **NVFP4A16** (weight-only) compressed-tensors
checkpoint so the GX10 is free to train the next run. checkpoint so the GX10 is free to train the next run.
@@ -38,7 +38,7 @@ flip plus `docker compose up -d`.*
json_schema` (guided decoding) 18/18 on this seat** — that is the deterministic path for a json_schema` (guided decoding) 18/18 on this seat** — that is the deterministic path for a
forced call; `tool_choice: auto` works normally. A parser plugin that re-enables guided JSON forced call; `tool_choice: auto` works normally. A parser plugin that re-enables guided JSON
would also need JSON extraction in the engine-parser path; not attempted. would also need JSON extraction in the engine-parser path; not attempted.
- **GPU1 is shared** — check real usage (`nvidia-smi --query-compute-apps=pid,used_memory`) before - **GPU0 is shared** (since 2026-10-05: cyberprev, voices, coder, parakeet-nemo; KV pinned 5.5 GiB = 346,436 tokens, 1.32×) — check real usage (`nvidia-smi --query-compute-apps=pid,used_memory`) before
raising `ERP_GPU_MEM_UTIL`; the flag sizes KV, not CUDA context. raising `ERP_GPU_MEM_UTIL`; the flag sizes KV, not CUDA context.
- **Rollback / next run:** point `ERP_MODEL` + `ERP_SERVED_NAME` at the next quant dir, keep the - **Rollback / next run:** point `ERP_MODEL` + `ERP_SERVED_NAME` at the next quant dir, keep the
previous on disk. Deploy with `scripts/deploy-stack.sh ana-ml2 erp-seat`. previous on disk. Deploy with `scripts/deploy-stack.sh ana-ml2 erp-seat`.
+1 -1
View File
@@ -151,7 +151,7 @@ services:
- homepage.group=AI - Inference - homepage.group=AI - Inference
- homepage.name=char-rp-fast (MeroMero 26B-A4B, NVFP4A16 MoE) - homepage.name=char-rp-fast (MeroMero 26B-A4B, NVFP4A16 MoE)
- homepage.icon=mdi-fire - homepage.icon=mdi-fire
- homepage.description=MeroMero A4B abliterated RP seat, NVFP4A16 weight-only, vision intact (fv-ml1 GPU1) - homepage.description=MeroMero A4B abliterated RP seat, NVFP4A16 weight-only, vision intact (fv-ml1 GPU0)
- homepage.href=http://10.251.50.54:${ERP_PORT:-8021}/docs - homepage.href=http://10.251.50.54:${ERP_PORT:-8021}/docs
networks: networks:
+4 -3
View File
@@ -1,12 +1,13 @@
# gen-small-seat — Qwen3.6-35B-A3B Heretic (llmfan46), fv-ml1 GPU 0, :8026. # gen-small-seat — Qwen3.6-35B-A3B Heretic (llmfan46), fv-ml1 GPU 1 (since 2026-10-05), :8026.
# Copy to .env on the host at /opt/docker/compose/gen-small-seat/.env. # Copy to .env on the host at /opt/docker/compose/gen-small-seat/.env.
GEN_SMALL_IMAGE=vllm/vllm-openai:nightly-e9d1398d9edfd90fcc1cf783805240e3effec013 GEN_SMALL_IMAGE=vllm/vllm-openai:nightly-e9d1398d9edfd90fcc1cf783805240e3effec013
API_KEY= API_KEY=
# ── Placement ── shares GPU 0 with cyberprev (sec). KV pinned in bytes so the two # ── Placement ── shares its card with other seats. KV pinned in bytes so the two
# co-resident seats don't fight over a util fraction. # co-resident seats don't fight over a util fraction.
GEN_SMALL_GPU_ID=0 # 2026-10-05 rebalance: moved to GPU 1 (with meromero-rp and intern-decision).
GEN_SMALL_GPU_ID=1
GEN_SMALL_PORT=8026 GEN_SMALL_PORT=8026
GEN_SMALL_CONTAINER_NAME=vllm-gen-small GEN_SMALL_CONTAINER_NAME=vllm-gen-small
+8 -1
View File
@@ -1,5 +1,5 @@
# gen-small-seat — a fast, cheap "gen-small" seat: Qwen3.6-35B-A3B (3B active MoE), # gen-small-seat — a fast, cheap "gen-small" seat: Qwen3.6-35B-A3B (3B active MoE),
# Heretic-abliterated, on fv-ml1 GPU 0, :8026. # Heretic-abliterated, on fv-ml1 GPU 1, :8026.
# #
# WHY: a tiny-active (A3B = ~3B active of 35B total) gen tier for high-volume / low-latency # WHY: a tiny-active (A3B = ~3B active of 35B total) gen tier for high-volume / low-latency
# work, complementing the big gen seat (flash-next :8022). Operator-directed 2026-09-14. # work, complementing the big gen seat (flash-next :8022). Operator-directed 2026-09-14.
@@ -59,6 +59,13 @@ services:
# attention footprint make this cheap; tuned to a sane concurrency band, not max. # attention footprint make this cheap; tuned to a sane concurrency band, not max.
- --kv-cache-memory - --kv-cache-memory
- "${GEN_SMALL_KV_CACHE_MEMORY:-5905580032}" - "${GEN_SMALL_KV_CACHE_MEMORY:-5905580032}"
# Default output cap (2026-10-05, Prime). A request that OMITS max_tokens stops at 32,768
# instead of max_model_len - prompt; an explicit max_tokens still wins (vLLM get_max_tokens:
# request value first, generation-config max_new_tokens only as the fallback). Reason: 9
# runaway replies in 7 days, each ~260k tokens over 20+ min (albok-nemi, all-agents-local),
# while the longest normal reply was 6,357 tokens.
- --override-generation-config
- '{"max_new_tokens": 32768}'
- --max-model-len - --max-model-len
- "${GEN_SMALL_MAX_MODEL_LEN:-262144}" - "${GEN_SMALL_MAX_MODEL_LEN:-262144}"
- --max-num-seqs - --max-num-seqs
+3
View File
@@ -92,6 +92,9 @@ service. This container may therefore use its rest (8,812 MiB) plus GPU 1's nvid
> 3,981 MiB, about 2.4 GB short of a 32k call. Fixed 2131–2135 the same night: meromero and coder now pin KV in > 3,981 MiB, about 2.4 GB short of a 32k call. Fixed 2131–2135 the same night: meromero and coder now pin KV in
> bytes (`playbooks/fv-ml1-kv-pin-20261005.yaml`), and `Free` reads **11,801 MiB**, a 20,613 MiB budget, so > bytes (`playbooks/fv-ml1-kv-pin-20261005.yaml`), and `Free` reads **11,801 MiB**, a 20,613 MiB budget, so
> ~5.4 GB is spare at the 15,220 peak. The static seats can no longer drift across restarts. > ~5.4 GB is spare at the 15,220 peak. The static seats can no longer drift across restarts.
> **Later that night (2226–2249), the GPU 0/1 rebalance** (`playbooks/fv-ml1-rebalance-20261005.yaml`) left this
> card with gen-small, meromero-rp and this service only. `Free` read 10,864 MiB right after, and should settle
> near ~9.7 GB once gen-small's post-boot growth levels off. That is a ~18.5 GB budget, so ~3.3 GB spare at the 32k peak.
Measured on the live service (per-process nvidia-smi every 0.1 s; single `noul` question unless noted; n=3, deterministic): Measured on the live service (per-process nvidia-smi every 0.1 s; single `noul` question unless noted; n=3, deterministic):
+4 -3
View File
@@ -83,12 +83,13 @@ HF_TOKEN=
# and the keyless zed-fim-proxy (stacks/zed-fim-proxy). util 0.06 (~5.7 GB) holds # and the keyless zed-fim-proxy (stacks/zed-fim-proxy). util 0.06 (~5.7 GB) holds
# the 1.5B fp16 + fp8 KV; 8192 ctx is ample for FIM (KV = 13.75x concurrency). # the 1.5B fp16 + fp8 KV; 8192 ctx is ample for FIM (KV = 13.75x concurrency).
CODER_PORT=8020 CODER_PORT=8020
CODER_GPU_ID=1 # 2026-10-05 rebalance: GPU 0; context raised to the card-published maximum (32,768).
CODER_GPU_ID=0
CODER_MODEL=Qwen/Qwen2.5-Coder-1.5B CODER_MODEL=Qwen/Qwen2.5-Coder-1.5B
CODER_SERVED_NAME=qwen2.5-coder-1.5b CODER_SERVED_NAME=qwen2.5-coder-1.5b
CODER_MAX_MODEL_LEN=8192 CODER_MAX_MODEL_LEN=32768
CODER_KV_CACHE_DTYPE=fp8 CODER_KV_CACHE_DTYPE=fp8
CODER_GPU_MEM_UTIL=0.055 CODER_GPU_MEM_UTIL=0.06
# KV pinned 2026-10-05 (77,056 tokens; see compose.yaml). Util now only gates the boot check. # KV pinned 2026-10-05 (77,056 tokens; see compose.yaml). Util now only gates the boot check.
CODER_KV_CACHE_MEMORY=1104674816 CODER_KV_CACHE_MEMORY=1104674816
CODER_MAX_NUM_SEQS=32 CODER_MAX_NUM_SEQS=32
+1 -1
View File
@@ -16,7 +16,7 @@ model on the box, currently three services:
**Server:** ana-ml2 **Server:** ana-ml2
**Ports:** `8001` (embed), `8002` (rerank), `8003` (reward) — all configurable via `.env` **Ports:** `8001` (embed), `8002` (rerank), `8003` (reward) — all configurable via `.env`
**GPU:** all three services share GPU 1 by default (configurable) **GPU:** only `vllm-coder` is live in this stack; since 2026-10-05 it runs on GPU 0 (`CODER_GPU_ID`) at the card-published maximum context of 32,768 (KV pinned 77,056 tokens, 2.35×). The embed/rerank/reward services here are retired
## Why one process per service ## Why one process per service
+7 -1
View File
@@ -69,7 +69,13 @@ separately.
## Placement warnings ## Placement warnings
⚠ **GPU 0 is now at 96.0 of 97.9 GB.** This seat's 10.7 GB went into the last real gap on > **2026-10-05 rebalance:** context raised 8,192 → **40,960** (Qwen3-4B's native maximum: the card's 32,768
> plus its 8,192 prompt reserve; 131,072 would need YaRN, which the card says hurts short prompts). KV pinned
> 6 GiB bf16 = 43,680 tokens (1.07×), util 0.16, resident ~15.4 GB. Still GPU 0, now beside cyberprev,
> erp-seat, coder and parakeet-nemo. The LoRA voices were trained on short prose; long-context behaviour
> with an adapter is unmeasured.
⚠ *(historical, pre-2026-10-05)* **GPU 0 is now at 96.0 of 97.9 GB.** This seat's 10.7 GB went into the last real gap on
fv-ml1: GPU 1 has ~5.7 GB, GPU 2 has ~2.4 GB, and GPU 3 is a held reserve for a future fv-ml1: GPU 1 has ~5.7 GB, GPU 2 has ~2.4 GB, and GPU 3 is a held reserve for a future
full-card seat (`flash-next` alone needs 93 of 96 GiB). **There is no room for another seat full-card seat (`flash-next` alone needs 93 of 96 GiB). **There is no room for another seat
here without a placement decision.** here without a placement decision.**
+3 -3
View File
@@ -87,11 +87,11 @@ services:
- --port - --port
- "8000" - "8000"
- --gpu-memory-utilization - --gpu-memory-utilization
- "${VOICES_GPU_MEM_UTIL:-0.12}" - "${VOICES_GPU_MEM_UTIL:-0.16}"
- --kv-cache-memory - --kv-cache-memory
- "${VOICES_KV_CACHE_MEMORY:-2147483648}" - "${VOICES_KV_CACHE_MEMORY:-6442450944}"
- --max-model-len - --max-model-len
- "${VOICES_MAX_MODEL_LEN:-8192}" - "${VOICES_MAX_MODEL_LEN:-40960}"
- --max-num-seqs - --max-num-seqs
- "${VOICES_MAX_NUM_SEQS:-8}" - "${VOICES_MAX_NUM_SEQS:-8}"
- --dtype - --dtype