Skip to content

Model runner v2: keep short prefills off speculative decode graphs - #687

Open
myshytf wants to merge 85 commits into
local-inference-lab:dev/infernal-invocationfrom
myshytf:fix/kimi-k3-prefill-tail-cudagraph-20260907
Open

myshytf wants to merge 85 commits into
local-inference-lab:dev/infernal-invocationfrom
myshytf:fix/kimi-k3-prefill-tail-cudagraph-20260907

Conversation

@myshytf

@myshytf myshytf commented Sep 7, 2026

Copy link
Copy Markdown

Prefill-aware graph dispatch

Status: qualified and serving. The reproduced boundary failure, broader serving regressions and final paired serving benchmark have completed. This lab PR preserves an operator-authorized diagnosis and fix. Human review is not claimed.

Model runner v2 used token counts alone to select a full speculative-decode graph. A prefill tail with exactly the verifier's query length was therefore eligible for decode graph replay even though its KDA metadata described a prefill. The graph contains capture-time decode kernels and cannot substitute for the prefill path.

The failure reproduces on Kimi-K3 TP9/DCP9 with colocated DFlash, speculative depth 3, 4,608-token prefill chunks, FULL_AND_PIECEWISE graphs, and the v2 runner:

Prompt length Cold generation Cached generation
4,609 / 4,610 / 4,611 expected JSON expected JSON
4,612 unrelated text followed by repetition unrelated text followed by repetition
4,613 / 4,616 / 4,617 / 4,624 / 4,661 expected JSON expected JSON
9,220 unrelated text followed by repetition unrelated text followed by repetition
Other tested tails after 9,216 expected JSON expected JSON

The affected user request had 115,204 input tokens, exactly 25 * 4608 + 4. Generation was already incoherent at its first tokens, then repeated one sentence until the output limit. An eager-profiled request with the failing 4,612-token shape returns the expected JSON. No CUDA Xid, finite-output alarm, or HTTP error accompanied the bad generation.

Change

Commit d4c3c7578f derives has_prefill from the already-available CPU prefill progress of scheduled requests. The graph manager excludes uniform and bounded-verifier FULL candidates when that flag is set. It retains piecewise graphs, generic FULL graphs captured for mixed batches, and eager fallback. Dummy capture and true decode retain their existing behavior.

The prefill flag participates in data-parallel graph agreement so a second dispatch cannot promote another replica's prefill into a decode graph. No GPU synchronization or new device kernel is introduced. Model arithmetic, precision, and the true-decode graph remain unchanged.

This branch is stacked on the served integration and lab #686, which only packs the local padded projection input. The graph-classification fix is independent of that packing optimization.

Validation

/opt/venv/bin/python -c '
from vllm.platforms import current_platform
current_platform.__class__._global_graph_pool = (0, 0)
import pytest
raise SystemExit(pytest.main([
    "-q", "-p", "no:cacheprovider",
    "tests/v1/cudagraph/test_cudagraph_manager.py",
    "tests/v1/spec_decode/test_dynamic_sd_cug.py",
]))'
# 19 passed. The inert graph-pool handle makes these CPU dispatch tests
# independent of the CUDA-only serving image platform discovery.

The tests cover four-token prefill versus identical-shaped decode, varlen FULL candidates, generic mixed FULL compatibility, eager fallback, and data-parallel agreement. Pre-commit and mypy passed. The CUDA API hook was skipped only for an unchanged diagnostic torch.cuda.synchronize() call outside the changed path.

Post-fix serving at the unchanged nine-GPU configuration passes all 36 short-boundary checks plus six incident-length neighbor checks. In particular, 4,612, 9,220 and 115,204-token prompts now return the exact JSON on both cold and cached runs. Another 16 semantic checks pass across cold, GPU-prefix, external LMCache restore and concurrent requests. Existing functional, vision, cache-resume and 220k prefill regressions pass. No repetition penalty or output truncation guard is used as a substitute for the correction.

The fixed source boots in a fresh cache namespace. Historical entries are retained without being reused for qualification. Twelve further checks pass for exact four-token tails with reasoning enabled, external restoration and simultaneous requests. At the incident length, the external path restored 115,200 tokens and correctly processed the final four prompt tokens.

The final llm_decode_bench.py cc=1 run over 0/8k/16k/32k/64k/128k completed at 2026-09-07 04:03 UTC. Compared with the same-day pre-change baseline, the combined fixed build with #686 gives decode steps/s +0.6–1.1%, and prefill -0.5–0.7%. No competing or queued requests were recorded. These are whole-build measurements, not isolated speed claims for this correctness fix. An opt-in serving regression is preserved in tests/models/kimi_k3/test_prefill_tail_serving.py. Additional CPU tests execute the real v2 runner through graph dispatch to cover active-slot mapping, inactive slots, dummy capture and completed prompts.

Duplicate-work check

Open-PR searches covered short-prefill graph dispatch in both lab and upstream repositories. Upstream vllm-project#51483 changes Kimi KDA metadata for stateless first chunks. It does not change model-runner-v2 graph selection, which happens before that metadata is consumed. The v1 runner already checks prefill progress, but the active v2 runner did not.

Written, investigated, tested and reviewed with Claude assistance. Private request contents are not included in this PR.

Publication status

Published as a non-draft on 2026-09-07 at the operator’s explicit request to expose all prepared PRs in local-inference-lab. Existing qualification evidence and limitations above are unchanged. No code, deployment configuration, merge approval or automatic merge is changed by this status update.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KxvNwugeU8RJFd7WRYwNLG

voipmonitor and others added 30 commits August 12, 2026 13:46
Record scheduler-side speculative widths in GrammarOutput so worker-side draft trimming cannot shift flattened grammar masks onto later requests. Destination logits continue to use the worker-visible width, while source offsets use the serialized scheduler width.

Validated with focused unit coverage and a 160-request concurrent DeepSeek V4 structured-output workload.
KimiK3ToolParser.extract_tool_calls_streaming matched calls with
_call_re, which requires the closing <|close|>call<|sep|> marker. Until
that marker arrived nothing was emitted for the call, so a long tool
call produced no SSE deltas for the whole generation and then dumped
the entire arguments JSON in one delta.

Track the call from its <|open|>call ...<|sep|> marker instead. The
name goes out immediately, and _partial_arguments serializes the
arguments seen so far as a prefix of the final JSON, so each step can
stream the difference against what it already sent. String argument
bodies are raw text, so they are forwarded as they arrive with a
trailing partial close marker held back; other types still need the
whole literal to decode and are held until their block closes.

The concatenated deltas are byte-identical to the non-streaming
extract_tool_calls output.

Signed-off-by: guptaishaan <guptaishaan@users.noreply.github.com>
Withhold whitespace-tolerant argument-close fragments until they form a complete XTML marker. This keeps streamed JSON argument deltas prefix-stable for every marker form accepted by the parser.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Codex <codex@openai.com>
Document the target model input and optional NeoX layout result using the repository's Google-style docstring contract. This is documentation-only and does not change runtime behavior.

Co-authored-by: OpenAI Codex <codex@openai.com>
Initialize fresh assistant generations in the reasoning channel when Kimi thinking is enabled, while preserving rendered marker state for continued assistant messages.

Filter complete and split XTML control markers at the composed parser boundary so malformed model transitions cannot expose protocol syntax as API content. The thinking-disabled path and continuation semantics remain unchanged.

Validation: 72 Kimi K3 reasoning and tool-parser tests; Ruff format and lint; git diff whitespace validation.
Signed-off-by: jungjiyu <libraryofjiyu@gmail.com>
Assisted-by: ChatGPT
Model a 17-group hybrid KV layout and report a load failure from the final group. The test requires failure_policy=fail to finish only the affected request, emit an error result, and schedule a subsequent healthy request.\n\nValidation: 20 KV load-failure tests and 7 hybrid/Mamba scheduler tests pass in the CUDA 13.3 PyTorch 2.13 runtime.
Stop accepting speculative token batches when the grammar matcher reaches its terminal state. Preserve terminal-state tracking across validation and acceptance calls so tokens after a complete structured value cannot be committed.

This is the Infernal Invocation backport of vllm-project#52805 commits d8cde608cf1f3de406c75f081a76a0e6eb55a9cb, 1cf6f25351357354cf8c520c0b2976b029429668, and 1856abd22452c3da67364986ece7245fce52c950.

Signed-off-by: Martin Vit <martin@voipmonitor.org>
Structured-output masks are prepared before speculative verification. An accepted block can cross reasoning activation or grammar termination, so its suffix may have been sampled under a grammar state that no longer applies at commit time.

Validate the accepted block without advancing the matcher, commit only its valid prefix, and roll scheduler accounting back for resampling. Preserve the unstructured and single-token fast paths, and report only committed draft tokens in speculative metrics.

Co-authored-by: Adam Moisa <adammoisa@gmail.com>

Assisted-by: OpenAI Codex
Signed-off-by: Martin Vit <martin@voipmonitor.org>
(cherry picked from commit fa0777f)
Signed-off-by: Martin Vit <martin@voipmonitor.org>
Infernal Invocation exposes prompt inspection through is_reasoning_end_for_prompt. Make the upstream structured-output regression fixture implement the branch contract so it exercises the production method instead of a stale mock interface.

Signed-off-by: Martin Vit <martin@voipmonitor.org>
Type the conditional Kimi compact-RoPE protection scope through the shared context-manager interface. Both the Kimi protection context and the no-op context retain their existing runtime behavior.

Signed-off-by: Martin Vit <martin@voipmonitor.org>
The debug branch initializes the event list before every sweep point. Assert that invariant after detaching the list from the model runner so static analysis can verify indexed event access. Profiling and warmup behavior are unchanged.

