Skip to content

fix: prepare DeepSeek-V4 draft sliding tables - #17178

Open
zcxGGmu wants to merge 1 commit into
NVIDIA:mainfrom
zcxGGmu:fix/dsv4-draft-sliding-tables
Open

fix: prepare DeepSeek-V4 draft sliding tables#17178
zcxGGmu wants to merge 1 commit into
NVIDIA:mainfrom
zcxGGmu:fix/dsv4-draft-sliding-tables

Conversation

@zcxGGmu

@zcxGGmu zcxGGmu commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • Prepare the DeepSeek-V4 draft KV cache manager sliding-window tables before the base metadata path copies draft block offsets.
  • Add a regression test that verifies draft sliding tables are computed before TrtllmAttentionMetadata.prepare() reaches copy_batch_block_offsets().

Fixes #17024

Test Plan

  • python -m ruff check tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
  • python -m py_compile tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
  • Source-level red/green check confirmed draft sliding-table preparation now occurs before base prepare.
  • Not run locally: targeted pytest, because this Windows checkout lacks a usable MPI runtime (impi.dll/msmpi.dll) and compiled TensorRT-LLM bindings.

Dev Engineer Review

  • DeepseekV4TrtllmAttentionMetadata.prepare now computes draft sliding-window block tables before the base metadata path copies draft block offsets.
  • The draft KV-cache manager remains optional. Existing target-only flows remain supported.
  • The change addresses the _num_tables initialization failure in DeepSeek-V4 speculative decoding.
  • No configuration files or test-list files changed.
  • Ruff, Python compilation, and source-order checks passed. Targeted pytest was not run because the environment lacks usable MPI and compiled TensorRT-LLM bindings.

QA Engineer Review

  • Added test_prepare_computes_sliding_tables_before_base_prepare.
  • The test verifies that target and draft cache managers receive request IDs and context count before base preparation runs.
  • No corresponding tests/integration/test_lists/, test-db/, or qa/ entry was added or modified.
  • Verdict: insufficient. The regression test is not listed for CI or manual QA coverage.

Signed-off-by: zq <zhouquan1511@163.com>
@zcxGGmu
zcxGGmu requested a review from a team as a code owner August 3, 2026 03:51
@zcxGGmu
zcxGGmu requested review from yunruis and yuxianq August 3, 2026 03:51
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

DeepSeek-V4 metadata preparation now computes sliding-window block tables for the target and optional draft KV-cache managers before base TRT-LLM preparation. A unit test verifies the calls, arguments, and ordering.

Changes

DeepSeek-V4 metadata preparation

Layer / File(s) Summary
Prepare target and draft sliding block tables
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py, tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
prepare invokes sliding block-table computation on the optional draft KV-cache manager before base preparation. The test verifies target and draft calls, arguments, and ordering.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: bowenfu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the DeepSeek-V4 fix for preparing draft sliding tables.
Description check ✅ Passed The description explains the issue, solution, regression test, validation steps, and test limitation.
Linked Issues check ✅ Passed The changes satisfy issue #17024 by preparing draft sliding tables before copying draft block offsets and adding regression coverage.
Out of Scope Changes check ✅ Passed The code and test changes are directly related to issue #17024 and contain no unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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
`@tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py`:
- Around line 26-55: Add
tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
to the appropriate CI and QA entries in
tests/integration/test_lists/test-db/l0_b200.yml so
test_prepare_computes_draft_sliding_block_tables_before_base_prepare is included
in automated coverage.
- Around line 26-43: Update
test_prepare_computes_draft_sliding_block_tables_before_base_prepare with
explicit pytest.MonkeyPatch and TrtllmAttentionMetadata parameter annotations
and a None return annotation. Rename the local stop_at_base_prepare helper to
_stop_at_base_prepare, annotate its self parameter with TrtllmAttentionMetadata
and its return type as None, and use the renamed helper in the monkeypatch.
🪄 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: 552abcac-19c8-4abe-b2c5-2074371be7db

📥 Commits

Reviewing files that changed from the base of the PR and between fb591d8 and 8dc40c2.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.py
  • tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py

Comment on lines +26 to +43
def test_prepare_computes_draft_sliding_block_tables_before_base_prepare(monkeypatch):
"""DeepSeek-V4 MTP draft KV managers need their sliding tables prepared.

The base TRT-LLM metadata prepare path copies block offsets from both the
target and draft managers. DeepSeek-V4's copy path consumes precomputed
sliding-window tables, so the draft manager must compute them before the
base prepare reaches copy_batch_block_offsets().
"""
metadata = object.__new__(DeepseekV4TrtllmAttentionMetadata)
metadata.kv_cache_manager = MagicMock()
metadata.draft_kv_cache_manager = MagicMock()
metadata.request_ids = [11, 12, 13]
metadata.num_contexts = 2

def stop_at_base_prepare(self):
raise RuntimeError("base prepare reached")

