fix: prepare DeepSeek-V4 draft sliding tables - #17178
Conversation
Signed-off-by: zq <zhouquan1511@163.com>
WalkthroughDeepSeek-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. ChangesDeepSeek-V4 metadata preparation
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
tensorrt_llm/_torch/attention_backend/sparse/deepseek_v4/deepseek_v4.pytests/unittest/_torch/attention/sparse/deepseek_v4/test_deepseek_v4_metadata.py
| 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) |
There was a problem hiding this comment.
📐 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 -160Repository: 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 -160Repository: 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 || trueRepository: 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 || trueRepository: 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.ymlRepository: 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)
PYRepository: 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
| 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, | ||
| ) |
There was a problem hiding this comment.
🎯 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' || trueRepository: 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*' \) -printRepository: 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
Summary
TrtllmAttentionMetadata.prepare()reachescopy_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.pypython -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.pyimpi.dll/msmpi.dll) and compiled TensorRT-LLM bindings.Dev Engineer Review
DeepseekV4TrtllmAttentionMetadata.preparenow computes draft sliding-window block tables before the base metadata path copies draft block offsets._num_tablesinitialization failure in DeepSeek-V4 speculative decoding.QA Engineer Review
test_prepare_computes_sliding_tables_before_base_prepare.tests/integration/test_lists/,test-db/, orqa/entry was added or modified.