Signed-off-by: Martin Vit <martin@voipmonitor.org>
…DFlash aux state (vllm-project#50487)

Signed-off-by: Rahul Chalamala <22563365+rchalamala@users.noreply.github.com>
Co-authored-by: Janelle Cai <janelle.cai@modal.com>
(cherry picked from commit 03a8d0b)
Verify that disabled AttnRes capture returns before reading unavailable weights and that enabled capture selects both normalization and projection weights from the correct consumer. Document the capture interface parameters and return value.
Compute MoonViT rotary frequencies only for the image grid sizes present in each request instead of materializing the configured 512x512 ceiling. This reduces the measured first-image CUDA allocation peak from 340,018,176 bytes to 1,990,656 bytes for a 36x36 grid while preserving bit-identical CPU and CUDA output.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Martin Vit <martin@voipmonitor.org>
Project independent Kimi vision features separately so MXFP8/Marlin workspace scales with the largest image instead of the sum of all scheduled images. Preserve output order, shape, activation dtype, and numerical results while reducing the measured TP16 three-image transient peak by 32.52 MiB.

Co-authored-by: OpenAI Codex <codex@openai.com>

Signed-off-by: Martin Vit <martin@voipmonitor.org>
Define token-position DCP shard count on each cache specification and use max_num_blocks_per_req as the worker block-table width contract. Attention caches retain full, partial, or replicated DCP layouts; recurrent caches report one token-position shard and preserve their mode-specific table width.

This removes the model runner's cache-type special case while retaining the 1,310-column Mamba align table required by a 1,000,000-token model length with 768-token blocks and seven speculative blocks.

Assisted-by: OpenAI Codex <noreply@openai.com>

Signed-off-by: Martin Vit <martin@voipmonitor.org>
Signed-off-by: Martin Vit <martin@voipmonitor.org>
Gather each tensor-parallel vision shard at its produced row count instead of padding every rank to the largest shard. This preserves embedding order and the uniform-size fast path while preventing the transient allocation from scaling with TP size when a request contains fewer images than ranks.

Validate zero-length PyNccl inputs, single-image output parity, empty inputs, uneven four-GPU assignments, and multi-image assignments. A TP16 Kimi-K3-shaped harness reduces the collective output from 224 MiB to 14 MiB per GPU with bit-exact gathered content.

Signed-off-by: Martin Vit <martin@voipmonitor.org>
Signed-off-by: Martin Vit <martin@voipmonitor.org>
Cache each head's prefix and suffix log-sum-exp values before any output write when the thread group fits inside a CUDA block. This preserves chunked-attention accumulators that pass the running LSE tensor as both prefix input and output destination, while retaining the direct-load path for head groups that cross block boundaries. Index all cached values through the declared tensor strides.\n\nAdd exact in-place versus disjoint-output coverage for the six-head, 128-element MLA geometry at 256 and 4096 tokens.\n\nThe shared-memory loading structure adapts vLLM PR vllm-project#45778 (commit c71576f) to the strided-LSE kernel contract.\n\nCo-authored-by: nicole-lihui <nicole.li@daocloud.io>

Signed-off-by: Martin Vit <martin@voipmonitor.org>
codex and others added 22 commits September 5, 2026 11:06
Describe TP-owned KV heads retaining window positions under target DCP and fixed-address context buffers captured by supported draft configurations.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Codex <codex@openai.com>
The draft receives concatenated target features before its row-sharded projection slices the input. Check the global source width for TP9 while preserving local matrix dimensions. Exercise combine_hidden_states for batched and single-token inputs and reject incompatible feature counts.

Validation: both TP9 projection tests pass through the model combination method, including all nine aux extents.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Codex <codex@openai.com>
`_B12X_DCP_WORLD_SIZES` gains 9 so the decode query-head gather, LSE
reduce-scatter and Kimi projection pair gather use the B12X PCIe IPC pool
at DCP9 instead of the NCCL fallbacks. The B12X kernel accepts nine ranks
(`b12x.comm.pcie.pcie_dcp_a2a.SUPPORTED_WORLD_SIZES`) and the Kimi-K3 TP9
head padding (99 MLA heads, eleven per rank) satisfies its divisibility
contract; pool initialization still falls back to NCCL on any rank failure.

Scope: with VLLM_USE_B12X_DCP_A2A=1 every MLA layer's DCP exchange and
every MoE layer's router/latent gather at DCP9 leave the NCCL path. The
fused Kimi top-k selection stays unavailable at nine ranks and uses the
ordinary paired gather plus grouped top-k.

Validation: tests/distributed/test_dcp_a2a.py::test_b12x_dcp_world_size_gate
covers 8 and 9 (pool reached) and 6 (declined); it needs a CUDA device and
has not run yet. Nine-GPU serving effect is unmeasured.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
`VLLM_PCIE_TWOSHOT_ALLREDUCE_MODE` (`pull`, the default, or `push`) is
applied to the b12x `PCIeTwoShotBF16` runtime before its graph launchers
are prepared, and the two-shot window log line reports the mode. A b12x
runtime without `all_reduce_mode` rejects a non-default request instead of
silently using the pull kernel.

Scope: with `VLLM_PCIE_ONESHOT_ALLREDUCE_MAX_SIZE` and the fused
add-RMSNorm limit lowered below the decode payload sizes, every decode
all-reduce takes the two-shot push kernel; the fused add-RMSNorm call
falls back to its unfused form above that limit.

Validation: syntax and import structure checked; serving effect at TP9
is measured by the TP9 qualification boot.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
…ment

`try_dcp_b12x_all_gather_pair_kimi_topk` returns None when the per-rank
latent or router width does not form whole 16-byte packs, which is the
case at nine ranks (398 bf16 and 99 fp32 elements). The B12X pool raised
`paired row widths must be multiples of 16 bytes` from the kernel warmup
and aborted the boot; callers now take the ordinary paired gather and
router as they do for other unsupported shapes.

Validation: the TP9 boot that hit the warmup failure is rerun by the
qualification script; the eight-rank path is unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
`kimi_projection_shard_width` rounds each rank's slice of a padded Kimi
column-parallel projection up to a multiple of eight elements, and the
B12X projection-gather warmup builds its sample rows with the same rule.
The B12X paired gather moves 16-byte packs, so at nine ranks the latent
(3584 -> 400 per rank) and router (896 -> 104 per rank) projections now
use the PCIe IPC pool instead of two NCCL all-gathers per MoE layer; the
gathered tensors are still sliced to their logical widths. The fused
gather-plus-top-k variant keeps its exact-width contract and stays on
the ordinary paired gather at nine ranks.

Compatibility: TP8 shards (448 and 112) are already aligned, so weights,
gathers and outputs are unchanged there. mypy findings at model.py lines
2303-2350 predate this change and are unrelated.

Validation: tests/models/kimi_k3/test_tp_projection.py (18 passed, CPU)
covers the width rule at TP8 and TP9; the nine-rank serving effect is
measured by the TP9 qualification boot.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
`_try_b12x_dcp_all_gather_heads` gathers into a packed intermediate and
copies into `out` when the caller-owned output is not contiguous. The
B12X gather kernel writes a packed [batch, heads, head_dim] result, and
the Kimi-K3 dense MLA decode path at nine DCP ranks hands it a 99-head
view of its 104-head query tile, which the kernel rejected with `output
must be contiguous` during CUDA graph capture.

Compatibility: contiguous outputs (eight DCP ranks: 96 heads in a
96-head tile) keep the direct in-place gather.

Validation: exercised by the TP9 qualification boot; no unit test yet
because the path needs a CUDA device and a B12X pool.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
`KimiK3PrefillProjectionWorkspace.get` returns None, with a one-time
warning, when a context chunk has more rows than the retained buffer, so
`project_context` falls back to the allocating projection instead of
raising. The buffer is reserved when weights are loaded with the cache
block size known then; the attention backend aligns its chunked-context
workspace to the final block size and DCP geometry, which at DCP9 rounds
`VLLM_MLA_INTERNAL_CONTEXT_WORKSPACE_SIZE=24576` up to 27,648 rows while
the buffer holds 24,624. Four concurrent 60k-token prefills killed the
TP9 server on that mismatch (`context projection needs 27648 rows, but
the retained workspace has 24624`).

Compatibility: TP8/DCP8 sizes coincide (24,576 both ways), so the
retained path is unchanged there. The `import re` lint finding at line 33
predates this change.

Validation: the TP9 serve script also sets the workspace to 27,648 so the
retained path stays in use; the fallback is exercised by the TP9
qualification runs.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
The hybrid coordinator enables fine-grained prefix hits whenever an
aligned Mamba group exists and lifts the hit alignment to the least
common multiple of the hash unit and every aligned Mamba block size. The
full-attention manager probes interior boundaries at hash granularity and
reports the longest boundary that is a multiple of that alignment. With a
1,536-token hash unit and a 1,536-token Mamba block (TP8) this is the
previous behaviour; with a 4,608-token Mamba block (TP9/DCP9) local hits
now land on 4,608-token boundaries instead of being disabled, which had
forced every repeated prompt shorter than the 13,824-token scheduler
block through an external LMCache retrieve (0 local hits, 100% external
hits on the TP9 validation server).

Validation: tests/v1/core/test_prefix_caching.py,
test_mamba_align_chunk_split.py and prefix_cache/
test_partial_prefix_cache_primitives.py: 123 passed (CPU). The TP9 serving
effect (local hit on the second 5,048-token probe) is checked by the
qualification run.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
Each single-type manager caches its reachable tails at
`cache_alignment_tokens`, which the hybrid coordinator sets to its
partial-hit alignment (the hash unit lifted to the Mamba block cadence).
Before, tails were aligned to the scheduler block, so a replicated
sliding-window draft group with 1,536-token blocks under a 13,824-token
scheduler block cached only the last blocks of each scheduler segment and
no lookup shorter than a scheduler block could hit in that group, which
made the hybrid hit zero even though the attention and Mamba groups had
cached the prefix.

Validation: tests/v1/core/test_prefix_caching.py gains
test_hybrid_partial_hits_align_to_mamba_block_under_dcp (16-token hash
unit, 48-token aligned Mamba block, 64x3-token DCP attention block,
replicated 16-token sliding-window group: a 96-token replay hits 96 in
all three groups); test_prefix_caching.py, test_mamba_align_chunk_split.py
and prefix_cache/test_partial_prefix_cache_primitives.py pass (CPU). The
test_kv_cache_utils.py cases that download Hugging Face configs are not
runnable offline and were not evaluated.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
The varlen tile scheduler sizes the grid from total_q + num_batch *
(tile_m - 1) query rows, so a batch whose sequence lengths do not fill
whole query tiles (a padded zero-length request, a prefix-hit tail next to
a fresh request, two chunked requests with tile remainders) gets extra CTAs
whose work tile maps to batch index num_batch with is_valid_tile False.
The SM80/SM120 kernel derived those CTAs' sequence lengths from
cu_seqlens[num_batch + 1], one element past the tensor, and then loaded K/V
tiles and stored O rows for the garbage length: out-of-bounds reads past K/V
(a CUDA illegal address when the offset leaves mapped memory) and writes
past the end of the attention output. The SM100 kernel already skips
invalid tiles; this kernel now does the same before touching global
memory. Valid tiles compute exactly as before.

Scope: vllm/vllm_flash_attn/cute/flash_fwd.py is the copy of
vllm-project/flash-attention 7484d475 flash_attn/cute/flash_fwd.py that the
vLLM build installs (untracked in this tree, force-added as an override for
the serving overlay); the same defect exists upstream. Kimi-K3 dense MLA
prefill on SM120 (VLLM_MLA_SM120_FA4_PREFILL=1) runs this kernel for every
prefill batch, at TP8 and TP9.

Validation: tests/kernels/attention/test_fa4_sm120_varlen_padding.py plants
2^30 after cu_seqlens and a NaN canary after out for batches [3615, 0],
[1055, 744], [1057, 111], [744, 1055] (11 heads, qk 192, v 128, bf16,
causal): 4 passed with this kernel; the image copy fails all 4 by writing
past the output. TP9/DCP9 serving reproduced the fault deterministically
(GPU core dump: FlashAttentionForwardSm120 grid 330, faulting CTAs 319-329,
LDGSTS at the K/V load) with a 12,831-token prompt replayed twice from a
9,216-token prefix hit next to a 927-token request.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
…ranks

VLLM_PCIE_TWOSHOT_ALLREDUCE_MODE accepts island9 in addition to pull and
push. At nine ranks it constructs b12x's PCIeIsland9AllReduce (ranks 0-3 and
4-7 reduce quarters inside their PCIe switch clusters, rank 8 on the CPU root
contributes through island 0, fp32 island partials, one bf16 rounding) for
the payload window above the one-shot ceiling and below the DMA ring, with
the same capacity vocabulary (row_elems x max_rows) and dispatch as the
two-shot. Other world sizes reject the mode at initialization.

Validation: the runtime is exercised by b12x tests/comm/test_pcie_tp9_physical.py
and benchmarks/{benchmark,precision}_pcie_tp9_allreduce.py on nine idle
GPUs (pending the next maintenance window). Serving selection follows the
measured latency and the precision comparison against the two-shot.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
…se format

The kquant serialized-MXFP8 dense format (dense_format "mxfp8" with the
checkpoint's ignored_layers) describes the language model only. The vision
tower and multimodal projector are stored in bf16 without MXFP8 scales, so
selecting the quantized linear method for them made the loader fail with
"serialized quantization parameters were not initialized from checkpoint:
mm_projector.linear_1.weight_scale, vision_tower.encoder.blocks.0.mlp.fc0
.weight_scale, ...". The multimodal wrapper now drops the quantization
config for those modules when the dense format is mxfp8, as it already does
for compressed-tensors configs; the language model is unchanged.

Validation: Kimi-K3 TP9/DCP9 boot with the image encoder enabled
(--limit-mm-per-prompt {"image":5}) on the QSRT-K2 checkpoint, whose
safetensors index carries 168 vision keys and no scale tensor; before the
change the boot failed at weight loading, after it the encoder loads and the
image probe (research/tp9-colocated-qsrt-20260905/vision_probe.py) runs
(result recorded in the maintenance window evidence).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
When the vision tower's head count does not divide the tensor-parallel
size, vLLM replicates the tower on every rank and shards a batch by
image, so a request with one large image runs the whole encoder on a
single rank while the others wait: on the Kimi-K3 TP9 target a
40,000-patch image costs 1.09 s of encoder time on rank 0, 0.90 s of it
in the 27 self-attention kernels (FlashAttention 2, 12 heads x 128).

vllm/model_executor/models/vit_query_shard.py plans a query split of
that replicated forward: patch embedding, norms, projections and MLPs
stay replicated (every rank computes the identical full tensors) and
each rank runs the varlen attention for its share of every image's
128-row query tiles against the image's complete keys and values; the
shares are all-gathered in the TP group and restored to packed row
order (one query segment per image, empty where a rank has no rows, so
segment b pairs with key segment b). FlashAttention computes every query
row independently over the key blocks in a fixed order, so the gathered
output equals the unsharded computation bit for bit; only the row-to-GPU
assignment changes. MoonViT's encoder layer takes the plan when it is
active and the FLASH_ATTN backend is in use; vision_tower_forward
activates it on the data-parallel path for batches of at least
VLLM_VIT_QUERY_SHARD_MIN_PATCHES patches (default 8192; below that the
gather cost exceeds the attention saved) unless VLLM_VIT_QUERY_SHARD=0.
MMEncoderAttention exposes its FlashAttention version as fa_version.

Every rank now materializes the full encoder activations of a batch
(previously only the rank that owned the image), which raises the other
ranks' transient to the owning rank's level.

Validation: tests/models/multimodal/test_vit_query_shard.py (29 cases:
tile-aligned single coverage of every row, balanced tiles, gather order
for one and several images, ranks without rows) passes in the serving
image; a nine-rank CPU run with stand-in attention and gathers matches
the unsharded path to bf16 precision for one and three images
(research/tp9-colocated-qsrt-20260905 scratch check); the nine-GPU
bit-identity and timing check is research/tp9-colocated-qsrt-20260905/
vit_query_shard_check.py, run at the maintenance window before the
option is served.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
…dings

The QSRT trellis prefill path binds the caller-owned scratch plan with
empty launch slots, so b12x's run_w4a16_moe resolves the fused MoE launch
on every layer call through compile_w4a16_fused_moe: tile selection,
W4A16FusedMoeKernel construction (two W4A16GemmKernel objects) and a
cache-key lookup, about 0.10 ms per call on an idle core and 4.5x that
under the with_stack profiler that produced the tail-chunk traces. The
compiled kernel is cached under a key that does not contain the row
count (only M == 1 and the small-M split-K band are specialized), so the
launch the plan compiled for the scheduler capacity at boot is the same
object every prefill row count resolves to.

_ensure_runtime now takes the prefill plan's prewarmed capacity fused
launch and the int32/mapped top-k sum launch once per layer state, and
_apply_once binds them into the binding (dataclasses.replace) for every
launch above the prefill route threshold. run_w4a16_moe then skips its
own resolution and validates the launch against the live contract, as
it does for the kept tier's preplanned launches. The decode plan keeps
lazy resolution, where the direct-route selection is dynamic.

Numerics: bit-identical. The kernel binary, the live row count, the
persistent grid (route-pack path: sms x blocks_per_sm) and the buffers
are unchanged; only the host-side derivation of the launch object is
skipped. VLLM_KQUANT_W4A16_PREPLANNED_LAUNCH=0 restores the lazy path
for an A/B comparison. Plans without prewarmed launches keep the lazy
path with a one-time warning.

Validation: CPU tests in the serving image (tests/quantization/
test_kquant_hybrid.py: launch selection, fallback, switch, and the
b12x cache key being identical for M in {2, 257, 830, 1536, 4608} and
different only for M = 1). GPU: logits digest with the switch on/off at
the next maintenance window.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
(cherry picked from commit 5b89012)
KimiPaddedRowParallelLinear splits its input axis into per-rank shards of
kimi_projection_shard_width columns (a multiple of eight elements; 400
for the 3,584-wide routed latent at TP9) instead of the ceiling division
(399), and its prefill entry points forward_into / accumulate_into read
the rank's K window as a strided view of the full-width latent, skipping
the last rank's zero-filled weight tail (384 real columns at TP9) rather
than padding the input.

Reason: with 399-column shards the bf16 operands of the 4,608-token
prefill up-projection (K=399, N=7,168 per rank) have a 798-byte row
pitch and, on odd ranks, 2-byte-aligned window starts, so cuBLAS falls
back to an align-1 SM75 tensor-op kernel: 202 us and 130 TFLOP/s per
MoE layer on the served TP9 stack (rank-0 trace, host frame
model.py forward_into), against 380-450 TFLOP/s for the other dense
projections of the same chunk. 400-column shards give every rank a
16-byte-aligned window start and row pitch and a K that is a multiple
of eight, which admits the vectorized SM120 kernels. The strided view
also removes the per-layer pad and shard-copy kernels (three elementwise
launches, ~39 us) that materialized the contiguous shard.

Compatibility: TP sizes that divide the latent width into 8-aligned
shards (TP8: 448 columns) keep their partition, operands and results;
their only change is the copy-free view. Decode keeps forward(), which
zero-pads the input to the same 400-column split. Weight loading needs
no change: the loader already zero-fills the last rank's tail from the
parameter shape.

Numerics: not bit-identical where the shard width changes (TP9). Each
rank's fp32 partial sum covers a different K range (400-column instead
of 399-column blocks) and a different kernel may order the reduction
differently, so the bf16 partials and the all-reduced sum can differ in
the last bit. Same precision class (bf16 operands, fp32 accumulation,
one bf16 rounding per rank); deployable only after the window
measurement against an fp64 reference shows equal or lower error
(research/prefill-campaign-20260906/evidence/item9/up_proj_alignment_probe.py).

