Skip to content

[https://nvbugs/6463967][fix] DeepSeek-V4 one-model MTP separate draft kv cache (TEP) - #16887

Open
nvxuanyuc wants to merge 1 commit into
NVIDIA:mainfrom
nvxuanyuc:dsv4aggfix
Open

[https://nvbugs/6463967][fix] DeepSeek-V4 one-model MTP separate draft kv cache (TEP)#16887
nvxuanyuc wants to merge 1 commit into
NVIDIA:mainfrom
nvxuanyuc:dsv4aggfix

Conversation

@nvxuanyuc

@nvxuanyuc nvxuanyuc commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds separate draft KV-cache support for DeepSeek-V4 sparse attention during one-model MTP under TEP.

  • Adds draft cache managers, sliding block tables, and pool base pointers for SWA-only draft layers.
  • Adds target-state save, draft-state apply, and target-state restore flows for eager execution and CUDA graph replay.
  • Adds draft replay lifecycle hooks to sparse attention metadata.
  • Disables separate draft KV cache for DeepSeek-V4 when PP > 1.
  • Adds GSM8K accuracy coverage and registers it in l0_dgx_b200.yml.
  • Scope is limited to SWA-only draft layers, PP=1, and beam_width=1.

Dev Engineer Review

  • The implementation separates target and draft sparse KV-cache state and restores target state through try/finally handling.
  • Unsupported DeepSeek-V4 configurations fail explicitly with NotImplementedError.
  • Generic attention metadata now exposes consistent draft replay hooks.
  • The changes add shared pointer construction and avoid separate-cache allocation for unsupported PP > 1 configurations.
  • The GSM8K reference includes the required FP8 KV-cache and MTP qualifiers.
  • CI passed on the third run. The first two runs required investigation.
  • No additional correctness or configuration issues are identified from the provided changes.

Verdict: sufficient

QA Engineer Review

Added test function:

  • TestDeepSeekV4Flash::test_tep_mtp_separate_draft_kv_cache
    • Uses GSM8K.
    • Covers TEP-style attention, MTP, CUDA graph padding, and FP8 KV cache.
    • Skips MPI world sizes below 4.

Coverage:

  • Registered in tests/integration/test_lists/test-db/l0_dgx_b200.yml with a 60-minute timeout.

Verdict: sufficient

Description

Wires DeepSeek-V4's sparse attention forward to the existing separate draft KV cache mechanism
for one-model MTP under TEP.

Design

  • A dedicated DeepseekV4CacheManager holds the MTP draft layers; the metadata carries
    draft-sized sliding block tables + draft pool base pointers, allocated separately.
  • The draft's _num_tables and precomputed sliding tables are populated in prepare() via
    draft_mgr.compute_sliding_block_tables(...), mirroring the target manager.
  • Target/draft state is swapped via save/apply/restore_target_sparse_state at both the
    eager draft_kv_cache_context and the CUDA-graph prepare_attn_metadata_for_draft_replay,
    gated by isinstance(attn_metadata, DeepseekV4TrtllmAttentionMetadata) (mirrors the DSA
    branch). The swap is a pointer rebind plus per-batch copy.
  • Scope: SWA-only draft layers (compress_ratio 1, asserted); PP=1 only (folds otherwise);
    beam_width=1 (pre-existing DSv4 + MTP constraint).

Validation Results

[DeepSeek-V4-Flash | TEP4 | fp8kv]

Separate vs unified draft KV

max_draft_len AL (separate) AL (unified) GSM8K (separate) GSM8K (unified)
1 1.9567 1.9609 94.84 94.92
2 2.6449 2.6496 95.19 94.92

Perf [nextn=1] ~1-2% decode-rate overhead

metric separate unified overhead%
BS1 ITL (ms) 3.876 3.825 +1.32%
throughput (tok/s) 7332.5 7468.7 +1.82%
steps/s 3843.97 3880.09 +0.93%

Test Coverage

TestDeepSeekV4Flash::test_tep_mtp_separate_draft_kv_cache (GSM8K), registered in
l0_dgx_b200.yml. Local run: 95.53.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

DeepSeek-V4 sparse attention now supports separate draft KV-cache buffers for one-model MTP. Draft replay snapshots and restores sparse metadata, DSA indexer state, and target mappings. Cache selection and B200 accuracy coverage are updated.

Changes

DeepSeek-V4 draft KV-cache support

Layer / File(s) Summary
Draft replay hook contract
tensorrt_llm/_torch/attention_backend/trtllm.py, tensorrt_llm/_torch/attention_backend/sparse/dsa.py
Attention metadata defines draft replay hooks. DSA metadata saves, updates, and restores indexer offsets and slot mappings.
Draft sparse buffer setup
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py
DeepSeek-V4 metadata builds shared target and draft pointer mappings, allocates draft buffers, validates draft layers, and prepares draft sliding-window tables.
Draft state replay and restoration
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py, tensorrt_llm/_torch/speculative/interface.py
Speculative replay and draft KV-cache contexts apply draft sparse state and restore target state after completion or failure.
Cache policy and integration coverage
tensorrt_llm/_torch/pyexecutor/_util.py, tests/integration/defs/accuracy/..., tests/integration/test_lists/test-db/l0_dgx_b200.yml
Separate draft cache creation is disabled for DeepSeek-V4 with pipeline parallelism greater than one. TEP MTP FP8 accuracy coverage is added.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SpeculativeInterface
  participant DeepseekV4TrtllmAttentionMetadata
  participant DSAtrtllmAttentionMetadata
  SpeculativeInterface->>DeepseekV4TrtllmAttentionMetadata: snapshot target sparse state
  SpeculativeInterface->>DeepseekV4TrtllmAttentionMetadata: apply draft sparse state
  DeepseekV4TrtllmAttentionMetadata->>DSAtrtllmAttentionMetadata: prepare DSA replay state
  SpeculativeInterface->>DeepseekV4TrtllmAttentionMetadata: restore target sparse state
  DeepseekV4TrtllmAttentionMetadata->>DSAtrtllmAttentionMetadata: restore DSA replay state
Loading

Possibly related PRs

Suggested labels: api-compatible

Suggested reviewers: qijune

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the required format and clearly identifies the DeepSeek-V4 one-model MTP separate draft KV-cache fix.
Description check ✅ Passed The description explains the change, design scope, validation results, and relevant test coverage, but omits the template checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py`:
- Around line 603-606: Modernize the annotations at all three sites: in
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py:603-606,
change the per_layer_compress_ratios property return type to list[int]; in
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py:653-761,
annotate helper parameters and returns, using None for mutating helpers and the
precise snapshot type for snapshot-returning helpers; in
tests/integration/defs/accuracy/test_llm_api_pytorch.py:3880, add -> None to the
test method.

In `@tensorrt_llm/_torch/speculative/interface.py`:
- Around line 196-204: Make the DeepseekV4TrtllmAttentionMetadata mutation in
prepare_attn_metadata_for_draft_replay exception-safe by wrapping
apply_draft_sparse_state() with restoration of the saved sparse state when it
raises. Restore the saved metadata before re-raising the original exception,
while preserving the existing saved-state behavior on success.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 78af6d9d-93bc-409f-919e-60efd609be8d

📥 Commits

Reviewing files that changed from the base of the PR and between 49e16c9 and 1c9fb3a.

📒 Files selected for processing (7)
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py
  • tensorrt_llm/_torch/pyexecutor/_util.py
  • tensorrt_llm/_torch/speculative/interface.py
  • tests/integration/defs/accuracy/references/gsm8k.yaml
  • tests/integration/defs/accuracy/test_llm_api_pytorch.py
  • tests/integration/test_lists/test-db/l0_dgx_b200.yml

Comment on lines +603 to +606
@property
def per_layer_compress_ratios(self) -> List[int]:
"""Per-layer compress ratios (MTP-extended), indexed by global layer."""
return self._compress_ratios

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required modern type annotations.

  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py#L603-L606: use list[int] instead of List[int].
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py#L653-L761: annotate helper arguments and return types; use None for mutating helpers and a precise snapshot type.
  • tests/integration/defs/accuracy/test_llm_api_pytorch.py#L3880-L3880: annotate the test method with -> None.
📍 Affects 3 files
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py#L603-L606 (this comment)
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py#L653-L761
  • tests/integration/defs/accuracy/test_llm_api_pytorch.py#L3880-L3880
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py`
around lines 603 - 606, Modernize the annotations at all three sites: in
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/cache_manager.py:603-606,
change the per_layer_compress_ratios property return type to list[int]; in
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py:653-761,
annotate helper parameters and returns, using None for mutating helpers and the
precise snapshot type for snapshot-returning helpers; in
tests/integration/defs/accuracy/test_llm_api_pytorch.py:3880, add -> None to the
test method.

Source: Coding guidelines

Comment thread tensorrt_llm/_torch/speculative/interface.py Outdated
@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62069 [ run ] triggered by Bot. Commit: 797c4a5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62069 [ run ] completed with state FAILURE. Commit: 797c4a5
/LLM/main/L0_MergeRequest_PR pipeline #50254 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62256 [ run ] triggered by Bot. Commit: 797c4a5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62256 [ run ] completed with state FAILURE. Commit: 797c4a5
/LLM/main/L0_MergeRequest_PR pipeline #50425 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62353 [ run ] triggered by Bot. Commit: 797c4a5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62353 [ run ] completed with state SUCCESS. Commit: 797c4a5
/LLM/main/L0_MergeRequest_PR pipeline #50520 completed with status: 'SUCCESS'

CI Report

Link to invocation

# Only SWA-only (compress_ratio 1) draft layers are supported; compress
# (128) / indexer (4) draft layers are intentionally unsupported.
draft_ratios = {
draft_mgr.per_layer_compress_ratios[layer_idx] for layer_idx in draft_mgr.pp_layers

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that you can directly use self.compress_ratios to get the list and don't need this per_layer_compress_ratios.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Given the MTP layers duplicate the final configured ratio, the guard now checks self.compress_ratios[-1] directly. And the metadata now locally extends self.compress_ratios for pointer construction, per_layer_compress_ratios helper has been removed.

# draft manager (the DSA block above only handles the indexer k-cache).
from ..attention_backend.sparse.deepseek_v4.deepseek_v4 import \
DeepseekV4TrtllmAttentionMetadata
if isinstance(attn_metadata, DeepseekV4TrtllmAttentionMetadata):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be better to add a prepare_for_draft_replay in deepseek_v4 metadata. And here we can get saved = attn_metadata.prepare_for_draft_replay.

Same for the above DSA metadata.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Added replay hooks to TrtllmAttentionMetadata, with the existing indexer preparation and restoration moved into the DSA metadata implementation. The DSv4 metadata implementation extends those hooks with its sparse-state apply and restore.

if isinstance(attn_metadata, DeepseekV4TrtllmAttentionMetadata):
saved_dsv4_state = attn_metadata.save_target_sparse_state()
try:
if saved_dsv4_state is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this part can be simplified to

# DeepSeek-V4: xxxxxxxxx
if hasattr(attn_metadata, "save_target_sparse_state"):
    saved_dsv4_state = attn_metadata.save_target_sparse_state()
    attn_metadata.apply_draft_sparse_state()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Replaced the type check with hasattr. The None guard remains because this context also serves non-MTP spec dec paths.

@nvpohanh
nvpohanh requested a review from liji-nv July 30, 2026 06:19
@nvpohanh

Copy link
Copy Markdown
Collaborator

[by Codex] @liji-nv Friendly review reminder: this PR is awaiting your review. Thanks!

@mikeiovine
mikeiovine requested a review from Mgluhovskoi July 31, 2026 15:50
…t kv cache (TEP)

Signed-off-by: Xuanyu Chen <xuanyuc@nvidia.com>
@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py (1)

791-799: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a single cached flag for the draft-support check.

The hasattr(draft_mgr, "compute_sliding_block_tables") check appears here and in _init_draft_sparse_buffers. The two gates must stay in agreement, because apply_draft_sparse_state copies tables that only this branch computes. Set one boolean in _init_draft_sparse_buffers (for example self._has_draft_sparse_state) and test it in both places.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py`
around lines 791 - 799, Introduce a cached draft-support boolean in
_init_draft_sparse_buffers based on whether draft_kv_cache_manager provides
compute_sliding_block_tables, then reuse that flag in this preparation block
instead of repeating hasattr. Ensure apply_draft_sparse_state and the
table-computation branch remain gated by the same flag.
tensorrt_llm/_torch/attention_backend/sparse/dsa.py (1)

717-730: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider persistent snapshot buffers instead of clone() per draft step.

prepare_for_draft_replay runs on every draft replay step. Each call allocates six new tensors, including two max_num_tokens int64 device buffers and their host counterparts. Allocate the snapshot buffers once and use copy_ into them. This removes per-step allocator traffic on a hot path and keeps addresses stable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/attention_backend/sparse/dsa.py` around lines 717 - 730,
Update the snapshot handling in prepare_for_draft_replay to use six persistent
buffers allocated once during initialization, rather than cloning the tensors on
every draft replay step. Copy each current buffer into its corresponding
persistent snapshot with copy_, preserving the existing saved keys and tensor
contents while keeping device and host buffer addresses stable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py`:
- Around line 791-799: Introduce a cached draft-support boolean in
_init_draft_sparse_buffers based on whether draft_kv_cache_manager provides
compute_sliding_block_tables, then reuse that flag in this preparation block
instead of repeating hasattr. Ensure apply_draft_sparse_state and the
table-computation branch remain gated by the same flag.

In `@tensorrt_llm/_torch/attention_backend/sparse/dsa.py`:
- Around line 717-730: Update the snapshot handling in prepare_for_draft_replay
to use six persistent buffers allocated once during initialization, rather than
cloning the tensors on every draft replay step. Copy each current buffer into
its corresponding persistent snapshot with copy_, preserving the existing saved
keys and tensor contents while keeping device and host buffer addresses stable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b1125ddd-fd86-4f7f-9530-0a7811ee864f

📥 Commits

Reviewing files that changed from the base of the PR and between 797c4a5 and e56cedb.

📒 Files selected for processing (3)
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py
  • tensorrt_llm/_torch/attention_backend/sparse/dsa.py
  • tensorrt_llm/_torch/attention_backend/trtllm.py

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63542 [ run ] triggered by Bot. Commit: e56cedb Link to invocation

@nvxuanyuc
nvxuanyuc requested a review from lfr-0531 August 3, 2026 23:15
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63542 [ run ] completed with state FAILURE. Commit: e56cedb
/LLM/main/L0_MergeRequest_PR pipeline #51508 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63580 [ run ] triggered by Bot. Commit: e56cedb Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63580 [ run ] completed with state FAILURE. Commit: e56cedb
/LLM/main/L0_MergeRequest_PR pipeline #51544 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63597 [ run ] triggered by Bot. Commit: e56cedb Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63597 [ run ] completed with state FAILURE. Commit: e56cedb
/LLM/main/L0_MergeRequest_PR pipeline #51558 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@nvxuanyuc

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63783 [ run ] triggered by Bot. Commit: e56cedb Link to invocation

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.

5 participants