Skip to content

vllm_dissag: add GLM-5.1-FP8 (MLA+DSA) MoRI-EP WideEP enablement#2

Open
raviguptaamd wants to merge 6 commits into
vllm-disagg-unified-launcherfrom
raviguptaamd/glm5.1-wideep-on-171
Open

vllm_dissag: add GLM-5.1-FP8 (MLA+DSA) MoRI-EP WideEP enablement#2
raviguptaamd wants to merge 6 commits into
vllm-disagg-unified-launcherfrom
raviguptaamd/glm5.1-wideep-on-171

Conversation

@raviguptaamd

Copy link
Copy Markdown
Owner

Summary

Adds GLM-5.1-FP8 (GlmMoeDsaForCausalLM — MLA + DeepSeek Sparse Attention) to the unified two-axis launcher for MoRI-EP WideEP disaggregated serving at 1P1D (EP=8) and 2P2D (EP=16). Stacked on the vllm-disagg-unified-launcher branch (ROCm#171).

GLM-5.1 is the first DSA-family model on this launcher; DSA adds a 2nd KV cache per layer (indexer) and a sparse-MLA decode path needing model-specific handling on top of the working DeepSeek path.

Changes

  • models.yaml: GLM-5.1-FP8 entry — DSA recipe env (KV_BLOCK_SIZE=1, VLLM_ROCM_USE_AITER_MLA=1, prefill eager / decode PIECEWISE, per-role mori HT/LL, fp8 KV) + dp_flags for the glm47/glm45 tool/reasoning parsers.
  • run_xPyD_models.slurm: GLM in VALID_MODELS / MORI_EP_VALID_MODELS / WIDE_EP_ONLY_MODELS; models.yaml-over-image-baked-ENV precedence fix (3-tier: image < yaml < submit -e).
  • connectors/moriio.sh: append model_args on the wideEP serve path (was only on the TP path — wideEP dp: flags were silently dropped); implement connector_runtime_patch() to apply the GLM DSA patchers, gated on MODEL_NAME==GLM-5.1-FP8 (no-op for every other model).
  • Patchers (idempotent, anchor-based, self-adapting to the image): DSA invalid-token kernel #45324; MoRIIO dual-KV geometry / per-layer engine offset / completion gate (native on mori v1.2.1 → self-skip); MoRIIO abort-path None-guard (fixes a decode EngineCore crash when a request is aborted before its KV peer handshake).

Isolation

Every GLM addition is gated on MODEL_NAME==GLM-5.1-FP8 or is an additive allowlist/catalog entry. The one shared-path edit (append model_args on wideEP) is a no-op for models with empty dp: flags. DeepSeek/other behavior unchanged.

Validation (MI300X gfx942, mori v1.2.1 / aiter 0.1.16.post3 / router181 image)

  • 1P1D EP=8 and 2P2D EP=16 both serve end-to-end through the full prefill→decode MoRIIO KV path. Coherent + factually correct output.
  • Decode PIECEWISE cudagraph captures cleanly (9/9). ITL flat ~87–91 ms across context 1k→28k and concurrency 8→32 (DSA O(top-2048) decode is context-independent); throughput scales near-linear. 0 stalls / crashes across a full 9-cell perf sweep per topology.
  • Random-Q accuracy 7/8.
  • Known limitation (upstream, not this PR): long-context NIAH collapses >~4k ctx — reproduces vLLM #47042 (AITER sparse-MLA persistent split-KV decode on gfx942). Short context (2k) is 10/10.

Test plan

  • bash -n slurm + moriio.sh + vllm_disagg.sh; py_compile patchers; models.yaml lint
  • DRY_RUN=1 RUN_MORI=1 MODEL_NAME=GLM-5.1-FP8 emits --block-size 1 --kv-cache-dtype fp8 --enable-expert-parallel + glm parsers; DeepSeek argv unchanged
  • 1P1D + 2P2D smoke: coherent canary, 0 deferred-send stalls
  • Perf sweep 1k/8k/28k × con 8/16/32

🤖 Generated with Claude Code

raviguptaamd and others added 6 commits July 3, 2026 15:50
Ports the GLM-5.1-FP8 WideEP enablement (from MAD-private #338) onto the
unified two-axis launcher (ROCm#171). GLM-5.1 is GlmMoeDsaForCausalLM: MLA +
DeepSeek Sparse Attention, served under MoRI-EP wideEP disagg at 1P1D (EP=8)
and 2P2D (EP=16).

Changes:
- run_xPyD_models.slurm: GLM-5.1-FP8 added to VALID_MODELS,
  MORI_EP_VALID_MODELS, and WIDE_EP_ONLY_MODELS (DSA validated only under
  wideEP; reject the untested moriio+TP path).
- models.yaml: GLM-5.1-FP8 entry. env: encodes the DSA recipe (KV_BLOCK_SIZE=1,
  VLLM_ROCM_USE_AITER_MLA=1, eager both roles, per-role mori all2all, 16GiB
  shmem heap). dp_flags carries the GLM tool/reasoning parsers.
- connectors/moriio.sh:
  * wideEP serve now appends model_args (per-model models.yaml flags) in both
    the dry-run and real paths. Previously only the TP path did, so wideEP
    dp:/dp_flags were silently dropped (rixl already appended them in both
    paths). No-op for models with empty dp flags (DeepSeek).
  * connector_runtime_patch() applies the 4 GLM DSA patchers, gated on
    MODEL_NAME=GLM-5.1-FP8; a pure no-op for every other model.
- 4 required DSA patchers (+1 optional instrument), idempotent + anchor-based
  + self-skipping: kernel invalid-token fix (vllm #45324), MoRIIO connector
  dual-KV geometry, engine per-layer transfer-offset cache, and the completion
  gate fix (gate on num_transfer_layers, excluding the DSA indexer caches).

DeepSeek/other models are byte-identical in behavior. Verified: bash -n,
py_compile, yaml lint, and DRY_RUN driver argv for GLM 1P1D/2P2D + DeepSeek
no-regression.

Co-Authored-By: Claude <noreply@anthropic.com>
… image

Verified the 4 GLM DSA patchers against the validated target image
(mori v1.2.1 / aiter 0.1.16.post3). The MoRIIO connector is restructured
there and handles DSA dual-KV natively: only the invalid-token kernel fix
(#45324) is still needed; the dual-KV geometry (moriio_layout.py per-layer
geometry) and completion-gate (engine sealed writes_expected, which already
covers "hybrid models register more caches than hooks fire") are native.

apply_glm_dsa_moriio_gate_fix.py previously patched the connector half
unconditionally, so on a restructured image it half-applied (injected a dead
num_transfer_layers into the connector) while the engine half found no anchor.
Made it atomic: skip BOTH halves unless the engine gate anchor is present.
Verified the connector is left unmutated on v1.2.1, while all 4 patchers still
fully apply on the older b10a9f7a image. No per-image branching — the patchers
self-adapt via anchor presence.

models.yaml: document the validated image and the per-image patcher behavior.
…ecedence)

A DeepSeek-tuned disagg image bakes recipe knobs as container ENV
(KV_BLOCK_SIZE=16, VLLM_ROCM_USE_AITER_MLA=0, VLLM_CUDAGRAPH_MODE=PIECEWISE,
KV_CACHE_MEMORY_BYTES=...). The driver exported models.yaml env: with
"if k in os.environ: continue" (so submit-time -e wins), but baked container
ENV also satisfies that check, so models.yaml silently LOST to the image. Any
model whose recipe differs from the image defaults was mis-configured — GLM-5.1
DSA needs block=1 + AITER sparse MLA on + eager, but ran with the DeepSeek
block=16 / MLA-off / PIECEWISE baked values.

Fix (precedence: image-baked < models.yaml < submit-time -e):
- run_xPyD_models.slurm captures MODELS_YAML_PROTECT = the recipe keys the USER
  set at submit time (checked on the host before the slurm sets defaults),
  forwarded via -e.
- vllm_disagg.sh: yaml env overrides existing env EXCEPT protected keys (genuine
  submit-time -e still wins). Falls back to legacy "skip if in env" when
  MODELS_YAML_PROTECT is unset (direct/no-slurm runs), so nothing regresses.

Verified in-container against the mori v1.2.1 router image: with baked
block=16/MLA=0/PIECEWISE, GLM-5.1-FP8 now emits --block-size 1, kv-cache-dtype
fp8, cudagraph NONE. DeepSeek (empty dp flags, matching baked recipe) unaffected.
Per-role cudagraph for GLM-5.1: prefill NONE (eager — prefill cudagraph capture
deadlocks on this stack), decode PIECEWISE (captures cleanly on DSA, ~3.7x ITL
win vs eager: ~69ms vs ~264ms, validated). Was NONE/NONE (the conservative
"cudagraph unproven" starting point); decode PIECEWISE is now proven safe.
Verified in-container: prefill emits cudagraph_mode NONE, decode PIECEWISE.
On the mori v1.2.1 connector (restructured vs the b10a9f7a connector #338 ran
on), a request aborted before its KV-transfer peer handshake has peer_zmq=None.
_release_write_prefill_blocks then calls parse_moriio_zmq_address(None) ->
None.split(",") -> AttributeError, which only the ValueError branch guards, so
the EngineCore dies and cascades to all decode workers (EngineDeadError) -> decode
dead. Observed live: a canary aborted during first-token cold JIT killed decode.

The same file already guards the other call site (request_finished notify path)
with `if peer_zmq is not None`; the release path just missed it. New patcher adds
the matching guard (bail gracefully, like the existing ValueError branch). This
function does not exist in the older b10a9f7a connector, so #338 never hit it.

Idempotent + anchor-based + self-skipping; wired into the GLM connector_runtime_patch
list. No effect when peer_zmq is valid.
Investigation finding (no functional change — comment only): GLM-5.1 on this
mori v1.2.1 image serves coherent + correct output up to ~14k prompt tokens;
beyond ~16-18k the ROCM_AITER_MLA_SPARSE prefill indexer corrupts output
(repetition collapse), reproducing vLLM #40018. This is an upstream AITER/vLLM
kernel bug (partial fix #43781 is in the image; the complete prefill fix is not),
NOT the MAD port.

Tested and ruled out --max-model-len 32768 as a mitigation: the corruption onset
is a fixed ~16-18k token count in the gather/logits kernel, not a workspace-size
artifact (workspace = max_model_len*40), so capping context does not move the
threshold and only limits usable range. Left at native max_model_len; documented
the ~14k safe envelope and that long-context needs a newer image with the
complete upstream fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant