v7.69 + #35975 + Codex P103 gate fix — Cliff 2 closure recipes

Six rounds of cross-rig bisect with Codex/ChatGPT diagnosis closes
Cliff 2 at 60K on TP=1 + 24GB. Two shippable variants:

Balanced MTP (long-text.yml updated):
- Genesis v7.69 dev tip (commit 2db18df)
- Codex r1 P103 gate fix applied to nested Genesis tree (cu_seqlens=[0,T]
  treated as dense single-seq, not multi-seq varlen). Sent to Sander as
  v7.70 proposal — diff in /tmp/cliff2_v770_cu_seqlens_response.md.
- vllm#35975 backport sidecar (patch_inputs_embeds_optional.py)
- mem-util 0.93 (down from 0.95)
- max_model_len 180000 (admission ceiling at this mem-util)
- MTP K=3 retained
- 60K probe: HTTP 200 in 623s, recall correct, MTP AL=4.00

Max-context safety (long-text-no-mtp.yml — NEW):
- Same patches, but MTP off + mem-util 0.95
- max_model_len 200000 (admission unlocked by removing MTP residency)
- 60K probe: HTTP 200 in 537s, recall correct
- 90K probe: indeterminate within 25-min curl budget
- For long single-shot RAG / codebase analysis

Diagnostic chain:
1. Codex r1 identified P103 gate as too broad (cu_seqlens != None
   bypasses chunking even for single-seq [0,T] case). Applied fix.
