Skip to content

feat(deepseek-v4): Define dynamic prefill attention contract#793

Open
hashiqiqixian wants to merge 2 commits into
hw-native-sys:mainfrom
hashiqiqixian:issue-766-dynamic-prefill-attention
Open

feat(deepseek-v4): Define dynamic prefill attention contract#793
hashiqiqixian wants to merge 2 commits into
hw-native-sys:mainfrom
hashiqiqixian:issue-766-dynamic-prefill-attention

Conversation

@hashiqiqixian

Copy link
Copy Markdown
Contributor

Summary

  • Establish a shared TOKENS_DYN contract for the DeepSeek V4 prefill Attention path and bind token-aligned inputs, outputs, positions, and slot mappings at JIT entry points.
  • Convert HC pre/post, RMSNorm, QKV projection with RoPE, ratio-4/ratio-128 compressors, indexer/indexer-compressor, and sparse Attention to derive their logical token extent from the physical tensor shape.
  • Remove the runtime num_tokens argument from the Attention leaf APIs and propagate dynamically shaped tensors through the SWA, HCA, and CSA dependency chains.
  • Keep hardware tile sizes static internally while sizing token-major scratch tensors from the runtime extent and using validshape for non-tile-aligned tail rows.
  • Preserve the current unified cache/state contracts, block-table metadata, request slot mappings, and the upstream normalize-before-RoPE precision behavior.
  • Retain num_tokens only at the three top-level Attention composition entry points as a temporary compatibility boundary for existing fixed-T callers. Removing that boundary, converting MoE, and integrating the full packed prefill_layer / prefill_fwd path are intentionally deferred to the follow-up PR.
  • Add source-contract regression coverage for dynamic annotations, leaf API boundaries, dynamic scratch allocation, Tile/Tensor operation levels, partial-tile stores, and QKV projection memory constraints.
  • Validation completed:
    • python -m pytest tests/golden/test_deepseek_v4_prefill_dynamic_source.py -q — 8 passed
    • Python syntax compilation for all changed modules
    • git diff --check origin/main...HEAD

Related Issues

Part of #766

Related to #410

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7da741d3-a402-44b5-a50c-f7f0c9166cbc

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a dynamic-token contract for DeepSeek V4 packed prefill, transitioning various attention, compressor, indexer, and sparse attention modules to use dynamic token shapes (T_DYN) instead of static dimensions. Feedback on the changes identifies an issue in hc_pre.py where pl.store is incorrectly passed a TensorType from pl.set_validshape instead of a Vec TileType. To fix this and meet hardware alignment constraints, it is recommended to allocate a same-core scratch tensor comb_pad_store to assemble the tiles and load them back as Vec tiles before storing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread models/deepseek/v4-flash/hc_pre.py
Comment thread models/deepseek/v4/hc_pre.py Outdated
Comment thread models/deepseek/v4-flash/hc_pre.py
Comment thread models/deepseek/v4/hc_pre.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
models/deepseek/v4/prefill_attention_csa.py (1)

263-292: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Guard the tail-tile assemblies.

For non-aligned token counts, t_idx exceeds the exact [num_tokens, ...] outputs on the final tile, but Lines 291-292 still assemble both rows out of bounds.

Proposed fix
             if t_idx < num_tokens:
                 abs_pos = pl.read(position_ids, [t_idx])
                 ...
-            swa_indices = pl.assemble(swa_indices, swa_row, [t_idx, 0])
-            cmp_indices = pl.assemble(cmp_indices, cmp_row, [t_idx, 0])
+                swa_indices = pl.assemble(swa_indices, swa_row, [t_idx, 0])
+                cmp_indices = pl.assemble(cmp_indices, cmp_row, [t_idx, 0])
🤖 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 `@models/deepseek/v4/prefill_attention_csa.py` around lines 263 - 292, Guard
the swa_indices and cmp_indices assemblies in the top-k tile loop so they run
only when t_idx is less than num_tokens. Keep the existing row initialization
and valid-token population unchanged, and avoid assembling either row for padded
tail-tile positions.
models/deepseek/v4/prefill_indexer.py (1)