Validation: CPU tests in the serving image (tests/models/kimi_k3/
test_mlp_tensor_lifetime.py: 16-byte-aligned copy-free windows on all
nine ranks, nine partials summing to the full projection with a
zero-filled last-rank tail, accumulate_into adding the same partial);
existing routed-output-transform and loader tests unchanged. The mypy
hook is skipped: it reports five pre-existing union-attr errors in the
unrelated auxiliary-stream code of this file on the base revision.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
(cherry picked from commit d8c5e5d)
VLLM_K3_DUMP_TOPK_IDS=<dir> makes the hybrid QSRT expert path (TP rank 0
only) save the routed expert ids of the first VLLM_K3_DUMP_TOPK_IDS_CHUNKS
(default 16) prefill launches of every MoE layer as
<dir>/layer{L}_chunk{c}_m{tokens}.pt holding {"topk_ids": int32
[tokens, top_k], "layer": int, "num_tokens": int}. Decode launches
(m <= 8) are not recorded. The variable is unset by default and the hook
then costs one environment lookup per layer call.

Reason: the W4A16 prefill route-block choice (48 vs 64 rows) depends on
the per-expert row histogram of real traffic, which synthetic uniform
routing cannot reproduce; the b12x extent harness replays these files
(--topk-ids) to measure the block-size trade-off on the served
distribution.

Compatibility: no behavior change while the variable is unset. When set,
the device-to-host copy synchronizes the stream once per recorded
launch; diagnostic runs only.

Validation: CPU test in the serving image (tests/quantization/
test_kquant_hybrid.py::test_topk_id_dump_writes_prefill_chunks_on_rank_zero:
file names, payload dtype/shape, chunk cap, rank filter).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
(cherry picked from commit f17a7bf)
With VLLM_K3_RING_STATIC_IO=1 (default off) the in-place prefill
all-reduces of a Kimi-K3 layer return the B12X DMA ring's static output
instead of a copy: the attention-output reduction
(tp_projection.reduce_kimi_full_width_projection) and the MoE runner's
two in-place reductions (routed latent before the output transform, final
output) pass borrow_output=True through
tensor_model_parallel_all_reduce_in_place -> GroupCoordinator ->
CudaCommunicator -> CustomAllreduce -> PCIeDmaAllReduce.all_reduce. The
ring's lossless replay entry is in place (one buffer is static input and
output), and the layer already chains one buffer through pre-attention
AttnRes output -> attention caller output -> o_proj out= -> in-place
reduction -> post-attention AttnRes output -> MoE up_proj out= -> in-place
reduction, so once a reduction is borrowed every later reduction of that
shape reads its input from the static buffer and returns it: the two
66 MB device copies per replayed reduction (about 18 ms per 4,608-token
chunk on rank 0) disappear.

A borrowed tensor is overwritten by the next reduction of the same
numel/dtype, so the two places that retain a reduction longer copy it
out: the AttnRes block-write layers, whose attention-output reduction
becomes the retained prefix (materialize_kimi_reduction), and the model
output, where the final AttnRes no longer reuses a borrowed last-layer
MoE output as its storage. Every other consumer (post-attention AttnRes,
the MoE front end, the latent RMSNorm and up-projection, the next layer's
pre-attention AttnRes, the auxiliary-state capture) reads the tensor
before that reduction. The keyword is forwarded only when set, so
communicators and call sites without the gate keep their exact calls.