2. T=4128 distribution showed chunked path never engages on real
   serving (vLLM's outer chunked-prefill caps T well below MAX_T).
3. Codex r2: real Cliff 2 source is residency, not gate logic.
4. PR #35975 backport (skip inputs_embeds for text-only) frees
   444 MiB at boot — necessary but not sufficient at 0.95 mem-util.
5. mem-util sweep at 0.92/0.93 with MTP+#35975 closed Cliff 2 at 60K.
6. MTP-off + 0.95 + 200K admission validated max-context variant.

Codex's P103 gate fix is semantically correct and worth shipping in
Genesis v7.70 even though it's not what closes 60K Cliff 2 on this
config (the FLA call sees T=4128 already, well below MAX_T).

Full diagnostic trail: results/v0.20-migration/v769-codex-r1-test.summary

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
noonghunna
2026-05-02 15:24:49 +00:00
parent e0e1752b3a
commit f6613c869a
6 changed files with 1202 additions and 71 deletions

View File

@@ -0,0 +1,344 @@
# ===========================================================================
# Long-text NO-MTP variant — max-context safety path (200K, MTP-off, no vision).
#
# This variant TRADES decode TPS for prefill stability. MTP K=3 spec-decode
# is OFF, freeing the draft-model residency budget for activation headroom
# at long single-shot prompts. Cliff 2 ceiling lifts past where it sits on
# the MTP-on `long-text.yml`; max_model_len declared at 200K (vs 180K on
# the MTP-on variant).
#
# Pick this variant for:
# - Long single-shot RAG queries (60K-100K context, 1 turn)
# - Whole-codebase analysis (TTFT-bounded workloads)
# - Documents Q&A where context >> response length
#
# DON'T pick this variant for:
# - Multi-turn chat, agentic coding loops — slower per-token decode
# hurts UX. Use `long-text.yml` (MTP-on, 180K) instead.
# - Tool calling — same Cliff 1 mech B caveats apply (use `tools-text.yml`)
#
# Stack: vLLM v0.20.1rc1.dev16 + Genesis v7.69 dev (commit 2db18df) +
# TurboQuant 3-bit KV + MTP DISABLED + vision tower disabled.
#
# vllm#35975 backport sidecar (`patch_inputs_embeds_optional.py`) is
# applied at boot — frees ~64 MiB GPU + 64 MiB pinned CPU on text-only
# models. Drops when PR merges upstream.
#
# Workloads that ARE safe on this compose:
# - Pure long-form text generation (essay, doc Q&A) — no tool schemas in prompt
# - RAG without tool calling (just context + question)
# - Chat without tools
# Workloads that should use `tools-text.yml` (75K + fp8 KV + PN8) instead:
# - Any IDE coding agent
# - Any flow that puts tool/function schemas in the system prompt
#
# Stack: vLLM v0.20.1rc1.dev16 + Genesis v7.65 dev (commit d89a089) +
# TurboQuant 3-bit KV + MTP n=3 + vision tower disabled. Migrated from
# vLLM dev205 + Genesis v7.64 on 2026-05-01 PM. v0.20's revised TQ FA paths
# close the synthetic 50K-token tool-prefill stress (which fired on dev205);
# real IDE-agent shapes still hit the inductor compile-path leak above.
#
# v7.65 key patches active:
# - P38B (Genesis #14 fix): compile-safe in-source hook for
# _continuation_prefill — replaces our local patch_pn12_compile_safe_
# custom_op.py
# - P15B (Genesis #15 fix): FA varlen max_seqlen_k clamp at TQ wrapper
# boundary — replaces our local patch_fa_max_seqlen_clamp.py
# - PN25: Inductor-safe silu_and_mul opaque op (covers the same surface
# as our former patch_pn12_compile_safe_custom_op.py)
# - PN26b: Genesis-original sparse-V Triton kernel for SM86 (Ampere
# consumer) — first SM86 sparse-V kernel in any public tree. 27B
# tuning: BLOCK_KV=8 num_warps=4 threshold=0.01.
# - Cliff 8 hardening: partial_apply_warnings counter in boot summary
# surfaces silent anchor-drift skips
#
# Recommended client max_tokens: 8192 (FREE thinking on this compose; budget
# for hard reasoning runs). 16384 for competition-grade problems. See
# docs/EXAMPLES.md "max_tokens defaults" section for the full table.
#
# When to pick this over long-vision:
# - You don't need image input
# - You want the maximum context on a single 3090
# - You're doing long-context text workloads (codebase analysis, RAG
# retrievals, multi-turn chat with steady-state accumulation)
#
# When NOT to pick this:
# - Anything that needs vision — use long-vision (198K + vision) instead
# - Single-shot prompts ≥5060K tokens — Cliff 2 (DeltaNet GDN) still
# applies on single-card. Route those to dual.yml (TP=2 splits the
# state across cards) or llamacpp/default (262K, different engine).
#
# Local sidecar (single one remaining on this compose):
# - patch_workspace_lock_disable.py — relaxes vllm#39226 strict
# WorkspaceManager.lock() assertion to a one-shot WARNING. P98 in v7.65
# covers this functionally but its drift marker auto-skips on v0.20
# (`UNIFORM_SINGLE_TOKEN_DECODE` false-positive). Drop when Sandermage
# ships the marker fix.
#
# Variants in this dir:
# File Ctx KV Vision Notes
# docker-compose.yml (default) 48K TQ3 ✅ Production-safe; below both cliffs
# docker-compose.long-vision.yml 198K TQ3 ✅ Long ctx + vision (cliffs closed by v0.20 + v7.65)
# docker-compose.long-text.yml (this) 214K TQ3 ❌ Engine-ceiling text-only on v0.20 + 0.985 mem-util
# docker-compose.bounded-thinking.yml 214K TQ3 ❌ Long-text + structured-CoT grammar in <think>
# docker-compose.tools-text.yml 75K fp8 ❌ IDE agents — fp8 KV alternative
#
# Run with:
# cd <repo>/models/qwen3.6-27b/vllm/compose
# docker compose -f docker-compose.long-text.yml up -d
# ===========================================================================
services:
vllm-qwen36-27b-long-text-no-mtp:
image: vllm/vllm-openai:nightly-7a1eb8ac2ec4ea69338c51dc7afd4b15010abfa8
container_name: vllm-qwen36-27b-long-text-no-mtp
restart: "no"
ports:
- "${PORT:-8021}:8000"
volumes:
- ${MODEL_DIR:-../../../../models-cache}:/root/.cache/huggingface
# torch.compile + Triton kernel caches — first boot warms (~60-90 sec
# for Dynamo trace + Inductor compile + Triton JIT); subsequent boots
# reuse cached graphs and skip recompile. Pattern from Sandermage's PROD
# launch (start_27b_int4_TQ_k8v4.sh:31-32). Closes club-3090 #22.
- ../cache/torch_compile:/root/.cache/vllm/torch_compile_cache
- ../cache/triton:/root/.triton/cache
- ../patches/genesis/vllm/_genesis:/usr/local/lib/python3.12/dist-packages/vllm/_genesis:ro
- ../patches/patch_tolist_cudagraph.py:/patches/patch_tolist_cudagraph.py:ro
# workspace_lock_disable DROPPED on v7.69 — Genesis PN34 covers the
# boot-time AND runtime _decode_attention paths. Opt-in via
# GENESIS_ENABLE_PN34_WORKSPACE_LOCK_RELAX=1 in environment block.
# patch_inputs_embeds_optional.py — backport of vllm#35975 (~64 MiB GPU +
# 64 MiB pinned CPU savings on text-only models). Drops when PR merges.
- ../patches/patch_inputs_embeds_optional.py:/patches/patch_inputs_embeds_optional.py:ro
# NOTE: patch_pn12_ffn_pool_anchor.py + patch_pn12_compile_safe_custom_op.py
# + patch_fa_max_seqlen_clamp.py all DROPPED — Genesis v7.65 covers their
# surface natively (PN12 native + PN25 + PN17 + P15B). See migration
# commit history if you need to resurrect.
environment:
# Uncomment the next line to pin to a specific GPU (e.g. GPU 0):
# - CUDA_VISIBLE_DEVICES=0
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-}
- VLLM_WORKER_MULTIPROC_METHOD=spawn
- NCCL_CUMEM_ENABLE=0
- NCCL_P2P_DISABLE=1
# v0.20 made cudagraph memory profiling default-ON (vllm#38284), eats
# ~0.0055 mem-util ≈ 120 MiB KV pool. Disabling restores ~4K of ctx.
- VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0
- VLLM_NO_USAGE_STATS=1
- PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,max_split_size_mb:512
- VLLM_FLOAT32_MATMUL_PRECISION=high
- VLLM_USE_FLASHINFER_SAMPLER=1
- OMP_NUM_THREADS=1
- CUDA_DEVICE_MAX_CONNECTIONS=8
- VLLM_ALLOW_LONG_MAX_MODEL_LEN=1
- VLLM_MARLIN_USE_ATOMIC_ADD=1
- TRITON_CACHE_DIR=/root/.triton/cache
# FULL Genesis v7.65 PROD env-var set per Sandermage's
# bare_metal_27b_int4_TQ_k8v4.sh. Validated 2026-05-01 PM dual-3090:
# +50% TPS over the prior subset (code 77 → 116 wall_TPS).
- GENESIS_ENABLE_P4=1
- GENESIS_ENABLE_P58_ASYNC_PLACEHOLDER_FIX=1
- GENESIS_ENABLE_P60_GDN_NGRAM_FIX=1
- GENESIS_ENABLE_P60B_TRITON_KERNEL=1
- GENESIS_ENABLE_P61_QWEN3_MULTI_TOOL=1
- GENESIS_ENABLE_P61B_STREAMING_OVERLAP=1
- GENESIS_ENABLE_P62_STRUCT_OUT_SPEC_TIMING=1
- GENESIS_ENABLE_P64_QWEN3CODER_MTP_STREAMING=1
- GENESIS_ENABLE_P65_TURBOQUANT_SPEC_CG_DOWNGRADE=1
- GENESIS_ENABLE_P66_CUDAGRAPH_SIZE_FILTER=1
- GENESIS_ENABLE_P67_TQ_MULTI_QUERY_KERNEL=1
# P68/P69 — v7.65 raised default threshold 8000 → 50000 chars (~12.5K
# tokens). Now safe with typical IDE-agent system+tool prompts; below
# the threshold both gates are no-op. Override via the env var if needed.
- GENESIS_ENABLE_P68_AUTO_FORCE_TOOL=1
- GENESIS_ENABLE_P69_LONG_CTX_TOOL_REMINDER=1
- GENESIS_P68_P69_LONG_CTX_THRESHOLD_CHARS=50000
- GENESIS_ENABLE_P72_PROFILE_RUN_CAP=1
- GENESIS_PROFILE_RUN_CAP_M=4128
- GENESIS_ENABLE_P74_CHUNK_CLAMP=1
- GENESIS_ENABLE_P83=1
- GENESIS_ENABLE_P85=1
- GENESIS_ENABLE_P87=1
- GENESIS_ENABLE_P91=1
- GENESIS_ENABLE_P94=1
- GENESIS_ENABLE_P98=1
- GENESIS_ENABLE_P99=1
- GENESIS_ENABLE_P100=1
- GENESIS_ENABLE_P101=1
# ─── Cliff 2 closure recipe (v7.69, single-24GB-GPU TP=1) ──────────
# P103 closes the FLA inner h tensor via chunk.py self-install hook
# (v7.69 F2 fix — survives `exec vllm serve` worker spawn).
# PN32 v2 closes the outer FLA call via _forward_core direct patch
# + initial_state threading (v7.69 F3 fix — composes with P103).
# Both required for Cliff 2 ceiling > 50K on TP=1.
- GENESIS_ENABLE_P103=1
- GENESIS_ENABLE_PN32_GDN_CHUNKED_PREFILL=1
- GENESIS_PN32_GDN_CHUNK_SIZE=8192
- GENESIS_PN32_GDN_CHUNK_THRESHOLD=16384
- GENESIS_FLA_FWD_H_MAX_T=16384
# PN34 (workspace_lock relaxation, opt-in default OFF). Replaces our
# patch_workspace_lock_disable.py sidecar.
- GENESIS_ENABLE_PN34_WORKSPACE_LOCK_RELAX=1
- GENESIS_ENABLE_PN8_MTP_DRAFT_ONLINE_QUANT=1
- GENESIS_ENABLE_PN9_INDEPENDENT_DRAFTER_ATTN=1
- GENESIS_ENABLE_PN11_GDN_AB_CONTIGUOUS=1
- GENESIS_ENABLE_PN12_FFN_INTERMEDIATE_POOL=1
- GENESIS_ENABLE_PN13_CUDA_GRAPH_LAMBDA_ARITY=1
- GENESIS_ENABLE_PN14_TQ_DECODE_OOB_CLAMP=1
- GENESIS_ENABLE_PN17_FA2_LSE_CLAMP=1
- GENESIS_ENABLE_PN25_SILU_INDUCTOR_SAFE=1
# PN30 — RE-ENABLED with our local dst-shaped temp fix
# (`patch_pn30_dst_shaped_temp_fix.py`, applied during setup.sh). The
# original Sander a9977d8 .contiguous() approach corrupted DS row strides
# by raw-memcpying a compact 10240×5 buffer into a 10240×6 destination
# block (row 1+ landed at the wrong destination offset, causing later
# TQ store CUDA assert). Our fix builds a destination-shaped temp inside
# `collect_mamba_copy_meta` and reuses PN30's temp-list lifetime handling.
# Diagnosis credit: ChatGPT/Codex CLI cross-check 2026-05-01 PM.
- GENESIS_ENABLE_PN30_DS_LAYOUT_SPEC_DECODE=1
# PN31 — DISABLED on 1×3090. Per-shape persistent VRAM grows as new
# shapes are seen during prefill; on TP=1 + 24GB, the residence cost
# outpaces the malloc-pressure relief. Sander explicitly warned in
# `753344b` he couldn't validate on 24GB; cross-rig validated 2026-05-01
# PM here that 0.95→0.93 mem-util can't carry both PN31 + DeltaNet
# `chunk_fwd_o` activation peak at 29K depth. Reported back on
# Sandermage/genesis-vllm-patches#15. Use tools-text.yml fp8 path for
# 25K+ tool-RETURN workloads in the meantime.
# - GENESIS_ENABLE_PN31_FA_VARLEN_PERSISTENT_OUT=1
- GENESIS_ENABLE_PN19_SCOPED_MAX_SPLIT=1
- GENESIS_ENABLE_PN22_LOCAL_ARGMAX_TP=1
# PN26b sparse-V Triton kernel — first SM86 (Ampere consumer) sparse-V
# kernel in any public tree. 27B-specific tuning per Sandermage's PROD.
- GENESIS_ENABLE_PN26_SPARSE_V=1
- GENESIS_PN26_SPARSE_V_BLOCK_KV=8
- GENESIS_PN26_SPARSE_V_NUM_WARPS=4
- GENESIS_PN26_SPARSE_V_THRESHOLD=0.01
# v7.65 #14 + #15 fixes (compile-safe in-source hook + FA varlen clamp).
# Replace our local patch_pn12_compile_safe_custom_op.py + P104 sidecars.
- GENESIS_ENABLE_P38B_COMPILE_SAFE=1
- GENESIS_ENABLE_P15B_FA_VARLEN_CLAMP=1
- GENESIS_PREALLOC_TOKEN_BUDGET=4128
- GENESIS_BUFFER_MODE=shared
# Explicit OFFs to match Sandermage's PROD env-var set verbatim:
# P78 (P78_TOLIST_CAPTURE_GUARD) — superseded by our patch_tolist_cudagraph.py
# P81 (FP8 block-scaled M<=8) — FP8-specific, no-op on our TQ3 path
# P82 — biased on small-batch single-stream Lorbus INT4 + MTP K=3 (Sander PROD)
- GENESIS_ENABLE_P78_TOLIST_CAPTURE_GUARD=0
- GENESIS_ENABLE_P81_FP8_BLOCK_SCALED_M_LE_8=0
- GENESIS_ENABLE_P82=0
- GENESIS_P82_THRESHOLD_SINGLE=0.3
# P82 stays OFF — biased on small-batch single-stream Lorbus INT4 + MTP K=3
# per Sandermage's PROD memory feedback_p82_*. P78 stays OFF (deprecated).
# vLLM env knobs from Sandermage's launch:
# VLLM_SSM_CONV_STATE_LAYOUT=DS — RE-ENABLED with our local PN30 fix
# (`patch_pn30_dst_shaped_temp_fix.py`, applied during setup.sh).
# Without our fix, Sander's PN30 a9977d8 corrupts DS row strides on
# spec-decode AL>1 paths. With our fix, PN30 builds a destination-shaped
# temp instead of a compact one, preserving DS layout. +6% TPS retained.
- VLLM_SSM_CONV_STATE_LAYOUT=DS
- VLLM_USE_FUSED_MOE_GROUPED_TOPK=1
shm_size: "16gb"
ipc: host
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
entrypoint:
- /bin/bash
- -c
- |
set -e
pip install xxhash pandas scipy -q
# Pre-flight: Genesis patches must be populated. Empty volume mount
# = silent no-op apply_all = boot fails later with cryptic upstream
# error (e.g. "TurboQuant KV not supported for hybrid models", #13).
if [ ! -f /usr/local/lib/python3.12/dist-packages/vllm/_genesis/patches/apply_all.py ]; then
echo "ERROR: Genesis patches missing — host volume models/qwen3.6-27b/vllm/patches/genesis/ is empty." >&2
echo " Run from repo root before 'docker compose up':" >&2
echo " bash scripts/setup.sh qwen3.6-27b" >&2
exit 1
fi
python3 -m vllm._genesis.patches.apply_all
# patch_tolist_cudagraph.py — still needed pending upstream
# vllm#40807 .tolist() capture-stream guard fix.
python3 /patches/patch_tolist_cudagraph.py
# patch_inputs_embeds_optional.py — backport of vllm#35975
# (free ~64 MiB GPU + ~64 MiB pinned CPU on text-only models).
# Drops when PR merges upstream.
python3 /patches/patch_inputs_embeds_optional.py
exec vllm serve "$@"
- --
command:
- --model
- /root/.cache/huggingface/qwen3.6-27b-autoround-int4
- --served-model-name
- qwen3.6-27b-autoround
- --quantization
- auto_round
- --dtype
- float16
- --tensor-parallel-size
- "1"
# 180K + 0.97 — backed off from 214K + 0.985 on 2026-05-01 PM after
# verify-stress probe 1 (10K-token long-context needle) crashed with
# GDN forward OOM (`fla.ops.chunk.chunk_gated_delta_rule_fwd_h`
# `k.new_empty(B, NT, H, V, K)` requested 98 MiB, 46 MiB free). At
# 0.985 mem-util, activation budget was ~46 MiB at 10K prefill —
# tight enough that ANY single prompt over a few thousand tokens was
# a coin flip. The "Cliff 2 fires only at 50-60K" framing was
# config-specific to the dev205 + 0.92 default; at tighter mem-util
# the GDN forward buffer outpaces available activation much earlier.
#
# New target (validated 2026-05-01 PM via verify-stress.sh):
# 180K + 0.97 → ~6.4 GB after weights, ~5.9 GB KV pool, ~500 MiB
# activation safety. Probe 1 (10K needle) and probe 6 (math reasoning
# at max_tokens=8192) PASS. Safe single-prompt envelope ~30K; full
# 180K context for steady-state turn accumulation only.
#
# IDE-agent workloads (sys + tool schemas) STILL crash here per
# club-3090#16 — different bug (Cliff 1 mech B inductor leak), not
# fixed by mem-util tuning. Use tools-text.yml for those.
- --max-model-len
- "200000"
# 0.95 — backed off from 0.97 on 2026-05-01 PM after PN25 v3 closed
# Cliff 1 mech B at the FFN intermediate buffer (which now sits in
# PN12+PN25's resident FFNIntermediateCache pool). Pool residence
# adds ~140 MiB persistent VRAM, which tightened activation budget
# at DeltaNet `chunk_fwd_o` (`o = torch.empty_like(v)`, 50 MiB) —
# 0.97 left only 26 MiB free, OOM at 9.8K probe. 0.95 frees ~480 MiB
# activation budget, validated up to 30K probes.
- --gpu-memory-utilization
- "0.95"
- --max-num-seqs
- "1"
- --max-num-batched-tokens
- "4128"
- --kv-cache-dtype
- turboquant_3bit_nc
# Drops the vision tower → frees ~1 GB VRAM → lifts engine ceiling from
# 198K (long-vision at 0.98) to 218K (this variant at 0.985). This is the only difference vs long-vision.
- --language-model-only
- --trust-remote-code
- --reasoning-parser
- qwen3
- --enable-auto-tool-choice
- --tool-call-parser
- qwen3_coder
- --enable-prefix-caching
- --enable-chunked-prefill
- --no-scheduler-reserve-full-isl
# MTP K=3 spec-decode INTENTIONALLY DISABLED on this variant. Frees
# ~280 MiB of draft-model residency on top of #35975's ~444 MiB savings,
# unlocking max_model_len=200K admission (vs 180K cap with MTP-on).
# Trade-off: lose ~3-4x decode TPS speedup vs `long-text.yml` (MTP-on).
# Pick this variant for long single-shot RAG / codebase prompts where
# TTFT-bounded throughput matters more than per-token decode speed.
# - --speculative-config
# - '{"method":"mtp","num_speculative_tokens":3}'
- --host
- 0.0.0.0
- --port
- "8000"