251-259: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Pass the dynamic input to the child compressor.

Line 252 passes padded [T, D] tensor x, so prefill_indexer_compressor derives num_tokens == T and can overrun the shorter metadata tensors. Pass x_in; the child already performs its own static padding.

Proposed fix
     prefill_indexer_compressor(
-        x,
+        x_in,
🤖 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 `@models/deepseek/v4/prefill_indexer.py` around lines 251 - 259, Update the
prefill_indexer_compressor call to pass the dynamic input tensor x_in instead of
the padded tensor x, preserving the child compressor’s existing static-padding
behavior and ensuring its token count matches the metadata tensors.
models/deepseek/v4/prefill_indexer_compressor.py (1)

463-469: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Dedent the scan-loop body after removing the guard.

Lines 465-469 remain indented under the deleted conditional, making the module fail Python parsing.

Proposed fix
             num_tokens = pl.tensor.dim(x, 0)
             for scan_w in pl.range(num_tokens):
-                    scan_slot_raw = pl.read(idx_slot_mapping, [scan_w])
-                    if scan_slot_raw >= 0:
-                        if write_seen == kv_i:
-                            src_row_raw = scan_slot_raw
-                        write_seen = write_seen + 1
+                scan_slot_raw = pl.read(idx_slot_mapping, [scan_w])
+                if scan_slot_raw >= 0:
+                    if write_seen == kv_i:
+                        src_row_raw = scan_slot_raw
+                    write_seen = write_seen + 1
🤖 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 `@models/deepseek/v4/prefill_indexer_compressor.py` around lines 463 - 469,
Dedent the scan-loop body in the token scan logic around num_tokens so
scan_slot_raw handling and write_seen updates are directly nested under the
pl.range loop after the removed guard. Ensure the resulting indentation is valid
Python and preserves the existing write_seen and src_row_raw behavior.
🧹 Nitpick comments (1)
tests/golden/test_deepseek_v4_prefill_dynamic_source.py (1)

19-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Cache AST parsing and reuse _tree.

This helper is called repeatedly across multiple tests (e.g., inside _function), which redundantly reads and parses the same files multiple times. Additionally, line 61 manually parses the AST instead of using _tree.

Consider caching the ASTs to improve test performance and replacing the inline parsing at line 61 with tree = _tree(filename).

♻️ Proposed refactor
+import functools
+
+@functools.lru_cache(maxsize=None)
 def _tree(filename: str) -> ast.Module:
     return ast.parse((MODEL / filename).read_text(encoding="utf-8"))

And update line 61:

-        tree = ast.parse((MODEL / filename).read_text(encoding="utf-8"))
+        tree = _tree(filename)
🤖 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/golden/test_deepseek_v4_prefill_dynamic_source.py` around lines 19 -
21, Cache the parsed AST result in the _tree helper so repeated requests for the
same filename reuse the existing module instead of rereading and reparsing it.
Update the inline AST parsing near the affected test flow to assign tree via
_tree(filename), preserving the current parsing behavior while centralizing it
in _tree.
🤖 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 `@models/deepseek/v4/prefill_indexer.py`:
- Around line 566-577: Update the standalone CLI entrypoints so each builder
receives the requested token count: add a --num-tokens argument and forward
args.num_tokens when calling build_tensor_specs in
models/deepseek/v4/prefill_indexer.py (566-577) and
models/deepseek/v4/prefill_indexer_compressor.py (610-622); pass args.num_tokens
alongside args.compress_ratio in models/deepseek/v4/prefill_sparse_attn.py
(638-654).
- Around line 530-536: Update the topk copy loop around cmp_topk_indices to
initialize each topk_idxs row to -1, then copy only IDX_TOPK columns from the
IDX_TOPK-wide source tensor. Do not slice cmp_topk_indices through
INDEXER_SCORE_CAP; preserve the existing token bounds and tiling behavior.
- Around line 404-408: Update the remaining golden calculations after num_tokens
is derived to use num_tokens instead of the stale T extent, including view
shapes and the reshape involving IDX_N_HEADS. Ensure all intermediate tensors
are sized to the actual token count so partial-token fixtures compute correctly.

---

Outside diff comments:
In `@models/deepseek/v4/prefill_attention_csa.py`:
- Around line 263-292: Guard the swa_indices and cmp_indices assemblies in the
top-k tile loop so they run only when t_idx is less than num_tokens. Keep the
existing row initialization and valid-token population unchanged, and avoid
assembling either row for padded tail-tile positions.

In `@models/deepseek/v4/prefill_indexer_compressor.py`:
- Around line 463-469: Dedent the scan-loop body in the token scan logic around
num_tokens so scan_slot_raw handling and write_seen updates are directly nested
under the pl.range loop after the removed guard. Ensure the resulting
indentation is valid Python and preserves the existing write_seen and
src_row_raw behavior.

In `@models/deepseek/v4/prefill_indexer.py`:
- Around line 251-259: Update the prefill_indexer_compressor call to pass the
dynamic input tensor x_in instead of the padded tensor x, preserving the child
compressor’s existing static-padding behavior and ensuring its token count
matches the metadata tensors.

---

Nitpick comments:
In `@tests/golden/test_deepseek_v4_prefill_dynamic_source.py`:
- Around line 19-21: Cache the parsed AST result in the _tree helper so repeated
requests for the same filename reuse the existing module instead of rereading
and reparsing it. Update the inline AST parsing near the affected test flow to
assign tree via _tree(filename), preserving the current parsing behavior while
centralizing it in _tree.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 792a0f7d-4390-47bd-a37f-ef0865b00ad9

📥 Commits

Reviewing files that changed from the base of the PR and between f894b63 and 392d743.

📒 Files selected for processing (14)
  • models/deepseek/v4/dynamic_shapes.py
  • models/deepseek/v4/hc_post.py
  • models/deepseek/v4/hc_pre.py
  • models/deepseek/v4/prefill_attention_csa.py
  • models/deepseek/v4/prefill_attention_hca.py
  • models/deepseek/v4/prefill_attention_swa.py
  • models/deepseek/v4/prefill_compressor_ratio128.py
  • models/deepseek/v4/prefill_compressor_ratio4.py
  • models/deepseek/v4/prefill_indexer.py
  • models/deepseek/v4/prefill_indexer_compressor.py
  • models/deepseek/v4/prefill_sparse_attn.py
  • models/deepseek/v4/qkv_proj_rope.py
  • models/deepseek/v4/rmsnorm.py
  • tests/golden/test_deepseek_v4_prefill_dynamic_source.py

Comment thread models/deepseek/v4-flash/prefill_indexer.py
Comment thread models/deepseek/v4/prefill_indexer.py Outdated
Comment thread models/deepseek/v4-flash/prefill_indexer.py
@hashiqiqixian
hashiqiqixian force-pushed the issue-766-dynamic-prefill-attention branch 27 times, most recently from 0a011c8 to 41f0ee5 Compare July 21, 2026 11:33
@hashiqiqixian
hashiqiqixian force-pushed the issue-766-dynamic-prefill-attention branch 14 times, most recently from 5a57853 to dadfe8f Compare July 23, 2026 01:37
@hashiqiqixian
hashiqiqixian force-pushed the issue-766-dynamic-prefill-attention branch 2 times, most recently from 329f905 to 1944a03 Compare July 23, 2026 03:54
@hashiqiqixian
hashiqiqixian force-pushed the issue-766-dynamic-prefill-attention branch from 1944a03 to 82f7542 Compare July 23, 2026 06:45
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.

1 participant