Commit Graph
26 Commits
Author SHA1 Message Date
ff4bbbc09d LMCache: in-repo L2 default + LMCACHE_L2=1 toggle, preflight disk-check, corrected sizing (#422)
* LMCache: in-repo L2 default + LMCACHE_L2=1 toggle, preflight disk-check, corrected sizing

- LMCACHE_L2=1 enables the L2 disk tier at a gitignored repo-root lmcache-kv/ (zero
  config); LMCACHE_L2_ADAPTER still fully overrides. L2 stays OFF by default.
- preflight_lmcache_ram now also soft-warns on low L2 disk space at the L2 host dir.
- CORRECTED capacity (measured > estimated): the LMCache offload cache is
  ~131 KB/token MEASURED (lmcache_mp_l1_memory_usage_bytes 4.93 GB + 4.6 GB on L2
  disk, 36,808-token session) — ~7x the GPU's 18.9 int8-PTH rate, which only sets
  max servable context. Prior docs used 18.9 for capacity and overstated it ~7x:
  --l1-size-gb 30 holds ~4 x 50K sessions (not ~33), <1 full 262K.
- Added a RAM/disk-vs-context sizing table to INTERNALS.md.
- Measured L2 rehydrate: 4.8s vs 43s cold re-prefill (~9x), cross-restart persistence
  confirmed; use the fs adapter (not nixl_store — this image's NIXL is broken).

Refs #133.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* LMCache: document fp8 serde is broken on this model (no reduced-size L2 yet)

Measured 2026-06-17: LMCache's fp8 serde shape-errors on this model's KV
('[2,16,1584,260]' invalid — wrong layout for the hybrid GDN/head_dim-256/int8-PTH
source), 60 serialize fails, L2 stays empty. CacheGen-MP is 'coming soon'. So L2 is
uncompressed (~131 KB/token); the fp8-serde toggle was NOT wired (it doesn't work).
Re-test trigger noted. Refs #133.

---------

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-06-18 00:06:54 +05:00
3531fd3551 Add opt-in LMCache KV-offload compose (vllm/qwen-27b-dual-lmcache, incubating) (#421)
Layers an LMCache tiered persistent prefix-KV cache (MP/HMA connector) onto the
dual-max fidelity profile (FP8 + int8-PTH KV + MTP n=3 @262K). Controlled A/B
shows ZERO decode penalty (74 narr / 94 code TPS == without LMCache, MTP intact
~83% accept); reuses cached prefix KV across long / multi-session workloads
(cold->warm TTFT ~7-8x) instead of re-prefilling.

- New vllm-lmcache engine profile: lmcache/vllm-openai DIGEST-pinned (the tag is
  mutable — bundled vLLM 0.22.1-dev then 0.23.1-dev under the same tag).
- preflight_lmcache_ram guard: rejects --l1-size-gb over-allocation even under
  --force (the l1=100-on-94GB host-OOM that forced a reboot, #133).
- env-gated L2 disk tier hook (LMCACHE_L2_ADAPTER, off by default).
- INTERNALS.md RAM/disk sizing section; registry entry + count bumps; full
  guard suite green.

Incubating (hidden, --force to launch): runs LMCache's third-party image with a
newer vLLM than our v0.22.0 pin; L2 latency unmeasured on-rig; 38 GB on-demand.

Refs #133.

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-06-17 20:46:38 +05:00
DeuceBucket 6be52294c8 feat(weights): optional revision: pin in weights-fetch schema (#319) (#408)
adds an optional `revision:` key per weights variant. weights.py emits
WEIGHT_REVISION; setup.sh threads it into `hf download --revision` and
pins the post-download sha-verify etag lookup to the same revision, so a
stale pin can't false-fail against a newer HEAD. preflight's manual hint
mirrors the flag. unset = track HEAD, so behavior is unchanged for every
current entry (nothing sets revision: today).

this is the weights half of #316: upstream quant repos re-quant
silently, and we had no lever to pin the bytes a BENCHMARKS row was
measured against. engine images already pin; weights didn't. mechanism
only, no real entry is pinned in this PR (that's a per-entry,
rig-validated call that's yours to make).

refs #319, #316
2026-06-15 06:35:43 +05:00
572b1c8ac0 scripts: auto-detect served model from /v1/models in verify/bench (#372) (#388)
report.sh autodetects the running container + URL + engine but not the
served model name, so verify.sh / verify-full.sh / verify-stress.sh /
bench.sh fell back to a hardcoded MODEL=qwen3.6-27b-autoround. Against a
non-qwen vLLM endpoint (e.g. vllm/gemma-26ba4b-single serving
gemma-4-26b-a4b-awq) every request 404'd with "The model
`qwen3.6-27b-autoround` does not exist", failing every check (#372).
llama.cpp ignores the request's model field, so the same wrong default
silently "worked" there (#371) — which masked the bug.

Add a shared preflight_autodetect_model helper that resolves the served
name from the endpoint's /v1/models (first id) when MODEL is unset, and
call it in the four affected scripts before their qwen fallback. This
mirrors what soak-test.sh / bench-agentic.sh / quality-test.sh already
do. An explicit MODEL= still wins (important for llama-swap/multi-model
endpoints); the qwen literal stays as a last resort if detection no-ops.

Validated: unit (resolve / respect-explicit / unreachable-fallback),
end-to-end against a mock /v1/models, and the full scripts/tests suite
stays green (the one pre-existing test-compose-registry-disk failure is
unrelated — untracked nex-n2-mini WIP composes).

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-06-12 14:54:51 +05:00
noonghunnaandClaude Opus 4.8 345f24ee1d preflight: resolve ${VAR:-default} model paths in compose-deps check
preflight_compose_deps falsely reported "missing model files" for any compose
whose vLLM --model / --speculative-config path uses an env-default, e.g.
  --model /root/.cache/huggingface/${MODEL_SUBDIR:-gemma-4-12b-autoround-int8}
The path grep's char-class excluded `:` and `}`, truncating the token to
`${MODEL_SUBDIR` before _preflight_compose_path_default (which already resolves
${VAR:-default}) ever ran → switch.sh aborted launching ALL 4 gemma-12b composes.

Two hardenings:
  - path grep: stop only at real delimiters (quote/space/comma), not `:`/`}`, so
    `${VAR:-default}` survives to the resolver. Also skip comment lines (`^\s*#`)
    so a path written in prose (e.g. "...expects it at /root/.cache/.../assistant.")
    isn't mistaken for a dependency.
  - _preflight_compose_path_default: strip a trailing `}` left when a path is the
    DEFAULT inside an outer ${VAR:-/root/.cache/huggingface/<path>} (the grep
    anchors mid-expansion → captures `<path>}`).

Verified: all 4 gemma-12b composes now pass preflight_compose_deps; qwen3.6-27b
+ gemma-4-31b composes still pass (no regression). Guard suite 41/41 (the lone
transient fail was bench-mock autodetect colliding with a concurrently-running
soak service — unrelated, passes with PREFLIGHT_NO_AUTODETECT=1).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-04 19:39:10 +00:00
noonghunnaandClaude Opus 4.8 f48a7ff514 preflight: detect endpoint by engine port, not a model-name allowlist (#310)
bench.sh / verify.sh / verify-stress.sh share preflight_autodetect_endpoint,
which only recognised a hardcoded list of container-name prefixes (qwen36-27b,
gemma-4-31b, ...). Any model on a differently-named container (gemma-4-12b,
qwen-35b-a3b, beellama, any BYO compose) fell through and the tools silently
defaulted to port 8020 → ran against the wrong endpoint (#310, syangsao).

Fix: detect by the ENGINE-INTERNAL port mapping instead — vLLM 8000 /
llama.cpp 8080 / sglang 30000. Prefer a recognised club-3090 engine-family
prefix when several match; otherwise take the first and print how to override
(CONTAINER=/URL=). Validated live: picks vllm-gemma-4-12b-int8-mtp on :8038
(missed by the old allowlist) and ignores non-engine containers (benchlocal
sandboxes :9000, litellm :4000). URL/CONTAINER env still win.

Guard suite 41/41 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-04 18:08:45 +00:00
noonghunnaandClaude Opus 4.8 bb9f9f23a0 Repoint stale single-card launcher hints off deprecated vLLM composes
When the Genesis/nightly single-card vLLM family was deprecated 2026-05-31
(vllm/default, long-text, long-vision, long-text-no-mtp, bounded-thinking,
tools-text), the hand-written user-facing hints weren't repointed (the
deprecation-checklist step 4 was missed) — surfaced by a #35 commenter who
read them as live. The DEFAULTS resolver was fine (vllm/default → vllm/minimal),
but the docs still advertised the dead composes.

- switch.sh header: the "Single-card vLLM" subset listed 6 deprecated composes
  as recommended options AND omitted beellama/dflash (the actual single-card
  default). Rewrote to lead with beellama/dflash + the functional vllm/minimal,
  with a dated deprecation note (mirrors the dual-section note).
- preflight.sh: the "start with: switch.sh vllm/default" hint now points at
  beellama/dflash (the single-card default).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 02:28:51 +00:00
noonghunnaandClaude Opus 4.8 180adb083e preflight: detect beellama + check spec-draft-model drafter GGUF
beellama.cpp composes (ghcr.io/{anbeeld/beellama.cpp,noonghunna/beellama-cpp})
were not matched by the is_llamacpp image regex, so they fell into the vLLM
HF-cache branch and skipped the model-presence check entirely. And even on the
llama.cpp branch, only `-m`/`--model` (the target) was checked — never the
`--spec-draft-model` drafter. Result: a missing target OR drafter GGUF passed
preflight and surfaced as a cryptic in-container "failed to open GGUF file"
crash instead of preflight's "download this:" hint (#288 eddie + George).

- Add `beellama` to the is_llamacpp image regex.
- Collect drafter paths from --spec-draft-model/--model-draft/-md into their own
  presence check, with a DRAFT_FILE env override mirroring GGUF_FILE/MMPROJ_FILE.
- New test case: beellama target-present + drafter-missing must refuse with the
  drafter path + its `hf download Anbeeld/...` hint; both present must pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 02:45:59 +05:00
noonghunna b327f0311a refactor(gemma): rename dual slugs + ladder gemma-bf16-mtp to 131K (#286)
Rename vllm/gemma-int8→gemma-int8-mtp + vllm/gemma-mtp→gemma-bf16-mtp (both carry MTP n=4; only KV format differs); kv-calc alias fix. Ladder gemma-bf16-mtp default 32K→131K (measured BF16 KV pool 196,527 tok @ 0.95; verify-stress 8/8, NIAH→120K, bench 118.8/154.3, zero TPS regression). Codify the dual-card max-context-priority rule in DUAL_CARD.md. Suite 41/41.
2026-06-01 01:20:46 +05:00
noonghunna 4ff14090c9 Gemma vLLM -> v0.22.0: bump dual gemma-mtp, deprecate gemma-mtp-tp1 (#278)
Bump vllm/gemma-mtp (dual, bf16+MTP) to stable v0.22.0 (validated 5/5 coherent). Deprecate vllm/gemma-mtp-tp1: fp8 KV is hardware-impossible for Gemma 4 on Ampere sm_86 (fp8e4nv kernel unsupported; fp8_e5m2 rejected by gemma4 attention allowlist; nvfp4 Blackwell-only), live-confirmed on v0.22.0. Remove the dead DEFAULTS[(gemma-4-31b,vllm,single)] row; repoint launch/setup/preflight single-card Gemma -> beellama/gemma-dflash. Also fixes a pre-existing unterminated NEXT_STEPS_NOTE string in setup.sh's gemma-4-31b arm. Suite 38/38.
2026-05-31 07:13:17 +05:00
0e12fb0225 scripts/preflight.sh: surface 4090 / 5090 hint with FAQ deep-link (#247)
When a user launches with a 4090 or 5090, preflight now prints a single
hint line pointing at the matching FAQ entry. Caught one class of UX
gap where users land on launch.sh without reading the README + run into
unexpected ctx ceilings (4090) or miss the 32 GB-headroom story (5090).

Detection: greps `nvidia-smi -L` output for "RTX 4090" / "RTX 5090".
Quiet on 3090-only rigs. Pure-additive — no behavior change beyond the
extra echo lines.

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-28 17:50:12 +05:00
noonghunna 5f37ae6d4d Add profile-backed model weight fetch registry 2026-05-24 16:08:21 +00:00
noonghunna 99d264b2c4 Generalize compose model preflight 2026-05-24 13:59:18 +00:00
noonghunnaandClaude Opus 4.7 d9fdab2961 fix(soak,preflight): recognize llama-cpp / ik-llama containers in autodetect (#403)
soak-test.sh::auto_container only matched vllm-* → rc=2 "no running
container" on the soak step of llama.cpp / ik_llama rebench-full, aborting
the chain before the aider step. Mirror preflight's canonical engine-prefix
set into auto_container, and probe internal 8080/30000 (not just 8000) in
endpoint_from_container. Also add ik-llama-qwen36-27b to
preflight_autodetect_endpoint + preflight_running globs — the ik leg's
autodetect was a latent gap (only llama-cpp was listed). Closes #403.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-23 16:03:22 +00:00
40a6688baa scripts: endpoint-first --url/--model/--engine for non-Docker engines (#174)
Adds a clean front-door to rebench-full.sh so users running OpenAI-compatible
endpoints outside our pre-baked Docker composes (llama-swap, ramalama, host-
build llama-server, ik_llama.cpp, raw vLLM) can run the canonical 5-phase
suite without script edits or env-soup.

Today the entry chain assumes a `vllm-qwen36-27b`-style container exists and
calls `docker logs` / `docker inspect` for ENGINE_KIND detection + spec-decode
metric scraping. The infra to bypass this was already 80% there (CONTAINER=none
host mode already in soak-test.sh + verify-stress.sh, docker-inspect guards
already in bench.sh line 82), but there was no top-level flag, and the silent
preflight pipefail bug below meant users hit a confusing no-op exit instead of
a useful error.

Changes:

* rebench-full.sh: add --url/--model/--engine. When --url is set we export
  PREFLIGHT_NO_AUTODETECT=1 + CONTAINER=none so the chained scripts skip the
  Docker scrapes (they no-op cleanly).
* rebench-full.sh: container snapshot block now wrapped in
  `[[ "$CONTAINER" != "none" ]] && command -v docker` so it doesn't try to
  inspect the wrong (or no) container in endpoint-first mode.
* rebench-full.sh: endpoint-not-responding error now distinguishes the --url
  case (check host/port) from the default case (start a compose or pass --url).
* bench.sh: same CONTAINER=none guard for the trailing `docker logs ... grep
  SpecDecoding metrics` block.
* preflight.sh: fix a pre-existing silent-exit when no container matches the
  autodetect regex. `grep -E` returns 1, which under `set -euo pipefail` in
  the caller killed rebench-full.sh before it reached its own endpoint-error
  path. Adding `|| true` to the pipe restores the intended fall-through.
* preflight.sh: regex now also recognises sglang-qwen36-27b containers + port
  30000 (added with v0.7.x sglang experimental tree, not yet auto-detected).
* docs/FAQ.md: document the new --url flag under the "Numbers from your rig"
  section.

Tested:

  - bash -n on all three scripts: OK
  - --help renders the new endpoint-first block
  - Default no-flag mode now exits cleanly with a helpful error (was silent
    exit 1 pre-fix)
  - --url FAKE skips autodetect and emits the external-endpoint-specific hint
  - --url + non-existent host: clean error, no Docker side-effects

Motivated by ampersandru's external eval in discussion #152 — running
ik_llama via llama-swap forced him to set PREFLIGHT_NO_AUTODETECT=1 manually
and edit scripts to bypass docker assumptions. With this PR the invocation
becomes:

  bash scripts/rebench-full.sh \
    --url http://192.168.29.235:8887 \
    --model 'Qwen3.6-27B MTP ik_llama:instruct' \
    --engine llama-cpp

— and the whole 5-phase chain (bench + verify-stress + quality-full + soak
+ aider-polyglot) works against any OpenAI-API endpoint.

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-21 04:07:50 +05:00
noonghunna 26985527f7 Add hardware-aware compose preflight
Release / release (push) Failing after 50s
2026-05-13 18:03:37 +00:00
noonghunnaandClaude Opus 4.7 fbf343129c docs: use \$MODEL_DIR placeholder, not the dev rig's /mnt/models/huggingface/
User docs were hardcoding the dev rig path (/mnt/models/huggingface/...) as
if it was canonical. It's not — cross-rig users have models at /data/models,
~/models, /mnt/nvme/llms, etc. Setting MODEL_DIR per their setup is the
intended UX (the compose already supports it via env-var default).

Updates:
- models/qwen3.6-27b/llama-cpp/README.md: download examples now use
  \$MODEL_DIR/qwen3.6-27b-gguf/ instead of /mnt/models/huggingface/...
- models/qwen3.6-27b/llama-cpp/compose/single/docker-compose.yml: header
  comment uses \$MODEL_DIR/qwen3.6-27b-gguf/ for download examples + says
  "MODEL_DIR=/your/models/dir docker compose up -d" instead of our path.
- docs/engines/LLAMA_CPP.md: same treatment + cleaned up Qwen3.5 + DFlash
  draft path examples to also use \$MODEL_DIR.
- scripts/preflight.sh: hf download hint shows literal \${MODEL_DIR} so user
  knows what to set, plus explicit "set MODEL_DIR first" line. Previously
  echoed the resolved relative path (../../../../models-cache) which lands
  outside the repo if pwd isn't the compose dir.

Caught by RobH589 in club-3090#116 — they hit the path-resolved-to-root-of-drive
case from the relative-path default. Closes the doc UX side; the compose's
env-var override mechanism was already correct.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-10 17:13:29 +00:00
noonghunnaandClaude Opus 4.7 cf7f1959fd fix: 4 stale refs missed in 2026-05-10 reorg push (caught by RobH589 #116)
After the GGUF dir move (/mnt/models/gguf/qwen3.6-27b/ → /mnt/models/
huggingface/qwen3.6-27b-gguf/), four refs were not updated and led to
a path-mismatch loop reported in club-3090#116:

- scripts/preflight.sh `hf download` hint pointed at qwen3.6-27b/, but
  the compose default expects qwen3.6-27b-gguf/. Same for the mv hint
  for mmproj relocation, and the in-container mmproj default at line 329.
- models/qwen3.6-27b/llama-cpp/README.md example command still said
  `MODEL_DIR=/mnt/models/gguf` (now /mnt/models/huggingface).
- models/qwen3.6-27b/llama-cpp/compose/single/{docker-compose,concurrent}.yml
  header comment said "Q5_K_XL" but the actual default has been Q3_K_XL
  for a while (this one predates the reorg — just stale doc).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-10 17:06:20 +00:00
Erik LaBiancaandClaude Sonnet 4.6 73c31848ea fix: BIND_HOST opt-in + localhost script fixes (#109)
Three related fixes for running benchmarks without IDE agent interference:

1. All 18 vLLM compose files: port binding is now
   ${BIND_HOST:-0.0.0.0}:${PORT:-<n>}:8000
   Setting BIND_HOST=127.0.0.1 in .env restricts the API to localhost,
   preventing IDE agents (Cline, Cursor) from competing for the
   max-num-seqs=1 slot and causing verify-stress HTTP 000 failures.

2. scripts/preflight.sh: port auto-detection regex now matches
   127.0.0.1:<port>->8000/tcp in addition to 0.0.0.0: and [::]:
   Previously all verify-*/bench scripts silently produced no output
   when BIND_HOST=127.0.0.1 was set.

3. scripts/report.sh: SOAK_TIMEOUT_S is now forwarded to soak-test.sh
   from the shell environment. Previously the variable was read from
   compose .env (docker-compose only) and silently ignored by the
   script, always using the 1800s default regardless of what was set.

Docs: .env.example gains BIND_HOST and SOAK_TIMEOUT_S entries.

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-05-10 05:50:08 +05:00
e6e6c827ba add Gemma 4 31B + Google MTP drafter (first Ampere data) (#68)
* add Gemma 4 31B + Google MTP drafter — first Ampere consumer cross-rig data

Adds models/gemma-4-31b/ tree with two compose variants (TP=2 + TP=1),
vendored vLLM PR #41745 overlay, switch.sh / launch.sh wiring, plus the
script-level extensions needed to make `bash scripts/{bench,verify-*,
soak-test}.sh` auto-detect Gemma containers.

Validated 2026-05-05 on 2× RTX 3090 (Ampere sm_86, PCIe-only):
  - canonical bench (3 warm + 5 measured per prompt):
      narrative wall TPS = 108.87 (CV 3.2%)
      code      wall TPS = 142.25 (CV 2.3%)
  - soak-continuous (5 sessions × 5 turns × 4 prompts = 100 turns):
      verdict PASS, 0 errors, 0 silent-empty, 0 MiB VRAM growth,
      98.3% TPS retention, p50 decode 111.79
  - VRAM 22.5 GB/card. Speedup vs no-spec-decode baseline: 1.79× narr / 2.31× code.

First published Ampere consumer numbers on Google's Gemma 4 MTP "assistant"
drafters (released 2026-05-05). Discussion #67 has the announcement +
upstream context. BENCHMARKS.md gets a new "Gemma 4 31B" section with
both TP=2 (working) and TP=1 (upstream-blocked on Ampere) rows.

Components landed:
  - models/gemma-4-31b/vllm/compose/docker-compose.gemma-mtp.yml (TP=2)
  - models/gemma-4-31b/vllm/compose/docker-compose.gemma-mtp-tp1.yml
    (TP=1 — preserved for re-test when upstream Ampere fp8 path lands)
  - models/gemma-4-31b/vllm/patches/vllm-gemma4-mtp/ — vendored overlay
    of vllm-project/vllm#41745 (lucianommartins/gemma4-mtp). 7 modified
    Python files RO-mounted over the stock nightly image. Same shape as
    vllm-marlin-pad. Drop the entire tree when PR merges + propagates.
  - scripts/switch.sh + launch.sh: vllm/gemma-mtp + vllm/gemma-mtp-tp1
    variants registered. RUNNING_PATTERN extended for vllm-gemma-4-31b*.
  - scripts/preflight.sh: autodetect_endpoint extended for gemma containers.
  - scripts/soak-test.sh: container grep extended.
  - scripts/soak-helper.py: SOAK_NO_CHAT_TEMPLATE_KWARGS=1 env knob to
    skip the Qwen3-specific `chat_template_kwargs.enable_thinking` body
    field for non-Qwen models that reject it (Gemma 4, etc.).
  - BENCHMARKS.md: new Gemma 4 31B section with both TP rows.
  - docs/UPSTREAM.md: PR #41745 row + the Ampere fp8-blocked finding +
    transformers 5.8.0 dependency entry.

Pre-merge dependencies (drop when both land):
  1. vllm-project/vllm#41745 → drop the patches/ tree + the volume block
  2. transformers ≥ 5.8.0 → drop the entrypoint pip install line

Out of scope:
  - TP=1 single-card. Tested + upstream-blocked on Ampere consumer:
    fp8 KV needed for fit; fp8_e4m3 hits Triton "fp8e4nv not supported"
    on sm_86; fp8_e5m2 rejected by gemma4_mm.py:1336 allowlist. Compose
    is preserved with the failing config baked in for future re-test.
  - Gemma 4 26B-A4B MoE single-card. Active params ~4B → should fit
    cleanly without fp8. Queued as the obvious follow-up.
  - vllm/vllm-openai:gemma4-0505-cu129 image swap. The recipe-image is
    Hopper-tagged and may not include sm_86 kernels; testing it would
    collapse this whole 4-layer wrangle to a one-line image bump but
    needs explicit Ampere validation first.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* models/gemma-4-31b/vllm/cache: gitignore + README (mirror qwen3.6 pattern)

Replaces the .gitkeep stubs with the standard .gitignore (ignore everything
except .gitignore + README.md) + README documenting the cache lifecycle.
Mirrors models/qwen3.6-27b/vllm/cache/ exactly.

Boot times this enables (validated tonight):
  - cold first boot: ~3-7 min (TP=1 / TP=2)
  - warm subsequent boot: ~2-3 min (cache hit)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-06 02:13:21 +05:00
noonghunna 29718cac99 scripts: auto-detect running container + port in verify / bench (closes #52 promise)
Verify-full / verify-stress / bench previously hardcoded
URL=http://localhost:8020 + CONTAINER=vllm-qwen36-27b. That assumption
silently broke for anyone running a non-default variant — dual-turbo on
8011, dual-dflash on 8012, etc. — making the embedded report.sh chain
emit false negatives. Reported by sudepo on club-3090#52.

Add preflight_autodetect_endpoint() that:
  - scans `docker ps` for one of our container patterns
    (vllm-qwen36-27b* / llama-cpp-qwen36-27b*)
  - extracts the host port from its 0.0.0.0:<port>->{8000,8080}/tcp
    mapping
  - sets URL + CONTAINER, but ONLY for fields the user didn't already
    set explicitly (env-var override always wins)
  - prints one [autodetect] line so the user sees what was picked
  - falls back silently to the existing hardcoded defaults if nothing
    is detected (no behaviour regression for fresh setups)

Wired into the three test scripts. Skip via PREFLIGHT_NO_AUTODETECT=1
for the rare case where the user wants to point at a non-running
container or remote endpoint.

Verified locally:
  - autodetect with no env vars → picks up vllm-qwen36-27b-dual-turbo on
    port 8011 (matches `docker ps`)
  - autodetect with URL=... CONTAINER=... env set → preserves both
  - all four scripts pass `bash -n`

Direct commit per the docs/cosmetic-direct-to-master convention; this
is small, additive, override-preserving and falls back to existing
behaviour on detection miss.
2026-05-05 19:04:24 +00:00
b9486f13c7 fix(preflight): catch missing llama.cpp GGUF before container boot (#63)
* fix(preflight): catch missing llama.cpp GGUF before container boot

preflight_compose_deps was vLLM-only — it always required the AutoRound
INT4 weights and never validated llama.cpp GGUF / mmproj mounts. Result:
fresh users following the README to switch to llamacpp/default would hit
a 600s container timeout buried in docker logs (syangsao on #58).

Make preflight engine-aware: detect llama.cpp composes by image, parse
the `-m /models/<path>` and `--mmproj /models/<path>` args (resolving
${GGUF_FILE:-...} / ${MMPROJ_FILE:-...} defaults, env-var overrides win),
and surface a copy-pasteable `hf download unsloth/Qwen3.6-27B-GGUF ...`
one-liner when files are missing.

Verified:
- llamacpp/default + missing GGUF → preflight errors with clear fix
- llamacpp/concurrent → same correct behavior
- vllm/dual on /mnt/models/huggingface → no regression

Closes the trip-hazard on #58. setup.sh stays vLLM-default so fresh
clones don't pay 16 GB for a path most users won't take first.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* fix(preflight): correct hf download CLI form (positional, not --include)

syangsao tested PR #63 and the suggested one-liner only fetched
mmproj-F16.gguf (928 MB), not the main 14 GB GGUF. Root cause:

  hf download unsloth/Qwen3.6-27B-GGUF \
    --include 'Qwen3.6-27B-UD-Q3_K_XL.gguf' 'mmproj-F16.gguf' \
    --local-dir <dir>

`hf download` parses positional [FILENAMES]... args; when both
--include and positional filenames are passed, --include is silently
ignored:

  UserWarning: Ignoring `--include` since filenames have being
  explicitly set.

So the second arg ('mmproj-F16.gguf') becomes the only filename to
fetch, and the main weights are skipped. Fix: drop --include and
list both files as positional args, matching `hf download`'s own
example in --help:

  hf download meta-llama/Llama-3.2-1B-Instruct config.json tokenizer.json

Also clarified the mmproj-move comment: this isn't a "sometimes" —
unsloth ships mmproj-F16.gguf at the top of the GGUF repo, so it
always lands inside the --local-dir we pass. The compose default
points at qwen3.6-27b/mmproj-F16.gguf (one level up from
unsloth-q3kxl/), so the move is mandatory, not conditional.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-05 23:07:58 +05:00
noonghunnaandClaude Opus 4.7 b6c8708209 feat(preflight): compose-dependency + HF_TOKEN + KV-format checks (#37, #47, #219)
Closes the UX gap raised by @snoby on #37 — previously, launching a
compose that needs the DFlash draft model without first running
WITH_DFLASH_DRAFT=1 setup.sh would fail with vLLM's generic
"Invalid repository ID or local directory" pydantic error.

Adds three new preflight functions in scripts/preflight.sh:

1. preflight_hf_token (soft warn) — wired into setup.sh before the model
   download. Catches missing HF_TOKEN early and tells the user exactly
   what to do (visit hf.co/settings/tokens, accept T&C, export the token).
   Skip via PREFLIGHT_NO_HF_TOKEN=1.

2. preflight_compose_deps (hard error) — wired into switch.sh just before
   docker compose up. Parses the target compose YAML for known model-path
   patterns (qwen3.6-27b-dflash, qwen3.6-27b-mtp-head, qwen3.6-27b-autoround-int4)
   and verifies each exists on host. If missing, emits a clear "Fix:" line
   with the exact setup.sh invocation needed (e.g. WITH_DFLASH_DRAFT=1).
   Refuses to proceed with exit 1. Skip via PREFLIGHT_NO_COMPOSE_DEPS=1.

3. preflight_kv_format_hint (soft warn) — wired into switch.sh as the last
   pre-up check. Detects smallest VRAM via nvidia-smi; if <24 GB and the
   target compose uses turboquant_3bit_nc KV, emits the @efschu finding
   from #47 (TQ3 → fp8_e5m2 swap rule) with cross-link to docs/HARDWARE.md
   + a tools/kv-calc.py one-liner to predict the user's specific config.
   Skip via PREFLIGHT_NO_KV_HINT=1.

Tested on this rig:
- HF_TOKEN unset → warns
- Missing DFlash dir → hard errors with WITH_DFLASH_DRAFT=1 hint
- Missing main model → hard errors with generic setup.sh hint
- 24 GB rig + TQ3 compose → silent (correct, TQ3 is right pick)
- Simulated 20 GB rig + TQ3 compose → fires the fp8_e5m2 hint

All preflights are individually skippable via PREFLIGHT_NO_<NAME>=1
env vars (matches existing PREFLIGHT_NO_FETCH=1 / PREFLIGHT_NO_GENESIS_PIN=1
patterns).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-04 12:59:02 +00:00
noonghunna 43fe2a4e20 feat: detect repo drift in preflight + add scripts/update.sh
Two-part addition for the most common stale-setup pattern: user cloned
weeks ago, master has moved (Genesis pin bumps, compose changes, vendored
patch updates), they re-run their compose, hit a stale config, and file
an issue we already solved on master. Wispborne's _register_op_once and
GuiPerPT's pre-pull boot OOM both surfaced through this loop.

scripts/preflight.sh — preflight_repo_drift:
  - Skips silently if not a git repo, on a non-master branch, or if
    PREFLIGHT_NO_FETCH=1 (offline rigs / CI / forks tracking elsewhere).
  - Verifies origin remote is noonghunna/club-3090 (avoids false positives
    on forks pointing elsewhere).
  - timeout 5 git fetch --quiet origin master — bounded so flaky networks
    don't block boot.
  - On behind > 0: WARN with commit count, last-fetch age (h/d), and the
    one-line fix command. Soft-warning, never blocks. Tells user about
    PREFLIGHT_NO_FETCH=1 for opting out.

Wired into both launch.sh and switch.sh, alongside the existing
preflight_genesis_pin so users get one consolidated stale-setup signal.

scripts/update.sh — the easy upgrade path:
  - Refuses on dirty tree (git status --porcelain) — surfaces the local
    edits and tells the user to commit or stash first. We don't clobber
    the rare user who's been editing a compose locally.
  - Refuses on non-master branch — feature branches and fork-trackers
    should pull manually; this script is the master-from-origin path.
  - git pull --ff-only — no merge commits, no rebase ambiguity. Diverged
    branches get an explicit error pointing at git pull --rebase.
  - Re-runs setup.sh (idempotent — re-pins Genesis, re-vendors Marlin).
  - Tells the user to restart their variant via switch.sh — doesn't auto-
    restart, so they can A/B old-vs-new if they want.
  - --dry-run shows the plan without changing anything.
  - --force re-runs setup.sh even when up-to-date (for "I edited Genesis
    by hand and want it re-pinned" cases).

Why detection-then-explicit-command instead of "press y to auto-update":
the user will rarely have local commits (they're consumers of the recipes,
not vLLM contributors), but we still want consent — they should see what
they're committing to. The dirty-tree guard handles the rare custom-edit
case without nagging the common path.
2026-05-03 12:17:04 +00:00
noonghunnaandClaude Opus 4.7 d552ed9216 feat(preflight): warn when Genesis tree out of sync with setup.sh's declared pin
Catches the failure mode @wispborne hit in #32: user pulled latest
club-3090 (which bumped GENESIS_PIN in scripts/setup.sh from old →
new), but didn't re-run setup.sh. The on-disk Genesis tree at
models/qwen3.6-27b/vllm/patches/genesis/ stays at the OLD pin while
setup.sh's declared pin advances. vLLM boots against the outdated
tree and hits mysterious patch failures (e.g. v7.66 PN25's
infer_schema crash that v7.69 PN25 worker-spawn registration fixed).

Adds new `preflight_genesis_pin` function to scripts/preflight.sh:

- Parses `GENESIS_PIN="${GENESIS_PIN:-<default>}"` from setup.sh to
  extract the declared default value
- Reads on-disk HEAD via `git -C <genesis-dir> rev-parse --short HEAD`
- Compares (declared short-form vs on-disk short-form match)
- On mismatch: emits a [preflight] WARN block with both pins + a
  "Fix: bash scripts/setup.sh qwen3.6-27b" hint
- Soft-warning only — does not block boot

Wired into both entry points:
- scripts/launch.sh (the wizard-driven path) — adds the check after
  preflight_running, before the variant pick
- scripts/switch.sh (the direct stateless switcher) — adds the check
  inside up_variant() right before the docker compose up call

Skips silently if the Genesis tree hasn't been cloned yet (caller
should run setup.sh first; preflight isn't the place to handle that
case). Also skips if setup.sh isn't present at the expected path
(weird state — silent skip rather than false warnings).

Tested locally:
- Current state (declared 2db18df = on-disk 2db18df): silent ✓
- bash -n passes on all three modified scripts

Coverage gap: users who bypass switch.sh + launch.sh and run
`docker compose -f docker-compose.X.yml up -d` directly won't get
the warning. That's the canonical-path-vs-bypass tradeoff; we lead
with switch.sh in the docs.

Closes the procedural follow-up flagged in
[#32 comment-4364895764](https://github.com/noonghunna/club-3090/issues/32#issuecomment-4364895764)
("setup.sh should be re-run after every git pull that bumps
GENESIS_PIN. We could add a check for this — compare setup.sh's
declared pin vs the on-disk tree's HEAD, warn if they differ.").

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-03 10:01:49 +00:00
noonghunnaandClaude Opus 4.7 abc06c3e33 UX polish: pre-flight checks + cards-first wizard + PNG embeds
- scripts/preflight.sh (new) — sourceable library: docker, GPU >= N,
  disk free, GPU-idle warning, running-container note. Each error has
  an actionable Fix: hint instead of a cryptic mid-run crash.
- scripts/setup.sh + scripts/launch.sh wire pre-flight in early.
  launch.sh adds --no-preflight escape hatch.
- launch.sh wizard inverted: cards → workload → auto-pick engine.
  Newcomers can answer "how many GPUs" and "what do I want to do" but
  rarely "vLLM or llama.cpp" — engine falls out of the pick with a
  one-paragraph why. --engine override still works (filters the
  workload list to that engine).
- Embedded charts swapped SVG → PNG in README + SINGLE_CARD +
  DUAL_CARD + qwen3.6-27b/README. Clicking a PNG on GitHub opens a
  viewable image; SVGs open as raw XML. SVG remains the editable
  source — re-export PNG when SVG changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 14:29:19 +00:00