Compatibility: no behavior change with the gate off; other models and
the functional all-reduce never borrow. Numerics: bit-identical (the ring
replays the same kernels; only staging copies are removed).

Validation: CPU tests in the serving image with --noconftest
(tests/models/kimi_k3/test_ring_static_io.py: the keyword reaches the
ring and the runner only when enabled, materialization copies only
borrowed storage, block-write layers copy the prefix while other layers
consume the reduction in place; tests/models/kimi_k3/test_tp_projection.py
and test_mlp_tensor_lifetime.py unchanged). tests/distributed/
test_inplace_allreduce.py's two CudaCommunicator cases fail on the base
revision as well (they predate the ca_comm dispatch). The mypy hook is
skipped: it reports five pre-existing union-attr errors in the unrelated
auxiliary-stream code of model.py. GPU: same-boot greedy fingerprint with
the gate on and off at the next maintenance window.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
(cherry picked from commit 64017bc)
With VLLM_K3_PROJECTION_GATHER=dma_pair (default nccl) a prefill MoE
layer (>= 1,024 tokens, TP-sharded auxiliary projections, latent MoE, no
fused tail) gathers its fp32 router-logit shard ([rows, 104] at TP9) and
bf16 routed-latent shard ([rows, 400]) with one PCIeDmaAllReduce.
all_gather_pair issued on the ring's side stream
(CustomAllreduce.pcie_dma_all_gather_pair, reached through
tensor_model_parallel_pcie_all_gather_pair -> GroupCoordinator ->
CudaCommunicator). The copy engine carries both blocks as one 8/9-loaded
ring pass while the main stream runs the shared experts, using the same
layer call the runner would make (writing into the consumed input when
the runner would); the runner then adopts that result
(SharedExperts.install_precomputed_output, MoERunner.forward(
shared_output=...)), waits for the gather, and assembles the logical
[rows, 896] and [rows, 3584] tensors from the rank-major blocks in one
pass each. The served path serializes two PyNCCL all-gathers (2.2 ms per
layer at 4,608 tokens, SM-occupying) before the shared experts and top-k
(0.85 ms); the ring pass takes about 1.6 ms and the shared experts hide
under it. When the ring declines a call (decode-sized, graph capture, no
ring) the layer falls back to the NCCL gathers of the same local shards.

Compatibility: no behavior change with the gate off; decode keeps its
paired B12X projection gather. Numerics: bit-identical (an all-gather
only copies; the GEMMs and the shared experts see identical operands,
only the issue order changes).

Validation: CPU tests in the serving image with --noconftest
(tests/models/kimi_k3/test_projection_gather_pair.py: block assembly
equals concatenation byte for byte for the 104/896 fp32 and 400/3584
bf16 shapes, the gate and the decode-size fallback, the pending gather's
wait assembles the ring outputs, the NCCL fallback, the shared experts
adopt a precomputed output and require aliasing under input reuse, and
KimiMoE.forward issues the gather, runs the shared experts, then waits;
test_ring_static_io.py, test_tp_projection.py and
test_mlp_tensor_lifetime.py unchanged). The mypy hook is skipped for the
five pre-existing union-attr errors in model.py. GPU: nine-rank physical
gather test, graph-replay mix with the ring all-reduce, and the same-boot
greedy fingerprint at the next maintenance window.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
(cherry picked from commit 3df9d9a)
…on shard

With VLLM_K3_LATENT_REDUCE=rs_fp32 or rs_bf16 (default allreduce) a
prefill MoE layer (>= 1,024 tokens, TP-sharded up-projection, no latent
capture) reduces its TP-partial routed latent ([rows, 3584] bf16 at
TP9) with PCIeDmaAllReduce.reduce_scatter_columns instead of the 33 MB
all-reduce: the MoE runner asks the output transform for
reduce_scatter_tp_partial, KimiRoutedOutputTransform hands the partial
to the ring with the up-projection's shard width (400 columns; the last
rank owns 384) through tensor_model_parallel_pcie_reduce_scatter_columns
-> GroupCoordinator -> CudaCommunicator -> CustomAllreduce, and the
transform continues on the returned [rows, 400] column block. The
RMSNorm variance comes from each rank's fp64 sum of squares of its block
combined by an fp64 all-reduce ([rows, 1], 37 KB over PyNCCL), the block
is scaled by rsqrt(variance + eps) and its slice of the norm weight in
fp32 and rounded to bf16 once, and KimiPaddedRowParallelLinear consumes
it as its input shard (local_block_shard: the last rank skips its 16
zero columns as it skips the zero-filled weight tail) through
forward_local_block, forward_into(x_is_local_block=True) or
accumulate_into(x_is_local_block=True). KimiMoE compiles the ring's
reduce-scatter kernels at construction, ahead of the kernel-resolution
freeze and graph capture. When the ring declines a call the layer
keeps the all-reduce.

Bytes per layer: 1.67 N link bytes (rs_fp32) or 0.89 N (rs_bf16) instead
of the all-reduce's 1.78 N, and the row-parallel up-projection reads
only its shard. Numerics: not bit-identical. rs_fp32 rounds each latent
element once (the served ring rounds after each of eight hops), and the
column-block RMSNorm uses fp64 partial sums and a single rounding where
the full-width kernel uses an fp32 block reduction and two roundings;
the up-projection GEMM reads a contiguous [rows, 400] block (row pitch
800 B) where the all-reduce path reads a strided window of the
full-width latent, so the kernel selection may differ within the same
precision class. rs_bf16 keeps eight roundings in column-block order
(candidate only). Deployment follows the campaign rule: only after the
precision harness (b12x benchmarks/precision_pcie_tp9_allreduce.py
--rows 4608 --width 3584 --norm) and the fp64 up-projection probe show
equal or lower error than the served path.

Compatibility: no behavior change with the gate off; the runner's
reduce hook returns a third value (column block) and passes
column_block to the transform only when set, so other transforms keep
their calls.

Validation: CPU tests in the serving image with --noconftest
(tests/models/kimi_k3/test_latent_reduce_scatter.py: the gate and the
decode-size fallback, the transform hook's capture and reduce_results
guards, nine emulated ranks' column-block RMSNorm within one bf16 ulp of
the fp64 reference and with no more mismatches and no larger relative L2
error than the served arithmetic, the up-projection's block entry points
producing the full-width window's projection on both ranks of a
two-way split with a padded last shard, and the runner preferring the
block and flagging it; the ring-side, gather-pair, in-place and
tensor-lifetime tests unchanged, 57 passing). The mypy hook is skipped
for the five pre-existing union-attr errors in model.py. GPU: the
nine-rank physical reduce-scatter test, the precision harness, the
up-projection probe and the same-boot fingerprint at the next
maintenance window.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aPCVZBsteYgs4PTAQYuE5

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
(cherry picked from commit 42d24bd)
VLLM_K3_DECODE_SHARD_PACK=1 avoids allocating, zero-filling and copying the
full routed latent on every TP rank before extracting a contiguous shard.
Interior ranks copy only their own columns. The tail rank packs valid
columns and zeros into the same contiguous padded K width in one Triton
launch. The normal GEMM dispatcher, weights, dimensions and arithmetic
remain unchanged. Prefill and unsupported shapes retain the existing path.