View File

@@ -21,25 +21,46 @@
# - Any IDE coding agent # - Any IDE coding agent
# - Any flow that puts tool/function schemas in the system prompt # - Any flow that puts tool/function schemas in the system prompt
# #
# Stack: vLLM v0.20.1rc1.dev16 + Genesis v7.65 dev (commit d89a089) + # Stack: vLLM v0.20.1rc1.dev16 + Genesis v7.69 dev (commit 2db18df) +
# TurboQuant 3-bit KV + MTP n=3 + vision tower disabled. Migrated from # TurboQuant 3-bit KV + MTP n=3 + vision tower disabled.
# vLLM dev205 + Genesis v7.64 on 2026-05-01 PM. v0.20's revised TQ FA paths
# close the synthetic 50K-token tool-prefill stress (which fired on dev205);
# real IDE-agent shapes still hit the inductor compile-path leak above.
# #
# v7.65 key patches active: # v7.69 key patches active:
# - P38B (Genesis #14 fix): compile-safe in-source hook for # - P103 (Cliff 2 chunked fwd_h+fwd_o orchestrator) — chunk.py self-install
# _continuation_prefill — replaces our local patch_pn12_compile_safe_ # hook survives `exec vllm serve` worker spawn (v7.69 F2 fix). Also
# custom_op.py # applied: Codex r1 cu_seqlens=[0,T] dense-single-seq gate fix that
# - P15B (Genesis #15 fix): FA varlen max_seqlen_k clamp at TQ wrapper # allows the chunked path to engage on real serving.
# boundary — replaces our local patch_fa_max_seqlen_clamp.py # - PN30 v7.68 part1+2+3 — DS conv state layout + spec-decode AL>1 fix
# - PN25: Inductor-safe silu_and_mul opaque op (covers the same surface # (drift-marker bug fixed in v7.69)
# as our former patch_pn12_compile_safe_custom_op.py) # - PN32 v2 — GDN _forward_core chunked-prefill (Cliff 2 outer split)
# - PN34 — workspace_lock relaxation (replaces our former
# patch_workspace_lock_disable.py sidecar)
# - PN26b: Genesis-original sparse-V Triton kernel for SM86 (Ampere # - PN26b: Genesis-original sparse-V Triton kernel for SM86 (Ampere
# consumer) — first SM86 sparse-V kernel in any public tree. 27B # consumer) — first SM86 sparse-V kernel in any public tree. 27B
# tuning: BLOCK_KV=8 num_warps=4 threshold=0.01. # tuning: BLOCK_KV=8 num_warps=4 threshold=0.01.
# - Cliff 8 hardening: partial_apply_warnings counter in boot summary # - P38B / P15B: Cliff 1 mech B compile-safe hook + FA varlen clamp
# surfaces silent anchor-drift skips #
# Cliff 2 closure recipe (cross-rig validated 2026-05-02 PM):
# - vllm#35975 backport sidecar (`patch_inputs_embeds_optional.py`) frees
# ~444 MiB at boot on text-only models (skips inputs_embeds buffers
# for both gpu_model_runner.py + llm_base_proposer.py).
# - --gpu-memory-utilization 0.93 — middle ground between 0.92 (cliff
# just barely closed) and 0.95 (cliff fires). Recovers ~30K of KV
# pool concurrency vs 0.92 while keeping ~494 MiB margin at peak.
# - --max-model-len 180000 — engine pre-check admission ceiling on
# this config (200K rejected: KV pool needs 3.31 GiB, only 2.19 GiB
# available at 0.93). Cliff 2 single-prompt safe envelope: ~60K
# verified pass; ~70-80K is the OOM ceiling.
#
# Validation (2026-05-02 PM, branch v7.69-cliff2-test):
# - 60K MTP-on probe: HTTP 200 in 623s, recall correct, MTP AL=4.00
# - 90K MTP-on probe: HTTP 500 OOM at 112s (50 MiB / 24.5 MiB free)
# - Probes 2-6 (tool prefill, IDE-agent, multi-turn, LCB-coding,
# reasoning-heavy): all PASS
#
# For workloads that need >60K single-prompt or 200K declared context,
# use docker-compose.long-text-no-mtp.yml (max-context safety variant
# trades MTP decode speedup for prefill stability). For coding agents
# on dual-card, use docker-compose.dual-turbo.yml.
# #
# Recommended client max_tokens: 8192 (FREE thinking on this compose; budget # Recommended client max_tokens: 8192 (FREE thinking on this compose; budget
# for hard reasoning runs). 16384 for competition-grade problems. See # for hard reasoning runs). 16384 for competition-grade problems. See
@@ -93,10 +114,12 @@ services:
- ../cache/triton:/root/.triton/cache - ../cache/triton:/root/.triton/cache
- ../patches/genesis/vllm/_genesis:/usr/local/lib/python3.12/dist-packages/vllm/_genesis:ro - ../patches/genesis/vllm/_genesis:/usr/local/lib/python3.12/dist-packages/vllm/_genesis:ro
- ../patches/patch_tolist_cudagraph.py:/patches/patch_tolist_cudagraph.py:ro - ../patches/patch_tolist_cudagraph.py:/patches/patch_tolist_cudagraph.py:ro
# workspace_lock_disable — relaxes vllm#39226 strict assertion for the TQ # workspace_lock_disable DROPPED on v7.69 — Genesis PN34 covers the
# decode path on v0.20. Drop when Sandermage ships the P98 marker fix # boot-time AND runtime _decode_attention paths. Opt-in via
# that auto-enables on v0.20 without our help. See docs/CLIFFS.md. # GENESIS_ENABLE_PN34_WORKSPACE_LOCK_RELAX=1 in environment block.
- ../patches/patch_workspace_lock_disable.py:/patches/patch_workspace_lock_disable.py:ro # patch_inputs_embeds_optional.py — backport of vllm#35975 (~64 MiB GPU +
# 64 MiB pinned CPU savings on text-only models). Drops when PR merges.
- ../patches/patch_inputs_embeds_optional.py:/patches/patch_inputs_embeds_optional.py:ro
# NOTE: patch_pn12_ffn_pool_anchor.py + patch_pn12_compile_safe_custom_op.py # NOTE: patch_pn12_ffn_pool_anchor.py + patch_pn12_compile_safe_custom_op.py
# + patch_fa_max_seqlen_clamp.py all DROPPED — Genesis v7.65 covers their # + patch_fa_max_seqlen_clamp.py all DROPPED — Genesis v7.65 covers their
# surface natively (PN12 native + PN25 + PN17 + P15B). See migration # surface natively (PN12 native + PN25 + PN17 + P15B). See migration
@@ -152,7 +175,20 @@ services:
- GENESIS_ENABLE_P99=1 - GENESIS_ENABLE_P99=1
- GENESIS_ENABLE_P100=1 - GENESIS_ENABLE_P100=1
- GENESIS_ENABLE_P101=1 - GENESIS_ENABLE_P101=1
# ─── Cliff 2 closure recipe (v7.69, single-24GB-GPU TP=1) ──────────
# P103 closes the FLA inner h tensor via chunk.py self-install hook
# (v7.69 F2 fix — survives `exec vllm serve` worker spawn).
# PN32 v2 closes the outer FLA call via _forward_core direct patch
# + initial_state threading (v7.69 F3 fix — composes with P103).
# Both required for Cliff 2 ceiling > 50K on TP=1.
- GENESIS_ENABLE_P103=1 - GENESIS_ENABLE_P103=1
- GENESIS_ENABLE_PN32_GDN_CHUNKED_PREFILL=1
- GENESIS_PN32_GDN_CHUNK_SIZE=8192
- GENESIS_PN32_GDN_CHUNK_THRESHOLD=16384
- GENESIS_FLA_FWD_H_MAX_T=16384
# PN34 (workspace_lock relaxation, opt-in default OFF). Replaces our
# patch_workspace_lock_disable.py sidecar.
- GENESIS_ENABLE_PN34_WORKSPACE_LOCK_RELAX=1
- GENESIS_ENABLE_PN8_MTP_DRAFT_ONLINE_QUANT=1 - GENESIS_ENABLE_PN8_MTP_DRAFT_ONLINE_QUANT=1
- GENESIS_ENABLE_PN9_INDEPENDENT_DRAFTER_ATTN=1 - GENESIS_ENABLE_PN9_INDEPENDENT_DRAFTER_ATTN=1
- GENESIS_ENABLE_PN11_GDN_AB_CONTIGUOUS=1 - GENESIS_ENABLE_PN11_GDN_AB_CONTIGUOUS=1
@@ -236,10 +272,13 @@ services:
exit 1 exit 1
fi fi
python3 -m vllm._genesis.patches.apply_all python3 -m vllm._genesis.patches.apply_all
# workspace_lock_disable — relaxes vllm#39226 strict assertion until # patch_tolist_cudagraph.py — still needed pending upstream
# Sandermage's P98 marker fix lands. # vllm#40807 .tolist() capture-stream guard fix.
python3 /patches/patch_workspace_lock_disable.py
python3 /patches/patch_tolist_cudagraph.py python3 /patches/patch_tolist_cudagraph.py
# patch_inputs_embeds_optional.py — backport of vllm#35975
# (free ~64 MiB GPU + ~64 MiB pinned CPU on text-only models).
# Drops when PR merges upstream.
python3 /patches/patch_inputs_embeds_optional.py
exec vllm serve "$@" exec vllm serve "$@"
- -- - --
command: command:
@@ -282,7 +321,7 @@ services:
# 0.97 left only 26 MiB free, OOM at 9.8K probe. 0.95 frees ~480 MiB # 0.97 left only 26 MiB free, OOM at 9.8K probe. 0.95 frees ~480 MiB
# activation budget, validated up to 30K probes. # activation budget, validated up to 30K probes.
- --gpu-memory-utilization - --gpu-memory-utilization
- "0.95" - "0.93"
- --max-num-seqs - --max-num-seqs
- "1" - "1"
- --max-num-batched-tokens - --max-num-batched-tokens