monkeypatch.setattr(TrtllmAttentionMetadata, "prepare", stop_at_base_prepare)

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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target file ---'
sed -n '1,180p' tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
printf '%s\n' '--- relevant guidelines ---'
if [ -f CODING_GUIDELINES.md ]; then
  rg -n -A4 -B4 'Annotate every function|Prefix non-public|pytest|test' CODING_GUIDELINES.md | head -160
fi
printf '%s\n' '--- nearby test-list references ---'
rg -n 'deepseek_v4|test_deepseek_v4_metadata' tests/integration/test_lists tests 2>/dev/null | head -160

Repository: NVIDIA/TensorRT-LLM

Length of output: 27204


🏁 Script executed:

#!/bin/bash
set -e
sed -n '1,180p' tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
printf '%s\n' '--- guidelines ---'
if [ -f CODING_GUIDELINES.md ]; then
  rg -n -A4 -B4 'Annotate every function|Prefix non-public|pytest|test' CODING_GUIDELINES.md | head -160
fi
printf '%s\n' '--- test-list references ---'
rg -n 'deepseek_v4|test_deepseek_v4_metadata' tests/integration/test_lists tests 2>/dev/null | head -160

Repository: NVIDIA/TensorRT-LLM

Length of output: 26941


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import ast
from pathlib import Path

path = Path("tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py")
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
        args = [*node.args.posonlyargs, *node.args.args, *node.args.kwonlyargs]
        missing_args = [arg.arg for arg in args if arg.annotation is None]
        print(
            f"{node.name}: return_annotation={node.returns is not None}, "
            f"missing_argument_annotations={missing_args}"
        )
        if node.name == "test_prepare_computes_draft_sliding_block_tables_before_base_prepare":
            for child in node.body:
                if isinstance(child, ast.FunctionDef):
                    print(
                        f"nested_helper={child.name}: "
                        f"self_annotated={bool(child.args.args and child.args.args[0].annotation)}, "
                        f"return_annotation={child.returns is not None}"
                    )
PY
printf '%s\n' '--- exact guideline matches ---'
rg -n -i 'annotat(e|ion).*every function|non-public|private names|leading underscore' CODING_GUIDELINES.md
printf '%s\n' '--- tracked-file status and focused diff ---'
git status --short -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py tests/integration/test_lists
git diff --stat -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py tests/integration/test_lists
git diff --unified=3 -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
printf '%s\n' '--- exact test-list membership ---'
rg -n -F 'unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py' tests/integration/test_lists || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 496


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import ast
from pathlib import Path

path = Path("tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py")
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
        args = [*node.args.posonlyargs, *node.args.args, *node.args.kwonlyargs]
        missing_args = [arg.arg for arg in args if arg.annotation is None]
        print(
            f"{node.name}: return_annotation={node.returns is not None}, "
            f"missing_argument_annotations={missing_args}"
        )
        if node.name == "test_prepare_computes_draft_sliding_block_tables_before_base_prepare":
            for child in node.body:
                if isinstance(child, ast.FunctionDef):
                    print(
                        f"nested_helper={child.name}: "
                        f"self_annotated={bool(child.args.args and child.args.args[0].annotation)}, "
                        f"return_annotation={child.returns is not None}"
                    )
PY
printf '%s\n' '--- exact guideline matches ---'
rg -n -i 'annotat(e|ion).*every function|non-public|private names|leading underscore' CODING_GUIDELINES.md
printf '%s\n' '--- focused status and diff ---'
git status --short -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py tests/integration/test_lists
git diff --stat -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py tests/integration/test_lists
git diff --unified=3 -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
printf '%s\n' '--- test-list membership ---'
rg -n -F 'unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py' tests/integration/test_lists || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 496


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- focused status and diff ---'
git status --short -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py tests/integration/test_lists
git diff --stat -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py tests/integration/test_lists
git diff --unified=5 -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
printf '%s\n' '--- exact test-list membership ---'
rg -n -F 'unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py' tests/integration/test_lists || true
printf '%s\n' '--- nearby CI list entries ---'
sed -n '68,90p' tests/integration/test_lists/test-db/l0_b200.yml

Repository: NVIDIA/TensorRT-LLM

Length of output: 2045


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import ast
from pathlib import Path

source = Path(
    "tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py"
).read_text()
tree = ast.parse(source)
test = next(
    node for node in tree.body
    if isinstance(node, ast.FunctionDef)
    and node.name == "test_prepare_computes_draft_sliding_block_tables_before_base_prepare"
)
helper = next(node for node in test.body if isinstance(node, ast.FunctionDef))
print("test_function_present:", test.name)
print("helper_present:", helper.name)
print("test_annotation_state:", test.returns is not None, [a.annotation is not None for a in test.args.args])
print("helper_annotation_state:", helper.returns is not None, [a.annotation is not None for a in helper.args.args])