The tail pack is warmed for decode row counts before graph capture. CUDA
graph replay reuses captured addresses and needs no replay allocation.
The pack uses 64-bit row addressing and preserves payload bits, including
negative zero and NaN payloads. It never writes the source tensor.

Validation on RTX PRO 6000 Max-Q and Workstation Edition, SM120:
37 tests passed per GPU across test_mlp_tensor_lifetime.py,
test_vision_warmup.py and test_kimi_k3_triton_warmup.py. Actual projection
outputs match bitwise at TP9 ranks 0-8, rows 1/2/4/8/9/16; interleaved CUDA
graph diagnostics include changed inputs and stable output addresses.
At rows=4 projection graph latency is 0.749-0.750x control on Max-Q and
0.601-0.800x on Workstation Edition. Whole-model validation is pending.
Lint and pre-commit passed except five pre-existing optional-projector
union-attr errors in model.py; the mypy-3.10 hook is skipped for that
unchanged baseline defect. The added test's type annotation is fixed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxvNwugeU8RJFd7WRYwNLG

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
Model runner v2 matched full speculative-decode graphs using only request
and token counts. A prompt ending four tokens past a 4608-token boundary
therefore replayed the verifier graph even though KDA metadata classified
those four prompt tokens as prefill. The older runner checks prefill
progress, but v2 did not.

Derive has_prefill from scheduled requests' existing CPU prefill progress
before graph selection. Exclude uniform and bounded-verifier FULL
candidates when a request is prefilling, retaining piecewise, generic
mixed FULL, and eager fallbacks. Propagate the flag through DP agreement
so its second dispatch cannot promote a peer's prefill to a decode graph.
True decode graph selection, model arithmetic, and precision are unchanged.

Validation: 13 CPU graph-dispatch and dynamic-speculation tests pass with
an inert graph-pool stub in the CUDA serving image. They cover an exact
four-token tail, varlen decode candidates, mixed FULL compatibility, eager
fallback and DP agreement. Before the fix, the serving target corrupts
cold and cached prompts of 4612 and 9220 tokens; neighboring tails
1/2/3/5/8/9/16/53 return the exact expected JSON. The captured incident had
115204 = 25*4608+4 prompt tokens and repeated one sentence 21747 times.
The same short-tail prompt succeeds during eager profiling.
Whole-model post-fix qualification is pending. Pre-commit including mypy
passes; the CUDA API policy hook is skipped only for an existing diagnostic
synchronize call in model_runner.py, outside the changed path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxvNwugeU8RJFd7WRYwNLG

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 136 files, which is 36 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 16fef669-d135-4260-bad0-66b2365cb632

📥 Commits

Reviewing files that changed from the base of the PR and between b5f995e and 7ee6b95.