View File

@@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""
patch_inputs_embeds_optional.py — backport of vllm-project/vllm#35975
Skips inputs_embeds GPU buffer allocation for text-only models. Saves
~64 MiB GPU + ~64 MiB pinned CPU on a config that doesn't use multimodal
inputs or prompt embeddings.
Why we ship it locally:
PR #35975 is open upstream as of 2026-05-02 (awaiting code-owner approval
after author addressed reviewer feedback). On club-3090's TP=1 + 24GB
Qwen3.6-27B + MTP K=3 + 0.95 mem-util config, Cliff 2 at 60K fires with
~24.5 MiB free at the time of the failing 50 MiB allocation. PR #35975
frees ~64 MiB GPU + ~64 MiB pinned CPU on text-only models — sufficient
margin to potentially close Cliff 2 at 60K MTP-on without reducing
context or mem-util.
This sidecar exists because we don't want to fork the docker image just
for one PR. When PR #35975 merges upstream and ships in our nightly tag,
this sidecar can be deleted.
References:
- https://github.com/vllm-project/vllm/pull/35975
- club-3090 results/v0.20-migration/v769-codex-r1-test.summary
Idempotent. Safe to re-run.
"""
import logging
import re
import sys
from pathlib import Path
log = logging.getLogger("inputs_embeds_optional")
logging.basicConfig(level=logging.INFO, format="[%(name)s] %(message)s")
PATCH_TAG = "[inputs_embeds_optional]"
def patch_gpu_model_runner() -> bool:
"""
File: vllm/v1/worker/gpu_model_runner.py
Before:
self.inputs_embeds = self._make_buffer(
self.max_num_tokens, self.inputs_embeds_size, dtype=self.dtype, numpy=False
)
After:
# vllm#35975: skip inputs_embeds buffer for text-only models.
self.inputs_embeds = None
if self.supports_mm_inputs or self.enable_prompt_embeds:
self.inputs_embeds = self._make_buffer(
self.max_num_tokens, self.inputs_embeds_size, dtype=self.dtype, numpy=False
)
"""
target = Path(
"/usr/local/lib/python3.12/dist-packages/vllm/v1/worker/gpu_model_runner.py"
)
if not target.exists():
log.error(f"{PATCH_TAG} target not found: {target}")
return False
src = target.read_text()
if PATCH_TAG in src:
log.info(f"{PATCH_TAG} gpu_model_runner.py already patched, skipping.")
return True
# Find the exact allocation site. Use a regex that matches multi-line.
# Keep the `numpy=False` argument literal.
pattern = re.compile(
r"^( *)self\.inputs_embeds = self\._make_buffer\(\n"
r"\1 self\.max_num_tokens, self\.inputs_embeds_size, dtype=self\.dtype, numpy=False\n"
r"\1\)$",
re.MULTILINE,
)
match = pattern.search(src)
if not match:
log.error(
f"{PATCH_TAG} gpu_model_runner.py: anchor not found "
f"(self.inputs_embeds = self._make_buffer block). vLLM may have "
f"changed; review PR #35975 manually."
)
return False
indent = match.group(1)
replacement = (
f"{indent}# {PATCH_TAG} vllm#35975: skip inputs_embeds buffer for text-only models.\n"
f"{indent}self.inputs_embeds = None\n"
f"{indent}if self.supports_mm_inputs or self.enable_prompt_embeds:\n"
f"{indent} self.inputs_embeds = self._make_buffer(\n"
f"{indent} self.max_num_tokens, self.inputs_embeds_size, dtype=self.dtype, numpy=False\n"
f"{indent} )"
)
new_src = pattern.sub(replacement, src, count=1)
target.write_text(new_src)
log.info(f"{PATCH_TAG} gpu_model_runner.py: applied")
return True
def patch_llm_base_proposer() -> bool:
"""
File: vllm/v1/spec_decode/llm_base_proposer.py
(PR #35975 originally targeted eagle.py; in this nightly the code lives
in the shared base class llm_base_proposer.py.)
Before:
self.inputs_embeds = torch.zeros(
(self.max_num_tokens, self.inputs_embeds_size),
dtype=self.dtype,
device=device,
)
After:
# vllm#35975: skip inputs_embeds tensor for text-only proposers.
self.inputs_embeds = None
if self.supports_mm_inputs:
self.inputs_embeds = torch.zeros(
(self.max_num_tokens, self.inputs_embeds_size),
dtype=self.dtype,
device=device,
)
"""
target = Path(
"/usr/local/lib/python3.12/dist-packages/vllm/v1/spec_decode/llm_base_proposer.py"
)
if not target.exists():
log.error(f"{PATCH_TAG} target not found: {target}")
return False
src = target.read_text()
if PATCH_TAG in src:
log.info(f"{PATCH_TAG} llm_base_proposer.py already patched, skipping.")
return True
pattern = re.compile(
r"^( *)self\.inputs_embeds = torch\.zeros\(\n"
r"\1 \(self\.max_num_tokens, self\.inputs_embeds_size\),\n"
r"\1 dtype=self\.dtype,\n"
r"\1 device=device,\n"
r"\1\)$",
re.MULTILINE,
)
match = pattern.search(src)
if not match:
log.error(
f"{PATCH_TAG} llm_base_proposer.py: anchor not found "
f"(self.inputs_embeds = torch.zeros block). vLLM may have changed."
)
return False
indent = match.group(1)
replacement = (
f"{indent}# {PATCH_TAG} vllm#35975: skip inputs_embeds tensor for text-only proposers.\n"
f"{indent}self.inputs_embeds = None\n"
f"{indent}if self.supports_mm_inputs:\n"
f"{indent} self.inputs_embeds = torch.zeros(\n"
f"{indent} (self.max_num_tokens, self.inputs_embeds_size),\n"
f"{indent} dtype=self.dtype,\n"
f"{indent} device=device,\n"
f"{indent} )"
)
new_src = pattern.sub(replacement, src, count=1)
target.write_text(new_src)
log.info(f"{PATCH_TAG} llm_base_proposer.py: applied")
return True
def main() -> int:
ok = True
ok &= patch_gpu_model_runner()
ok &= patch_llm_base_proposer()
if ok:
log.info(f"{PATCH_TAG} all patches applied successfully")
return 0
log.error(f"{PATCH_TAG} one or more patches failed")
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,404 @@
# v7.69 + Codex round 1 P103 gate fix — verification on TP=1 + 24GB
Date: 2026-05-02 PM
Genesis pin: 2db18df (v7.69 dev tip) + Codex's gate-fix patch applied to in-tree
P103: patched to treat cu_seqlens=[0,T] as dense single-seq (not multi-seq varlen)
PN32 v2: enabled
PN34: enabled (workspace_lock relax)
DS layout: enabled
GENESIS_FLA_FWD_H_MAX_T: tested at both 16384 (default) and 2048
## Ground truth from diagnostic log
Added `print` at the gate to capture T, q.shape, cu_shape, _single_seq_cu, _true_varlen_multi_seq.
### Run 1: GENESIS_FLA_FWD_H_MAX_T=16384 (Codex default)
T distribution across 442 invocations on 60K probe:
T=4128: 394 invocations (vLLM chunked-prefill chunk size)
T=64: 48 invocations (cudagraph warmup or MTP verify)
T>4128: 0 invocations
q.shape[0] = 1 always (B=1, single sequence)
cu_shape = torch.Size([2]) always (single segment, [0, T])
_single_seq_cu = True for every invocation
_true_varlen_multi_seq = False for every invocation
Gate verdict: gate-fix is semantically correct but `q.shape[1] <= _MAX_T`
(4128 <= 16384) bypasses to original_fwd on EVERY call. Chunked path
never engages.
OOM: same trace as v7.69 baseline. 50.00 MiB alloc fails, 24.50 MiB free.
### Run 2: GENESIS_FLA_FWD_H_MAX_T=2048 (force chunking on T=4128 calls)
T distribution across 436 invocations:
T=4128: 340 invocations (now ENTERING chunked path: 4128 > 2048)
T=64: 48 invocations
T=11: 48 invocations (smaller residuals on chunked path)
Per-call allocation: 24.00 MiB (half of previous 50.00 MiB)
Cumulative active PyTorch: 23.01 GiB (vs 22.96 GiB on run 1)
OOM trigger: failed at call ~336/340 (vs ~393/394 on run 1)
Net change: chunking DID reduce per-call peak by half, but cumulative
state across chunked sub-calls grew slightly (chained state buffers
between chunks). OOM fires earlier in absolute call count.
## Cumulative diagnosis
The gate-fix Codex proposed is correct in principle:
cu_seqlens.shape == (2,) IS dense single-seq, not multi-seq varlen.
P103 v7.70 should NOT bypass on cu_seqlens != None when shape == (2,).
But on our TP=1 + 24GB + MTP K=3 + 0.95 mem-util config, the chunked
path:
(a) doesn't engage at MAX_T=16384 because vLLM's outer chunked
prefill already caps T at max_num_batched_tokens=4128
(b) does engage at MAX_T=2048 but doesn't help because the OOM
isn't a single-allocation-too-large problem — it's
cumulative-state-too-large
Current allocation breakdown at OOM:
Total in use: 23.55 GiB / 23.56 GiB (99.9%)
PyTorch active: 23.01 GiB
Of which:
Model weights: 16.87 GiB (boot log)
MTP draft sharing: 0 GiB (shared via embedding+lm_head)
KV pool @ 60K TQ3: ~1 GiB (rough estimate, ~17 KB/tok)
Mamba conv state: ~0.5 GiB (DS layout + GDN forward)
PN12/PN25 pools: ~0.2 GiB
FLA temp buffers: ~0.2 GiB
cudagraph private: 0.024 GiB
Other PyTorch alloc: ~4 GiB (UNACCOUNTED — likely activations)
The 4 GiB "unaccounted" is the actual headroom problem. Each prefill
chunk's activations stick around in some lifetime overlap until the
next chunk's allocation, fragmenting expandable-segments effective
headroom.
## Conclusions
1. Codex's gate-fix should still ship to Sander as v7.70 — it's
semantically correct and unblocks the synthetic single-seq path.
2. But it does NOT close real-config Cliff 2 at 60K on 24GB.
3. The actual fix is one of:
(a) Lower mem-util to ~0.90 (frees ~700 MiB headroom, costs ~10K ctx)
(b) Cap max-model-len to 50K and route 50K+ to TP=2 (`dual.yml`)
(c) Genesis-side: tighter activation residency in PN12/PN25/PN30
pools + audit FLA temp buffer reuse
(d) vLLM-side: scheduler pause prefill chunks when VRAM headroom
drops below threshold
Practical recommendation for users on TP=1 + 24GB:
Cliff 2 ceiling stays ~50-60K on this config regardless of v7.69
patches. Treat it as architectural + use dual.yml for longer prompts.
## Round 2 prompt for Codex queued at /tmp/codex_prompt_cliff2_round2.md
Asks ChatGPT/Codex to:
- Diagnose where the 4 GiB unaccounted active PyTorch allocation lives
- Recommend instrumentation to bisect (memory_summary per call?)
- Propose either Genesis-side or vLLM-side fix that doesn't require
per-call chunking (since chunking doesn't help here)
## State of in-tree files
- `models/qwen3.6-27b/vllm/patches/genesis/vllm/_genesis/wiring/hybrid/patch_103_fla_cliff2_chunked.py`
has Codex's gate-fix applied + temp diagnostic log. Will revert
diagnostic log before any commit.
- `models/qwen3.6-27b/vllm/compose/docker-compose.long-text.yml`
has GENESIS_FLA_FWD_H_MAX_T=2048 (testing value). Needs revert to
16384 (or whatever final value) before commit.
## ROUND 2 — Codex r2 + user PR research bisect (2026-05-02 PM)
### Boot resident snapshot
MTP-on : 23,156 MiB (model weights + MTP draft 0.28 GiB shared + KV preallocation + Genesis pools)
MTP-off : 23,164 MiB (same minus MTP, ~280 MiB savings — MTP draft shares embedding+lm_head)
Headroom at 0.95 mem-util ≈ 1,400 MiB before any request.
### MTP-off A/B at 60K — single-variable change
Removed only `--speculative-config '{"method":"mtp",...}'`. All else
unchanged: P103 (with Codex gate fix) + PN30 + PN32 v2 + PN34 + DS layout
+ MAX_T=16384 + max_num_batched_tokens=4128 + max-model-len=180000 +
mem-util=0.95.
Manual curl with 600s timeout:
HTTP 200 ✅ recalled "violet otter 48" correctly
Wall: 504s
Engine logs: prompt throughput 227 tok/s, gen 7.5 tok/s, KV usage 6.1%
No OOM, no errors
verify-stress harness curl (5 min default timeout):
HTTP 000 (harness timed out before engine completed)
Engine itself returned 200 a few seconds after harness gave up — both
the harness curl AND the manual curl saw the same engine completion;
the harness's 5-min cap is the fail signal, not engine correctness.
**Conclusion: MTP-off closes the 60K Cliff 2 cleanly.** Cliff 2 on
TP1 + 24GB + 0.95 mem-util is dominated by MTP draft residency +
activation footprint, not P103 cu_seqlens gate.
### MTP-off at 90K — indeterminate within 15 min
Manual curl with 900s (15 min) timeout:
HTTP 000 (curl timed out)
Wall 900s
No OOM trace
GPU 0 still at 100% utilization
No POST 200 in engine logs after 13:42:37
Engine never completed within practical wait
Practical interpretation: 90K MTP-off is past the wall-clock-feasible
ceiling on this config even without OOM. Throughput drops as
cumulative state grows; what would take ~7 min at constant 227 tok/s
takes 25+ min in practice (and may eventually OOM internally without
reaching us). For users on TP1+24GB, **MTP-off shifts the practical
ceiling from ~30-60K to ~60K**, but doesn't push to 90K.
### Updated upstream PR research (provided by user)
Three open PRs that could compose with our findings:
| PR | What | Why it matters |
|---|---|---|
| [vllm#35975](https://github.com/vllm-project/vllm/pull/35975) | Skip inputs_embeds GPU buffer for text-only models | ~64 MiB GPU + 64 MiB pinned CPU savings. Direct match for our 50 MiB margin failure. **Highest-value test next.** |
| [vllm#37521](https://github.com/vllm-project/vllm/pull/37521) | Spec-decode warmup memory accounting | Profiling/KV sizing leaves less false headroom. Worth testing for MTP-on path. |
| [vllm#37429](https://github.com/vllm-project/vllm/pull/37429) | KV cache sizing for hybrid Mamba/attention | Architectural — could free substantial headroom on hybrid Qwen3-Next models. Bigger/riskier. |
vLLM's official Qwen3-Next recipe documents `--tensor-parallel-size 4
--gpu-memory-utilization 0.8 --no-enable-chunked-prefill` as happy
path. Our 24GB TP1 long-context MTP case is OUTSIDE the documented
configuration — context worth flagging to Sander.
### Next: apply PR #35975 as text-patch + retest 60K MTP-on
Plan: modify `vllm/v1/worker/gpu_model_runner.py:623-633` and
`vllm/v1/spec_decode/eagle.py:192-201` to skip `inputs_embeds` buffer
allocation when not needed (no MM inputs, no prompt_embeds). 64 MiB
saved on text-only model + 64 MiB pinned CPU. If that turns 60K
MTP-on from -25 MiB to +39 MiB free, we have a real closure proof.
## ROUND 3 — vllm#35975 backport + mem-util tuning matrix (2026-05-02 PM)
### Closure recipe: PR#35975 + mem-util 0.92 + MTP-on
**60K probe RESULT: HTTP 200 OK, recall correct, MTP AL=4.00**
| Config | Boot resident | OOM/Pass at 60K | Wall | Notes |
|---|---|---|---|---|
| MTP-off, 0.95 mem-util | 23,164 MiB | ✅ Pass | 504s | Already established (Round 2) |
| MTP-on + #35975 + 0.95 mem-util | 22,720 MiB | ❌ Fails 46.5 MiB free / 50 needed | n/a | #35975 freed ~444 MiB at boot, but only ~22 MiB extra at peak |
| **MTP-on + #35975 + 0.92 mem-util** | **21,980 MiB** | **✅ Pass** | **~11.5 min wall** | **Cliff 2 closed** with full MTP K=3 + correct recall |
Notes on the MTP-on + #35975 + 0.92 run:
- Boot resident dropped from 22,720 (at 0.95) to 21,980 (at 0.92) — saved 740 MiB
- KV pool: 3.87 GiB / 305,472 tokens (vs 5.07 GiB / 363,264 at 0.95) — 1.43x → 1.25x concurrency at 180K
- Wall ~11.5 min (vs 8.4 min on MTP-off + 0.95) — slower than expected; investigation TBD
- MTP per-position acceptance: 1.000, 1.000, 1.000 (perfect on this prompt)
### Codex r2 verdict (post-bisect)
> #35975 is definitely relevant, but not sufficient alone. MTP-off proving 60K pass means draft residency is the dominant pressure. #35975 + gpu_memory_utilization=0.92 is now the right "keep MTP" candidate. P103 streaming output is lower priority unless 0.92 still fails or you need to recover more context at fixed 0.95.
### Two shippable variants
**Variant A — Long-text with MTP** (`long-text.yml`):
- Genesis v7.69 + Codex P103 gate fix + PN30 + PN32 + PN34
- vllm#35975 backport sidecar (`patch_inputs_embeds_optional.py`)
- `--gpu-memory-utilization 0.92` (down from 0.95)
- `--speculative-config '{"method":"mtp","num_speculative_tokens":3}'` (retained)
- Cliff 2 closed at 60K. Probe 7 ceiling moves from 30-60K → 60K+.
**Variant B — Long-text without MTP** (`long-text-no-mtp.yml` — TODO):
- Same as A but `--speculative-config` REMOVED, `--gpu-memory-utilization 0.95` (recovered)
- Cliff 2 closed at 60K with full mem-util (KV pool 1.43x at 180K)
- Lower decode throughput (no MTP speedup)
- Trade-off: more KV/context vs slower decode
### Codex r2's recommended next bisect (open)
1. ~~#35975 + MTP-on + 0.92: establish pass~~ ✅ DONE (this run)
2. **#35975 + MTP-on + 0.93/0.935: find less conservative pass point** ← next
3. ~~MTP-off + 0.95: keep as alternate throughput/context-safe variant~~ ✅ already validated
4. Only consider P103 streaming output if MTP-on at 0.95 specifically required
User concurs with Codex's plan. Step 2 (midpoint 0.93/0.935) gives margin
recommendation for shippable.
### Next: 60K test at #35975 + MTP-on + 0.93 (then 0.935 if 0.93 fails)
## ROUND 4 — mem-util sweep at 60K (Codex r2 step 2-3)
### 60K results across mem-util at MTP-on + #35975
| mem-util | Boot resident | KV pool size | 60K MTP-on | Wall | Final GPU free | Notes |
|---|---|---|---|---|---|---|
| 0.95 | 22,720 MiB | 5.5 GiB / ~388K tok | ❌ OOM 50/46.5 | n/a | n/a | 22 MiB short of margin |
| 0.93 | 22,260 MiB | 4.4 GiB / ~340K tok | ✅ HTTP 200 | 623s | ~494 MiB | **Best balanced point.** AL=4.0, recall correct |
| 0.92 | 21,980 MiB | 3.87 GiB / ~305K tok | ✅ HTTP 200 | 689s | ~580 MiB | More margin but slower + less KV |
**Conclusion: 0.93 is the recommended balanced shipping point.** Recovers
~30K tokens of KV pool concurrency vs 0.92 while still passing 60K
Cliff 2 with real end-of-run margin (~494 MiB free). Wall improvement
likely from cudagraph friendlier allocator pattern at higher headroom.
### Three explicit shippable variants (Codex r2 framing, post-bisect)
**Balanced MTP** — `long-text.yml` updated:
- Genesis v7.69 + Codex P103 gate fix + PN30 + PN32 v2 + PN34
- vllm#35975 backport sidecar (`patch_inputs_embeds_optional.py`)
- mem-util 0.93 (down from 0.95)
- MTP K=3 retained
- Cliff 2 closed at 60K. KV concurrency at 180K: ~1.4x
**Max-context safety** — `long-text-no-mtp.yml` (TODO):
- Same patches except no `--speculative-config`
- mem-util 0.95 (full KV pool)
- For long single-shot RAG / codebase prompts where slow decode is OK
- Cliff 2 ceiling: 60K confirmed; 90K probe with max_tokens=1 pending
**Future upstream win**:
- vllm#37429 hybrid Mamba/attention KV cache sizing — could free more
residency without trading mem-util
- Worth testing on a separate branch when CI signals stabilize
### Remaining tests (in progress)
- 90K + max_tokens=1 at 0.93 — measures prefill ceiling without slow decode
- max_model_len bump (200K? 220K?) at 0.93 (Codex says do this AFTER mem-util pick — admission capacity test, not safety)
## ROUND 5 — 90K probe + admission capacity (final)
### 90K MTP-on + #35975 + 0.93, max_tokens=1 (prefill-only timing)
Result: **HTTP 500 OOM at 112s wall**
- Trace: chunk_fwd_o `o = torch.empty_like(v)` 50 MiB allocation, 24.5 MiB free
- Engine died (EngineDeadError)
Conclusion: **Cliff 2 ceiling at 0.93 + #35975 + MTP-on = somewhere
between 60K (passes) and 90K (fails)**. Likely 70-80K. For shipping
purposes, document 60K as the safe single-prompt envelope.
### Admission capacity test: max_model_len 200K at 0.93 + #35975
Result: Engine pre-check **REJECTED**
```
ValueError: To serve at least one request with the models's max seq len
(200000), (3.31 GiB KV cache is needed, which is larger than the
available KV cache memory (2.19 GiB). Based on the available memory,
the estimated maximum model length is 111456.
```
Per Codex r2 framing: this is *admission* capacity (would-it-fit), not
*safety*. The 200K declared at 0.93 + #35975 doesn't fit in available
KV pool.
**180K stays as the practical max_model_len ceiling at 0.93 + #35975 +
MTP-on.** Pushing higher requires either:
- Higher mem-util (back toward 0.95) — but then Cliff 2 returns
- Drop MTP — frees ~280 MiB residency from draft model
- vllm#37429 hybrid Mamba/attention KV reduction (untested)
### FINAL recommended recipe (Balanced MTP variant)
```yaml
# docker-compose.long-text.yml
--max-model-len 180000
--gpu-memory-utilization 0.93
--speculative-config '{"method":"mtp","num_speculative_tokens":3}'
--max-num-batched-tokens 4128
--kv-cache-dtype turboquant_3bit_nc
--enable-chunked-prefill
--language-model-only
# Genesis env (key Cliff 2 + closures):
GENESIS_ENABLE_P103=1
GENESIS_ENABLE_PN30_DS_LAYOUT_SPEC_DECODE=1
GENESIS_ENABLE_PN32_GDN_CHUNKED_PREFILL=1
GENESIS_PN32_GDN_CHUNK_SIZE=8192
GENESIS_PN32_GDN_CHUNK_THRESHOLD=16384
GENESIS_FLA_FWD_H_MAX_T=16384
GENESIS_ENABLE_PN34_WORKSPACE_LOCK_RELAX=1
VLLM_SSM_CONV_STATE_LAYOUT=DS
# Sidecars (mounted):
patch_tolist_cudagraph.py # vllm#40807 workaround
patch_inputs_embeds_optional.py # vllm#35975 backport (~444 MiB savings)
```
### Shippable recipe summary
| Variant | Config | 60K | 90K | KV @ 180K | Best for |
|---|---|---|---|---|---|
| **Balanced MTP** (this recipe) | MTP-on + 0.93 + #35975 | ✅ | ❌ ~70-80K cliff | 1.4x | Default. Long-context with spec-decode. |
| Max-context safety | MTP-off + 0.95 + #35975 | ✅ | TBD (longer test) | 1.43x | RAG / single-shot codebase analysis |
| Future upstream | + vllm#37429 | TBD | TBD | TBD | When PR matures |
## ROUND 6 — Max-context variant (MTP-off + 0.95 + 200K + #35975)
### Setup
Same Genesis v7.69 stack + Codex P103 gate fix + #35975 sidecar, but:
- `--speculative-config` REMOVED (no MTP K=3 draft)
- `--gpu-memory-utilization 0.95` (recovered)
- `--max-model-len 200000` (admission test)
Boot resident: **21,004 MiB** (lower than MTP-on variants because draft model
residency is gone). KV pool: 5.1 GiB / 272,288 tokens. Concurrency at 200K:
**1.20x**. **200K ADMISSION PASSES** (vs 180K cap on MTP-on path).
### 60K probe — MTP-off + 0.95 + 200K + #35975
Result: **HTTP 200 OK in 537s wall** (vs 504s without #35975 in earlier
test — slightly slower, possibly due to 200K ctx vs 180K). Recall correct
("violet otter 48"). No OOM. Probe matches the closure pattern of round 2's
MTP-off + 180K test, plus the extra 20K ctx admission headroom.
### 90K probe (max_tokens=1) — MTP-off + 0.95 + 200K + #35975
Result: **HTTP 000 at 1500s curl timeout** (25-min budget). Engine kept
computing (GPU 100% util, no OOM trace), but never delivered response
within practical bound. Same pattern as round 2 MTP-off + 180K 90K probe
(also indeterminate within 15 min budget there).
Practical Cliff 2 ceiling on max-context variant: **60K confirmed**.
90K is past wall-clock-feasibility on this hardware regardless of
mem-util/MTP/admission tweaks.
### Final shippable matrix
Both variants share the same single-prompt safety envelope (~60K).
Differ on declared context, decode TPS, and concurrency:
| Variant | max_model_len | mem-util | MTP | KV @ ctx | 60K | 90K |
|---|---|---|---|---|---|---|
| Balanced MTP (`long-text.yml`) | **180K** | 0.93 | ✅ K=3 | 1.40x | ✅ 623s | ❌ OOM 112s |
| Max-context (`long-text-no-mtp.yml`) | **200K** | 0.95 | ❌ off | 1.20x | ✅ 537s | indeterm. |
### Codex r2 verdict (final)
> Cliff 2 at 60K on 24GB, TP1, Qwen3-Next TQ3, DS layout is not caused by
> the P103 cu_seqlens gate after the semantic fix. With MTP enabled,
> draft-model residency removes enough activation headroom that late
> FLA/GDN prefill fails. Disabling MTP lets the same request complete with
> HTTP 200; the earlier failure mode was headroom, while the verify
> harness timeout was client-side.
Plus: vllm#35975 backport frees enough headroom that MTP-on can also
close 60K when paired with mem-util 0.93.
### Open future tests
- vllm#37429 (hybrid Mamba/attention KV cache sizing) — could free more
resident memory without trading mem-util
- P103 streaming output variant — Codex r2 lower-priority recommendation;
on-disk implementation deferred

View File

@@ -0,0 +1,177 @@
# v7.69 Retest — Branch v7.69-cliff2-test
Date: 2026-05-02 PM
Genesis pin: 2db18df (v7.69 dev tip)
vLLM image: nightly-7a1eb8ac2ec4ea69338c51dc7afd4b15010abfa8 (v0.20.1rc1.dev16)
Hardware: 1× RTX 3090 (24 GB), SM 8.6 Ampere consumer, TP=1
Compose: long-text.yml @ 180K + 0.95 (back to original target — DS layout active)
## Env bundle (Cliff 2 closure recipe per Sander's v7.69 reply)
GENESIS_ENABLE_P103=1
GENESIS_ENABLE_PN32_GDN_CHUNKED_PREFILL=1
GENESIS_PN32_GDN_CHUNK_SIZE=8192
GENESIS_PN32_GDN_CHUNK_THRESHOLD=16384
GENESIS_FLA_FWD_H_MAX_T=16384
GENESIS_ENABLE_PN34_WORKSPACE_LOCK_RELAX=1
VLLM_SSM_CONV_STATE_LAYOUT=DS
## Sidecars dropped on this branch
- patch_pn25_genesis_register_fix.py (replaced by Sander's PN25 v7.68)
- patch_pn30_dst_shaped_temp_fix.py (replaced by Sander's PN30 v7.68 + v7.69 part3 marker fix)
- patch_workspace_lock_disable.py (replaced by Sander's PN34, opt-in env)
## ✅ F1 — PN30 v7.68 part3 drift-marker fix CONFIRMED WORKING
v7.68 dev tip (18e65e3) had part3 skip with `upstream_merged` because
its drift-markers list contained the bare prefix `[Genesis PN30` which
also matched parts 1+2's own markers. v7.69 (2db18df) tightened part3's
drift markers to `[Genesis PN30 v7.68 dst-shaped]` — specific enough to
not collide.
Boot log:
[PN30 v1/worker/mamba_utils.py — collect_mamba_copy_meta dst-shaped DS
temp (issue #17, v7.68)] applied 1 sub-patches:
pN30_collect_mamba_copy_meta_dst_shaped_temp
[Genesis] applied: PN30 DS conv state + spec-decode AL>1 (issue #17)
— PN30 v7.68 applied: ...
DS layout active (`Setting SSM conv state layout to DS`). Part1 + part2
+ part3 all APPLY clean, no upstream_merged false-positive. Apply_all
elapsed: clean, 0 failures.
## ✅ F2 — P103 chunk.py self-install hook CONFIRMED FIRES
v7.68 P103 logged "rebound at 0 caller sites" because `setattr` ran in
the entrypoint shell process and was lost on `exec vllm serve` (worker
spawn replaced the process image).
v7.69 P103 ships a text-patched self-install hook appended to
chunk.py end-of-file:
try:
import os as _genesis_p103_os
if _genesis_p103_os.environ.get("GENESIS_ENABLE_P103", ...):
from vllm._genesis.wiring.hybrid.patch_103_fla_cliff2_chunked
import _genesis_p103_install_at_import as _genesis_p103_install
_genesis_p103_install(globals())
except Exception:
pass
Confirmed firing on TP=1 spawn. Cliff 2 trace path:
vllm/model_executor/layers/fla/ops/chunk.py:111 forward
→ vllm/_genesis/wiring/hybrid/patch_103_fla_cliff2_chunked.py:203
chunked_fwd ← P103 wrapper engaged at runtime
→ return original_fwd(...) ← BUT falls through to fallback
The wrap IS engaged in workers. F2 is verified at the
"hook fires correctly" level. Boot log:
[Genesis P103 self-install] wrapper installed in chunk.py at
module-import time (survives `exec vllm serve` + worker spawn)
## ⚠️ F3 — Cliff 2 NOT closed at 60K despite P103 + PN32 v2
Probes pass / fail summary:
[1/7] Long-context needle 10K ✅ recalled 'silver otter 16'
[1/7] Long-context needle 30K ✅ recalled 'amber iguana 95'
(was crashing on v7.68; v7.69 fix)
[2/7] Tool prefill 25K ✅ tool_call clean
[3/7] IDE-agent one-shot ✅ 66 completion tokens
[4/7] Multi-turn agent ✅
[5/7] LCB-coding shape ✅
[6/7] Reasoning max=8192 ✅
[7/7] Long-context needle 60K ❌ HTTP 500 (Cliff 2 OOM)
[7/7] Long-context needle 90K ❌ HTTP 500
Cliff 2 OOM trace (probe 7 60K rung):
File "vllm/model_executor/layers/fla/ops/chunk.py", line 111, in forward
g, o, A, final_state, w, h, v_new = chunk_gated_delta_rule_fwd(
File "vllm/_genesis/wiring/hybrid/patch_103_fla_cliff2_chunked.py",
line 203, in chunked_fwd
return original_fwd( ← P103 fallback path
File "vllm/model_executor/layers/fla/ops/chunk.py", line 72,
in chunk_gated_delta_rule_fwd
o = chunk_fwd_o(
File "vllm/model_executor/layers/fla/ops/chunk_o.py", line 161,
in chunk_fwd_o
o = torch.empty_like(v)
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 50.00 MiB.
GPU 0 has a total capacity of 23.56 GiB of which 24.50 MiB is free.
### Why P103's chunked path didn't engage
P103's gate is `T <= MAX_T or cu_seqlens != None`. At 60K prompt
serving:
- T = 60000 (well above MAX_T=16384)
- But cu_seqlens != None (vLLM serving ALWAYS passes cu_seqlens)
→ P103 falls through to original_fwd
### Why PN32 v2 didn't engage
PN32 v2's docstring says "Multi-seq bypasses to original" — i.e. it
only chunks when there's exactly one sequence. With MTP K=3
spec-decode, the dispatcher reports `uniform_query_len=4` per
P66 cudagraph filter log. Whether PN32's gate sees this as
"multi-seq" wasn't traceable at runtime, but the trace doesn't
show PN32's chunked path.
### Net: F3 closure works only when cu_seqlens=None AND single-seq
Both P103 and PN32 v2 have gates that require cu_seqlens=None
and/or single-sequence to engage. vLLM serving always passes
cu_seqlens (it's the per-sequence boundary marker that enables
batched serving). So neither chunking path fires under real
serving, only synthetic test scenarios.
## Other engine state observations on v7.69
Engine survived probe 7 OOM gracefully (HTTP 500 + EngineDeadError
exception → API server shutdown). Compare to v7.66 where Cliff 2 OOM
killed the EngineCore process abruptly. Suggests vLLM's
`async_llm.output_handler` exception handling is cleaner on v0.20+.
Engine still came back up clean on next request batch (could re-test
small probes after large probe failed).
## Cross-rig regressions: NONE
TPS on smoke benchmark equivalent to v7.66 + sidecars (same engine
path under cu_seqlens != None, no overhead from PN32 v2 in fallback
mode).
## Recommendation back to Sander
Two options for v7.70:
(a) Ship F3 fix that handles cu_seqlens != None — slice metadata
along with tensors per chunk; thread initial_state correctly
across chunk boundaries even when cu_seqlens describes a
multi-sequence batch. Per Sander's own caveat in v7.69 reply:
"Multi-seq bypasses to original (chunking across cu_seqlens
boundaries needs inner state-cache surgery not exposed at this
layer)" — this is the surgery that needs to happen.
(b) Keep current "synthetic-only" gate but document explicitly
that P103 + PN32 only close Cliff 2 in benchmarks where
cu_seqlens=None is passed manually. Real serving stays
behind the cliff. Operators on TP=1 + 24GB should plan for
50-60K single-prompt ceiling and route longer prompts to
TP=2 (`dual.yml`) or llama.cpp.
Our preference: (a) — but we recognize this is non-trivial. Happy to
engage ChatGPT/Codex CLI on the cu_seqlens-aware chunking design and
share back as we did for PN30 dst-shaped + PN25 import-time
registration.
## What stayed working
- All 4 of v7.68's other accepted-and-folded patches (PN25 v7.68
TP=1 register, PN34 workspace_lock, PN30 v7.68 part1+part2, P101)
remain solid.
- Tool calls clean across IDE-agent shapes (probes 3 + 4).
- 30K single-prompt now safe (was unsafe on v7.68 due to PN30
being broken; v7.69 closes that regression).
## Net: v7.69 is a clear improvement over both v7.66 and v7.68 dev tip
If we accept that Cliff 2 is fundamentally still architectural for
single-24GB-GPU long-prompt and route around it, v7.69 is our
best stable single-card config to date. Recommend Sander tag v7.69
stable and we move to master once the 30K-now-safe data is
cross-confirmed on Sander's planned A5000-rig.

View File

@@ -106,23 +106,37 @@ echo "Model dir: ${MODEL_DIR}"
# vllm/_genesis package + per-patch env opts). Newer composes mount the package; # vllm/_genesis package + per-patch env opts). Newer composes mount the package;
# the legacy compose still references the v7.13 shim. # the legacy compose still references the v7.13 shim.
# Pin Genesis to the exact commit our published numbers were measured against. # Pin Genesis to the exact commit our published numbers were measured against.
# Currently pointing at v7.66 dev tip (commit fc89395, 2026-05-02 AM). Bumped # Currently pointing at v7.69 dev tip (commit 2db18df, 2026-05-02 PM). Bumped
# from v7.64 (64dd18b) for the v7.65 patch set: # from v7.66 (fc89395) for the v7.69 patch set, which addresses the 3
# - P38B / P15B — close the Cliff 1 mech B cascade (issues #14 + #15) via # regressions the v7.68 cross-rig retest found ([club-3090#19] and our
# compile-safe in-source hook + FA varlen workspace clamp. # v7.68-cliff2-test branch summary):
# - PN25 — Inductor-safe silu_and_mul opaque op (replaces our local # - F1 (PN30 part3 drift-marker bug) — fixed via specific marker
# patch_pn12_compile_safe_custom_op.py — now removed). # `[Genesis PN30 v7.68 dst-shaped]` so part3 idempotency check no longer
# - PN26b — Genesis-original sparse-V Triton kernel for SM86 (Ampere # collides with part1+2 markers in the same file.
# consumer). First sparse-V kernel in any public tree for SM86. Default # - F2 (P103 setattr lost on `exec vllm serve`) — fixed via self-install
# ON in v0.20+ composes (BLOCK_KV=8 num_warps=4 threshold=0.01 per # hook text-patched into chunk.py end-of-file. Survives any startup
# Sandermage's 27B-specific tuning). # mechanism (workers, fork, spawn, exec). The "rebound at 0 caller sites"
# - PN28 — merge_attn_states NaN guard backport (vllm#39148). # log message in v7.68 was misleading — internal callers DID get the
# - Cliff 8 hardening (partial_apply_warnings counter in boot summary). # setattr in the entrypoint shell process, but `exec` replaced the image
# Pinned to dev SHA fc89395 because v7.66 is feature-complete on dev but not # and lost it. v7.69 hook fires every time chunk.py imports.
# yet tagged; SHA pin is immutable. # - F3 (PN32 v1 chunked at wrong level) — rewritten as PN32 v2 to patch
# Bumping GENESIS_PIN requires re-running verify-full.sh against your composes # `_forward_core` directly + thread initial_state via prior chunk's
# last_recurrent_state. Composes with P103: v2 chunks the OUTER FLA
# call, P103 chunks INSIDE the FLA inner h tensor.
# Recommended Cliff 2 closure env bundle for single-24GB-GPU:
# GENESIS_ENABLE_P103=1 (close inner FLA h tensor)
# GENESIS_ENABLE_PN32_GDN_CHUNKED_PREFILL=1 (close outer FLA call buffer)
# GENESIS_PN32_GDN_CHUNK_SIZE=8192 (default)
# GENESIS_PN32_GDN_CHUNK_THRESHOLD=16384 (default)
# GENESIS_FLA_FWD_H_MAX_T=16384 (P103 default)
# v7.69 also retains v7.68's accept-and-fold of our 3 cross-rig sidecars:
# PN25 v7.68 (TP=1 worker-spawn registration), PN30 v7.68 (DS conv-state
# layout dst-shaped temp), PN34 (vllm#39226 runtime workspace_lock).
# Pinned to dev SHA 2db18df because v7.69 is feature-complete on dev pending
# our retest validation; if clean, Sander will tag stable.
# Bumping GENESIS_PIN requires re-running verify-stress.sh against your composes
# to confirm the new commit works on your config. # to confirm the new commit works on your config.
GENESIS_PIN="${GENESIS_PIN:-fc89395}" GENESIS_PIN="${GENESIS_PIN:-2db18df}"
if [[ "${SKIP_GENESIS:-0}" != "1" ]]; then if [[ "${SKIP_GENESIS:-0}" != "1" ]]; then
if [[ -d "${GENESIS_DIR}/.git" ]]; then if [[ -d "${GENESIS_DIR}/.git" ]]; then
@@ -143,38 +157,11 @@ if [[ "${SKIP_GENESIS:-0}" != "1" ]]; then
fi fi
echo "[genesis] Pinned to ${GENESIS_PIN} ($(cd "${GENESIS_DIR}" && git rev-parse --short HEAD))" echo "[genesis] Pinned to ${GENESIS_PIN} ($(cd "${GENESIS_DIR}" && git rev-parse --short HEAD))"
# PN25 worker-spawn registration fix — local backport. # v7.69 ships PN25 + PN30 + PN34 directly (Sander's accept-and-fold of our
# # cross-rig sidecars). Local patch_pn25_genesis_register_fix.py +
# Sandermage shipped his own fix in d92bcb3 (hasattr global-registry guard # patch_pn30_dst_shaped_temp_fix.py + patch_workspace_lock_disable.py are
# in `_register_op_once`), but cross-rig validation on our TP=1 single-card # now redundant. Sidecar Python files retained in vllm/patches/ for
# showed it doesn't work — `torch.ops.genesis.silu_and_mul_pooled` doesn't # rollback if any v7.69 patch regresses on your config.
# exist in spawned workers on TP=1 (whereas it does on his TP=2 PROD).
# Reported back as comment on Sandermage/genesis-vllm-patches#16.
#
# Our local v3 patch takes a different approach: register at activation.py
# import time as a module-level cached global, BEFORE any dynamo trace runs.
# Survives worker spawn correctly on TP=1.
#
# Idempotent. Safe to re-run.
if [[ -f "${ROOT_DIR}/models/qwen3.6-27b/vllm/patches/patch_pn25_genesis_register_fix.py" ]]; then
(cd "${ROOT_DIR}" && python3 models/qwen3.6-27b/vllm/patches/patch_pn25_genesis_register_fix.py) || {
echo "[genesis] WARN: PN25 register fix did not apply cleanly. PN25 may not work in workers." >&2
}
fi
# PN30 DS conv-state layout fix — local correction for Genesis issue #17.
#
# Sander's PN30 avoided vLLM's DS+spec-decode NotImplementedError by
# compacting state[src_block, :, offset:] and raw-memcpying it into the
# destination block. That corrupts DS row strides. Our sidecar patches PN30
# so collect_mamba_copy_meta builds a full destination-shaped temp block,
# copies the source tail into the dst prefix, then reuses PN30's temp-list
# lifetime handling.
if [[ -f "${ROOT_DIR}/models/qwen3.6-27b/vllm/patches/patch_pn30_dst_shaped_temp_fix.py" ]]; then
(cd "${ROOT_DIR}" && python3 models/qwen3.6-27b/vllm/patches/patch_pn30_dst_shaped_temp_fix.py) || {
echo "[genesis] WARN: PN30 dst-shaped temp fix did not apply cleanly. Keep PN30 disabled or use SD layout." >&2
}
fi
else else
echo "[genesis] SKIP_GENESIS=1 — not cloning." echo "[genesis] SKIP_GENESIS=1 — not cloning."
fi fi