list_path = Path("tests/integration/test_lists/test-db/l0_b200.yml")
list_text = list_path.read_text()
print("parent_directory_covers_file:",
      "  - unittest/_torch/attention\n" in list_text)
print("exact_file_entry:",
      "unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py"
      in list_text)
PY
python3 - <<'PY'
import pytest
print("pytest.MonkeyPatch_available:", hasattr(pytest, "MonkeyPatch"))
print("pytest.MonkeyPatch:", pytest.MonkeyPatch)
PY

Repository: NVIDIA/TensorRT-LLM

Length of output: 540


Annotate the new test functions and mark the local helper as non-public.

Annotate test_prepare_computes_draft_sliding_block_tables_before_base_prepare() and _stop_at_base_prepare() with pytest.MonkeyPatch, TrtllmAttentionMetadata, and None as appropriate. Rename stop_at_base_prepare to _stop_at_base_prepare.

Test coverage: test_prepare_computes_draft_sliding_block_tables_before_base_prepare() was added and is covered by unittest/_torch/attention in tests/integration/test_lists/test-db/l0_b200.yml. Verdict: sufficient.

🤖 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
`@tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py`
around lines 26 - 43, Update
test_prepare_computes_draft_sliding_block_tables_before_base_prepare with
explicit pytest.MonkeyPatch and TrtllmAttentionMetadata parameter annotations
and a None return annotation. Rename the local stop_at_base_prepare helper to
_stop_at_base_prepare, annotate its self parameter with TrtllmAttentionMetadata
and its return type as None, and use the renamed helper in the monkeypatch.

Source: Coding guidelines

Comment on lines +26 to +55
def test_prepare_computes_draft_sliding_block_tables_before_base_prepare(monkeypatch):
"""DeepSeek-V4 MTP draft KV managers need their sliding tables prepared.

The base TRT-LLM metadata prepare path copies block offsets from both the
target and draft managers. DeepSeek-V4's copy path consumes precomputed
sliding-window tables, so the draft manager must compute them before the
base prepare reaches copy_batch_block_offsets().
"""
metadata = object.__new__(DeepseekV4TrtllmAttentionMetadata)
metadata.kv_cache_manager = MagicMock()
metadata.draft_kv_cache_manager = MagicMock()
metadata.request_ids = [11, 12, 13]
metadata.num_contexts = 2

def stop_at_base_prepare(self):
raise RuntimeError("base prepare reached")

monkeypatch.setattr(TrtllmAttentionMetadata, "prepare", stop_at_base_prepare)

with pytest.raises(RuntimeError, match="base prepare reached"):
DeepseekV4TrtllmAttentionMetadata.prepare(metadata)

metadata.kv_cache_manager.compute_sliding_block_tables.assert_called_once_with(
metadata.request_ids,
metadata.num_contexts,
)
metadata.draft_kv_cache_manager.compute_sliding_block_tables.assert_called_once_with(
metadata.request_ids,
metadata.num_contexts,
)

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate candidate CI and QA test-list files.
fd -t f . tests/integration/test_lists | sort

# Check whether this test module or DeepSeek-V4 test group is registered.
rg -n -i -C 2 \
  'test_deepseek_v4_metadata|deepseek_v4' \
  tests/integration/test_lists || true

# Locate available CBTS scope evidence.
fd -t f 'cbts_touchmap.sqlite' . || true
fd -t f '*coverage*' . | rg -i 'cbts|touchmap' || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 12813


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- changed files ---'
git diff --name-status

printf '%s\n' '--- test file diff ---'
git diff -- tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py

printf '%s\n' '--- matching test-list entries ---'
rg -n -F \
  'unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py' \
  tests/integration/test_lists || true

printf '%s\n' '--- DeepSeek-V4 test-list entries ---'
rg -n -i -C 1 \
  'unittest/_torch/attention/sparse/deepseek_v4' \
  tests/integration/test_lists || true

printf '%s\n' '--- CBTS artifacts tracked or present ---'
git ls-files | rg -i '(^|/)(cbts_touchmap\.sqlite|.*cbts.*coverage.*|.*coverage.*cbts.*)$' || true
find . -type f \( -name 'cbts_touchmap.sqlite' -o -iname '*cbts*coverage*' -o -iname '*coverage*cbts*' \) -print

Repository: NVIDIA/TensorRT-LLM

Length of output: 2077


Add the test module to tests/integration/test_lists/test-db/l0_b200.yml. test_deepseek_v4_metadata.py is absent from the CI and QA lists. Coverage summary: one test added; coverage verdict: insufficient.

🤖 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
`@tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py`
around lines 26 - 55, Add
tests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
to the appropriate CI and QA entries in
tests/integration/test_lists/test-db/l0_b200.yml so
test_prepare_computes_draft_sliding_block_tables_before_base_prepare is included
in automated coverage.

Source: Path instructions

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.

DeepSeek-V4 + MTP: AttributeError: '_num_tables' in DeepseekV4CacheManager.copy_batch_block_offsets during warmup

2 participants