📒 Files selected for processing (136)
  • benchmarks/kernels/benchmark_kimi_decode_shard_pack.py
  • cmake/external_projects/flashkda.cmake
  • csrc/flashkda_registration.cpp
  • csrc/libtorch_stable/attention/merge_attn_states.cu
  • tests/config/test_dflash_tp9_projection.py
  • tests/config/test_virtual_tp_kimi_k3.py
  • tests/distributed/test_dcp_a2a.py
  • tests/distributed/test_flashinfer_pcie_all_reduce.py
  • tests/distributed/test_pynccl.py
  • tests/kernels/attention/test_fa4_sm120_varlen_padding.py
  • tests/kernels/attention/test_merge_attn_states.py
  • tests/models/kimi_k3/test_aux_attn_res_stream.py
  • tests/models/kimi_k3/test_eagle3.py
  • tests/models/kimi_k3/test_latent_reduce_scatter.py
  • tests/models/kimi_k3/test_mla_padding.py
  • tests/models/kimi_k3/test_mlp_tensor_lifetime.py
  • tests/models/kimi_k3/test_prefill_tail_serving.py
  • tests/models/kimi_k3/test_projection_gather_pair.py
  • tests/models/kimi_k3/test_ring_static_io.py
  • tests/models/kimi_k3/test_tp_projection.py
  • tests/models/kimi_k3/test_vision_projector.py
  • tests/models/kimi_k3/test_vision_warmup.py
  • tests/models/multimodal/test_vit_query_shard.py
  • tests/quantization/test_kquant_hybrid.py
  • tests/reasoning/test_kimi_k3_reasoning_parser.py
  • tests/tool_use/test_kimi_k3_tool_parser.py
  • tests/v1/attention/test_dcp_tp9_relay.py
  • tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py
  • tests/v1/core/test_dspark_prefix_cache_policy.py
  • tests/v1/core/test_kv_cache_utils.py
  • tests/v1/core/test_prefix_caching.py
  • tests/v1/core/test_scheduler.py
  • tests/v1/cudagraph/test_cudagraph_manager.py
  • tests/v1/kv_connector/unit/test_invalid_blocks_correctness.py
  • tests/v1/kv_connector/unit/test_lmcache_mp_dflash_tp9.py
  • tests/v1/kv_connector/unit/utils.py
  • tests/v1/spec_decode/test_acceptance_length_controller.py
  • tests/v1/spec_decode/test_dflash_causality.py
  • tests/v1/spec_decode/test_dflash_context_cudagraph.py
  • tests/v1/spec_decode/test_dflash_swa.py
  • tests/v1/spec_decode/test_dspark_cudagraph_contract.py
  • tests/v1/spec_decode/test_k3_dspark_remote_speculator.py
  • tests/v1/spec_decode/test_k3_dspark_standalone.py
  • tests/v1/spec_decode/test_mtp_structured_output.py
  • tests/v1/structured_output/test_reasoning_structured_output.py
  • tests/v1/structured_output/test_utils.py
  • tests/v1/worker/test_cp_utils.py
  • tests/v1/worker/test_gpu_structured_outputs.py
  • tests/v1/worker/test_mamba_hybrid_model_state.py
  • tests/v1/worker/test_mamba_utils.py
  • vllm/config/speculative.py
  • vllm/config/virtual_tp.py
  • vllm/config/vllm.py
  • vllm/distributed/communication_op.py
  • vllm/distributed/device_communicators/base_device_communicator.py
  • vllm/distributed/device_communicators/cuda_communicator.py
  • vllm/distributed/device_communicators/custom_all_reduce.py
  • vllm/distributed/device_communicators/flashinfer_pcie_all_reduce.py
  • vllm/distributed/kv_transfer/kv_connector/v1/lmcache_mp_connector.py
  • vllm/distributed/parallel_state.py
  • vllm/entrypoints/k3_dspark_rpc.py
  • vllm/entrypoints/k3_dspark_standalone.py
  • vllm/envs.py
  • vllm/model_executor/layers/attention/mla_attention.py
  • vllm/model_executor/layers/attention/mm_encoder_attention.py
  • vllm/model_executor/layers/attention/sparse_mla_attention.py
  • vllm/model_executor/layers/fused_moe/runner/moe_runner.py
  • vllm/model_executor/layers/fused_moe/runner/shared_experts.py
  • vllm/model_executor/layers/linear.py
  • vllm/model_executor/layers/mamba/ops/causal_conv1d.py
  • vllm/model_executor/layers/quantization/kquant_hybrid.py
  • vllm/model_executor/layers/quantization/kquant_qsrt_atoms_v2.py
  • vllm/model_executor/models/dflash_tp9.py
  • vllm/model_executor/models/kimi_k25_vit.py
  • vllm/model_executor/models/qwen3_dflash.py
  • vllm/model_executor/models/vision.py
  • vllm/model_executor/models/vit_query_shard.py
  • vllm/model_executor/warmup/kimi_k3_triton_warmup.py
  • vllm/models/kimi_k3/nvidia/dspark_mla.py
  • vllm/models/kimi_k3/nvidia/kda.py
  • vllm/models/kimi_k3/nvidia/kda_metadata.py
  • vllm/models/kimi_k3/nvidia/l2_prefetch.py
  • vllm/models/kimi_k3/nvidia/latent_moe_runner.py
  • vllm/models/kimi_k3/nvidia/mla.py
  • vllm/models/kimi_k3/nvidia/model.py
  • vllm/models/kimi_k3/nvidia/ops/fused_mla_key_concat_kv_cache.py
  • vllm/models/kimi_k3/nvidia/ops/projection_shard.py
  • vllm/models/kimi_k3/nvidia/tp_projection.py
  • vllm/parser/kimi_k3.py
  • vllm/reasoning/kimi_k3_reasoning_parser.py
  • vllm/tool_parsers/kimi_k3_tool_parser.py
  • vllm/v1/attention/backends/flash_attn.py
  • vllm/v1/attention/backends/mla/b12x_mla.py
  • vllm/v1/attention/backends/mla/prefill/aiter_flash_attn.py
  • vllm/v1/attention/backends/mla/prefill/base.py
  • vllm/v1/attention/backends/mla/prefill/flash_attn.py
  • vllm/v1/attention/backends/mla/prefill/flashinfer.py
  • vllm/v1/attention/backends/mla/prefill/tokenspeed_mla.py
  • vllm/v1/attention/backends/mla/prefill/trtllm_ragged.py
  • vllm/v1/attention/ops/dcp_alltoall.py
  • vllm/v1/attention/ops/dcp_utils.py
  • vllm/v1/core/block_pool.py
  • vllm/v1/core/kv_cache_coordinator.py
  • vllm/v1/core/kv_cache_manager.py
  • vllm/v1/core/kv_cache_utils.py
  • vllm/v1/core/sched/output.py
  • vllm/v1/core/sched/scheduler.py
  • vllm/v1/core/single_type_kv_cache_manager.py
  • vllm/v1/executor/multiproc_executor.py
  • vllm/v1/kv_cache_interface.py
  • vllm/v1/request.py
  • vllm/v1/structured_output/__init__.py
  • vllm/v1/structured_output/backend_xgrammar.py
  • vllm/v1/structured_output/utils.py
  • vllm/v1/worker/cp_utils.py
  • vllm/v1/worker/gpu/async_utils.py
  • vllm/v1/worker/gpu/buffer_utils.py
  • vllm/v1/worker/gpu/cudagraph_utils.py
  • vllm/v1/worker/gpu/dp_utils.py
  • vllm/v1/worker/gpu/input_batch.py
  • vllm/v1/worker/gpu/k3_ubatch_prefill.py
  • vllm/v1/worker/gpu/model_runner.py
  • vllm/v1/worker/gpu/model_states/mamba_hybrid.py
  • vllm/v1/worker/gpu/spec_decode/__init__.py
  • vllm/v1/worker/gpu/spec_decode/dflash/speculator.py
  • vllm/v1/worker/gpu/spec_decode/dflash/utils.py
  • vllm/v1/worker/gpu/spec_decode/dspark/p2p_transport.py
  • vllm/v1/worker/gpu/spec_decode/dspark/remote_speculator.py
  • vllm/v1/worker/gpu/spec_decode/dspark/utils.py
  • vllm/v1/worker/gpu/spec_decode/utils.py
  • vllm/v1/worker/gpu/structured_outputs.py
  • vllm/v1/worker/gpu/warmup.py
  • vllm/v1/worker/gpu_worker.py
  • vllm/v1/worker/mamba_utils.py
  • vllm/v1/worker/utils.py
  • vllm/vllm_flash_attn/cute/flash_fwd.py

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

g0san added 2 commits September 7, 2026 12:39
An opt-in test targets an explicitly supplied idle test server. It calibrates
prompt lengths with the tokenizer endpoint, checks completion usage for the
same count, and requires an exact JSON answer on cold and cached requests.
It covers prefill chunk boundaries plus the observed 115204-token incident
length. Requests use fresh salts and never execute tools or reset caches.

Validation: the equivalent local serving harness reproduced failures only
at four-token tails before the graph-dispatch fix. After the fix all 36
short-tail checks and six 115200-token-boundary neighbor checks pass,
including cold and cached prompts of exactly 115204 tokens.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxvNwugeU8RJFd7WRYwNLG

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
Execute the real runner until graph dispatch with minimal request state.
Assert that four-token tails at 4612 and 115204 tokens are marked prefill,
completed prompts remain decode, reordered mixed batches preserve the guard,
inactive slots cannot trigger it, and dummy capture keeps prior behavior.

Validation: 19 graph-manager, runner-entry and dynamic-speculation tests
pass in the serving image with an inert CPU graph-pool handle. The tests
introduce no runtime or serving source change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxvNwugeU8RJFd7WRYwNLG

Signed-off-by: g0san <hhg698pknv@privaterelay.appleid.com>
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.

8 participants