[Test] Deterministic e2e regression tests (xfail) for hybrid-Mamba prefix cache corruption (#43559) - #48198
Closed
puririshi98 wants to merge 8 commits into
Closed
[Test] Deterministic e2e regression tests (xfail) for hybrid-Mamba prefix cache corruption (#43559)#48198puririshi98 wants to merge 8 commits into
puririshi98 wants to merge 8 commits into
Conversation
Contributor
Author
Add tests/v1/e2e/test_hybrid_mamba_prefix_cache_correctness.py: two deterministic, output-level regression tests for issue vllm-project#43559 (hybrid Mamba models silently corrupt output with prefix caching enabled in mamba_cache_mode="align" plus MTP speculative decoding), the reproducer and end-to-end regression coverage maintainers asked for in the issue, plus a Buildkite step (engine.yaml, optional 4xH200, running pytest with --runxfail so the live corruption is a visible red on the non-gating job) so the file is actually executed — no existing glob covers new top-level files under tests/v1/e2e/. - test_cold_concurrent_prefill_mamba_prefix_cache reproduces the chunk-fragmentation mechanism (concurrent cold prefills of prompts sized between 1 and 2 Mamba blocks under a small token budget caching a mid-block recurrent state as a boundary snapshot) on nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16 (TP4). - test_multi_turn_decode_written_mamba_prefix_cache reproduces the eagle-lookahead hit-length overrun (re-asks resuming from Mamba blocks whose boundary snapshot was written during speculative decode) on Nemotron-Super (TP4) and Qwen/Qwen3.6-27B-FP8 (TP1). Both tests grade strictly relative to an in-run control engine with enable_prefix_caching=False. The arms necessarily differ in chunk boundaries too (vLLM forces mamba_cache_mode back to "none" when caching is off), and vLLM does not guarantee batch-shape-invariant numerics, so benign nondeterminism (symmetric across arms, budgeted via FLIP_MARGIN) is separated from vllm-project#43559 corruption (one-sided, wrong only with caching). Token geometry is derived at runtime from the resolved MambaSpec block size and geometry skips carry a distinctive coverage-lost prefix. Corruption checks raise a dedicated CorruptionDetected type and both xfail markers are restricted to it (raises=CorruptionDetected, strict=False), so only the vllm-project#43559 signal can XFAIL: engagement guards (prefix-cache queries/hits, MTP drafts, a >=3-block cache-liveness probe in the cold-race test) run before the corruption checks and hard-fail the tests even while the markers are present. Neither marker is strict: pytest-level red is not calibrated as deterministic per model, and the Qwen parametrization is unvalidated (a community harness on the issue could not reproduce corruption on it), so no red claim is made for it. No source fix is included (fail-closed on duplicate work): both known mechanisms are claimed by open PRs (vllm-project#45477, vllm-project#47861, vllm-project#45614, vllm-project#46281, plus the vllm-project#43650 workaround). The live vllm-project#43559 corruption signal was measured on GB200 with Nemotron-3 Super 120B (evidence in the PR); behavior under the open fixes is expected green, not measured — whoever lands a fix removes the markers (and the CI step's --runxfail) after one green calibration run. Signed-off-by: Rishi Puri <riship@nvidia.com> Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Rishi Puri <riship@nvidia.com>
Signed-off-by: Rishi Puri <riship@nvidia.com>
- Cold-race geometry skip was unreachable: VllmConfig.validate_block_size asserts block_size <= max_num_batched_tokens at engine boot in align mode, before the in-body check could run. Catch that specific boot AssertionError and convert it to the GEOMETRY_SKIP coverage-lost skip; drop the dead in-body skip. - _mamba_block_size now verifies the docstring's cross-component invariant: the scheduler's align-mode split size (cache_config.block_size, the engine-core MIN over all kv-cache groups) must equal the MambaSpec block size, skipping with GEOMETRY_SKIP on mismatch instead of silently mistargeting. - Module docstring updated to the validated state: all three parametrized arms (cold-race Nemotron, multi-turn Nemotron, multi-turn Qwen) measured red on main; stale 'Qwen unvalidated' claim removed. Signed-off-by: Rishi Puri <riship@nvidia.com>
- Replace pytest.fail control-quality gates with ControlQualityFailure (plain Exception): _pytest.outcomes.Failed is a BaseException and escapes the fork wrapper's except-Exception child handler, losing the diagnostic and letting the forked pytest session continue. - Replace the five GEOMETRY_SKIP pytest.skip paths with a hard-failing GeometryUnsupported exception: the fork runner converts in-body Skipped to exit code 0, so geometry drift would report as PASS and green the --runxfail CI step while the bug is live. - Raise the Nemotron-Super TP4 gate to large_gpu_mark(min_gb=140) to match the Buildkite step's H200-class capacity requirement; 4x80GB H100 would OOM at engine boot, which this file treats as a hard fail. Signed-off-by: Rishi Puri <riship@nvidia.com>
Docstring-only: replace the stale 'behavior on a tree with the open fixes applied has not been measured at all' / 'EXPECTED to be cured' claims with the measured five-PR matrix outcome (three PRs cure the multi-turn lane; none cures the cold-race lane), and direct fix landers to remove each xfail marker only after a green calibration run of its own lane. Signed-off-by: Rishi Puri <riship@nvidia.com>
Ports the measured-data comments from vllm-project#48361's copy of the shared e2e test file, keeping this PR's xfail posture: the cold-race Qwen exclusion now cites the measured control-arm failure (wave-1 misses 16/16 with APC off), and the stale 'wave-2 hit ratio ... has not been calibrated on a fixed tree' comment now records the 0.431-0.477 fixed-tree calibration range already published in this PR's own cross-verification matrix. Comment-only; no executable change (the two copies now differ only in xfail posture, verified by AST compare). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Rishi Puri <riship@nvidia.com>
puririshi98
added a commit
to puririshi98/vllm
that referenced
this pull request
Aug 19, 2026
…m-project#43559) End-to-end output-correctness regression tests for vllm-project#43559 (hybrid-Mamba prefix caching under MTP/EAGLE spec decode). Two graded arms vs an APC-off control: a cold-concurrent-prefill (write-side) arm and a multi-turn decode-written-block (read-side) arm, parametrized on Nemotron-Super-120B-A12B-BF16 (TP4) and Qwen3.6-27B-FP8. Corruption raises CorruptionDetected; an anti-vacuous liveness gate fails a "clean" run whose prefix cache is not demonstrably live, so a silently-disabled cache cannot pass vacuously. Registered as a non-gating optional Buildkite step (4xH200, 240-min). Supersedes the test-only vllm-project#48198. Requires both the corruption fix (vllm-project#48361, Mooncake connector eagle-peek gate) and the prefix-cache stats fix (vllm-project#48668) to pass — the cold-race liveness gate reads vllm:prefix_cache_hits, which under-reports to 0 without vllm-project#48668. Validated green on GB200 with both applied: cold-race liveness_probe_hits=4224 + no corruption, multi-turn wave-2 hit ratio 0.477 + no corruption. Signed-off-by: Rishi Puri <riship@nvidia.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[Test] Deterministic e2e regression tests (xfail) for hybrid-Mamba prefix cache corruption (#43559)
Status / supersession
Open as the red/xfail reproducer; superseded by #48361 on its merge,
after which this PR will be closed. The earlier caveat here — that this
branch's copy of the test file had diverged ahead of the copy
shipped in #48361 — is resolved: #48361's branch now carries the same
hardened file (fork-runner-safe
GeometryUnsupported/ControlQualityFailurehard-fail guards replacing in-bodypytest.skip/pytest.fail, the engine-bootAssertionError→GeometryUnsupportedmapping, thecache_config.block_size↔MambaSpecgeometry cross-invariant, and the Nemotron-Superlarge_gpu_markbump tomin_gb=140). The two copies now differ onlyin posture: #48361 drops the two xfail markers, the
XFAIL_REASONconstant, and the CI step's
--runxfail, and its docstrings describethe fixed-tree status instead of the live bug (executable content
verified identical by AST compare modulo docstrings/decorators).
Purpose
#43559 reports silent, hard-to-reproduce output corruption on hybrid-Mamba
models when prefix caching and MTP speculative decoding are combined. The
maintainer ask in that thread was a reliable reproducer. This PR delivers
exactly that: two deterministic, output-level e2e regression tests that fail
on
main(validated red ate08a9151and re-confirmed at88e5e2c5)for mechanistically understood reasons, and will guard
against regressions once the open fixes land.
This PR intentionally contains no source fix. Both corruption mechanisms
already have open fix PRs — #45477 (chunked-prefill fragmentation caching a
mid-block Mamba state as a block-boundary snapshot) and #47861 / #45614 /
#46281 (Mamba hit length overrunning the attention-verified hit under the
spec-decode lookahead). Adding a fifth fix would duplicate them; nobody had
yet contributed e2e output-level coverage, which is a different layer from
those PRs' unit/scheduler tests.
The tests
tests/v1/e2e/test_hybrid_mamba_prefix_cache_correctness.py, parametrizedover Qwen/Qwen3.6-27B-FP8 (TP1) and
nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16 (TP4), plus an optional
non-blocking Buildkite step:
blocks, prefilled cold and concurrently under a small token budget.
Asserts output-distinctness and needle recall relative to an APC-off
control, margin-graded rather than zero-tolerance: benign batch-shape
nondeterminism flips symmetrically across arms, while [Bug]: Accuracy drops ~20% when
--enable-prefix-cachingis used together with MTP speculative decoding (Qwen3.6 35B-A3B) #43559corruption is one-sided. Runs Nemotron-Super only: Qwen corrupts under this
geometry even with APC off, so the control-quality gate cannot grade
the cache there — hence three result rows below, not four.
speculative decode. Asserts recall parity vs. an APC-off control.
Both are
xfail(raises=CorruptionDetected, strict=False): only genuinecorruption xfails. Engagement failures (zero cache hits / zero drafts),
weak-control results, geometry drift, and boot errors hard-fail, so the
tests cannot pass vacuously. Geometry and control-quality guards raise
plain
Exceptionsubclasses rather thanpytest.skip/pytest.fail:the fork-based per-test runner reports in-body skips as PASS and loses
Failed(aBaseException) diagnostics, so both would otherwise besilent-green vectors on the
--runxfailstep. Remove the markerstogether with
--runxfailin the CI step once a fix merges.Results on main at validation time (
e08a9151, GB200)Run with
--runxfailso the raw failures are visible:CorruptionDetected: 16 identical prompts → 3 distinct outputs (needle824568corrupted to82423567/82447757); control: 1CorruptionDetected: 17/32 recall flips vs. control, 0 in the benign directionCorruptionDetected: 14/32 one-sided flips — first reproduction on the issue's own model family (batch-1 harnesses in the thread could not)Prefix-cache and spec-decode engagement verified via
llm.get_metrics()counters in every run. The corruption is unchanged at
e12b91b03(which contains #48132), ruling that fix out for this issue.
pytest tests/v1/e2e/test_hybrid_mamba_prefix_cache_correctness.py \ --runxfail -v -s -rA -k "nemotron-super" # 4x >=140GB GPUs (H200/GB200 class) pytest tests/v1/e2e/test_hybrid_mamba_prefix_cache_correctness.py \ --runxfail -v -s -rA -k "qwen" # 1x >=80GB GPURun note: in the executed copies the two HF model ids were substituted with
pre-downloaded local paths (
HF_HUB_OFFLINE=1breaks model-redirectresolution); everything else was byte-identical to the diff as of the
validation commit. The diff has since received post-validation amendments:
comment/docstring rewording, ruff-format whitespace, review-round hardening
of the geometry/control-quality guard paths (now fork-runner-safe hard
failures instead of skips), a hardware-capacity gate bump on the
Nemotron-Super param (
large_gpu_markmin_gb 80 → 140, matching the CIstep's H200-class targeting), and a comment-only calibration-data sync
with #48361's copy of the file. None of these touches the validated trigger
geometry or grading logic, which are unchanged from the validation commit.
The branch has also since merged
main@1ef1c7ebb(2026-07-11); thatmerge touched only surrounding steps in
engine.yaml, not this PR's stepor the test file.
Cross-verification: these tests discriminate between the open fix PRs
We ran the two Nemotron-Super arms against each open fix PR, hand-rebased
onto main as of the cross-verification runs (
88e5e2c5, at which theclean-main baseline is RED on both arms). GB200, one run per arm; "zero-hit" = the test's anti-vacuous
liveness/engagement gate fired because prefix-cache hits dropped to
exactly 0 while queries stayed >150k.
mainTakeaways: the multi-turn lane is cured by three independent PRs; no
open PR fixes the cold-race lane — it either still corrupts (#45614) or
goes quiet only because hybrid prefix caching stops producing hits under
this pattern (a caching regression, not a cure — which is exactly what the
liveness gate in these tests exists to catch). Caveats: hand-rebased
ports, not the PRs' own branches; single run per arm.
AI assistance disclosure
Developed with AI assistance (Claude Code), including adversarial
multi-agent review; every line reviewed by the human submitter, who ran the
tests and can defend the change end-to-end.