Skip to content

feat(attention): add deterministic CP reference - #238

Open
inaniloquentee wants to merge 19 commits into
testfrom
feat/ws2-cp-attention-reference-pr3
Open

feat(attention): add deterministic CP reference#238
inaniloquentee wants to merge 19 commits into
testfrom
feat/ws2-cp-attention-reference-pr3

Conversation

@inaniloquentee

@inaniloquentee inaniloquentee commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Scope

PR3 provides the independent deterministic CP Attention reference for prefill and chunked-prefill.

Contract

Area Enforced behavior
Partial state Materializes FP32 Out/LSE states
Merge order Sorts logical global_block_index before every merge, independent of arrival order
Split-KV Disabled in strict mode; CP/chunk layout cannot change arithmetic order
Inputs Uses attention-ready Q/K/V with global RoPE positions, causal masks, varlen, padding, empty rows, and GQA
TE relation TE may be used as an oracle; the reference remains the numerical contract

Validation

Check Result
Focused deterministic-reference suite 46 passed
Integrated 8xH100 forward gate Passed: Out/LSE max abs 0.0 across 2/4/8 ranks

Boundary

This PR validates reference arithmetic and fixed logical merge semantics. It does not claim real NCCL/AG/RS execution, fused CUDA arithmetic, decode replay, or performance.

Commit: 988d954

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a deterministic PyTorch context-parallel attention reference with chunked-prefill support, FP32 partial-state merging, WS2 contracts, registry dispatch, synthetic inputs, documentation, and validation tests.

Changes

Context-parallel attention

Layer / File(s) Summary
Attention contract and runtime planning
rl_engine/kernels/attention_contract.py
Defines validated sharding, reduction, cache, RoPE, backend capability, Split-KV, runtime-plan, alignment, and dispatch-result contracts.
CP attention execution
rl_engine/kernels/ops/pytorch/attention/cp_attention.py
Adds deterministic CP partitioning, partial-state merging, chunked prefill, masking, position offsets, backward validation, drift reporting, and Split-KV provenance.
Dispatch and harness inputs
rl_engine/kernels/registry.py, rl_engine/kernels/gtest/operator_inputs.py
Registers cp_attention across platforms and adds CP-specific generated inputs and shape naming.
Behavior validation and documentation
tests/test_cp_attention.py, tests/test_cp_attention_transformer_engine.py, tests/test_operator_inputs.py, docs/operators/attention.md
Adds correctness, merge, masking, chunking, gradient, dtype, validation, dispatch, Transformer Engine, operator-input, and documentation coverage.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant DeterministicCPAttentionReferenceOp
  participant AttentionPartialState
  participant merge_attention_partial_states
  Caller->>DeterministicCPAttentionReferenceOp: Submit Q, K, V and CP parameters
  DeterministicCPAttentionReferenceOp->>AttentionPartialState: Compute query-shard and KV-block partials
  DeterministicCPAttentionReferenceOp->>merge_attention_partial_states: Merge partial states by global KV block order
  merge_attention_partial_states-->>DeterministicCPAttentionReferenceOp: Return output and LSE
  DeterministicCPAttentionReferenceOp-->>Caller: Return attention result and provenance
Loading

Possibly related PRs

Suggested reviewers: flink-ddd, kjldefeated

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding a deterministic context-parallel attention reference.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ws2-cp-attention-reference-pr3

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.

Signed-off-by: inaniloquentee <3051000145@qq.com>
@inaniloquentee
inaniloquentee force-pushed the feat/ws2-cp-attention-reference-pr3 branch from 8e3c6aa to ed05a09 Compare July 20, 2026 15:15

@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

🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py`:
- Around line 313-329: Run Black on
rl_engine/kernels/ops/pytorch/attention/cp_attention.py and commit its
formatting changes, specifically the torch.zeros call in the skv == 0 early
return and the None if key_padding_mask is None conditional in
local_partial_state(...).
- Around line 427-444: Update the zero-output tensor created in the states-empty
branch of the attention operation to explicitly use torch.float32, matching the
lse tensor and the analogous no-chunks branch. Keep the existing device, shape,
and zero-dependency behavior unchanged so all entries in out_chunks have a
consistent dtype for torch.cat.
- Around line 340-353: In local_partial_state and _merge_two_states, replace
non-finite LSE values with a finite placeholder before any subtraction or
exponentiation. Use the guarded LSE for scores - lse and lse_a - merged_lse,
while preserving zero outputs for fully masked rows and the existing merge
behavior for finite rows.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1055b627-fdd1-486c-bf2d-d99ae146f9fa

📥 Commits

Reviewing files that changed from the base of the PR and between 063a261 and 8e3c6aa.

📒 Files selected for processing (6)
  • docs/operators/attention.md
  • rl_engine/kernels/gtest/operator_inputs.py
  • rl_engine/kernels/ops/pytorch/attention/cp_attention.py
  • rl_engine/kernels/registry.py
  • tests/test_cp_attention.py
  • tests/test_operator_inputs.py

Comment on lines +313 to +329
if skv == 0:
zero_dep = _zero_dependency(qf, kf, vf)
return AttentionPartialState(
out=torch.zeros(
q.size(0), hq, sq, dim, device=q.device, dtype=torch.float32
)
+ zero_dep,
lse=torch.full(
(q.size(0), hq, sq),
float("-inf"),
device=q.device,
dtype=torch.float32,
)
+ zero_dep,
block_start=k_start,
block_end=k_start,
)

Copy link
Copy Markdown

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

Black formatting not applied — CI linting fails.

Per pipeline failures, the black pre-commit hook reformats this file at two spots that were committed unformatted: the multi-line torch.zeros(...) call inside the skv == 0 early-return (around lines 316-319), and the None if key_padding_mask is None else ... conditional inside the local_partial_state(...) call (around lines 415-419). Run black locally and commit the reformatted file.

Also applies to: 405-426

🧰 Tools
🪛 GitHub Actions: CI-Pipeline / 1_linting.txt

[error] 313-315: Pre-commit hook 'black' failed: reformatting required. The hook modified the file (e.g., changed multi-line torch.zeros(...) call to a single line).

🪛 GitHub Actions: CI-Pipeline / linting

[error] 313-313: pre-commit hook "black" failed. File was reformatted by Black (CI requires committed formatting changes).

🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py` around lines 313 -
329, Run Black on rl_engine/kernels/ops/pytorch/attention/cp_attention.py and
commit its formatting changes, specifically the torch.zeros call in the skv == 0
early return and the None if key_padding_mask is None conditional in
local_partial_state(...).

Source: Pipeline failures

Comment on lines +340 to +353
if key_padding_mask is not None:
scores = scores.masked_fill(~key_padding_mask[:, None, None, :], float("-inf"))

lse = torch.logsumexp(scores, dim=-1)
finite_lse = torch.isfinite(lse)
weights = torch.exp(scores - lse.unsqueeze(-1))
weights = torch.where(finite_lse.unsqueeze(-1), weights, torch.zeros_like(weights))
out = torch.matmul(weights, vf)
return AttentionPartialState(
out=out,
lse=lse,
block_start=k_start,
block_end=k_start + skv,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and nearby symbols first.
ast-grep outline rl_engine/kernels/ops/pytorch/attention/cp_attention.py --view expanded || true

# Read the implementation around the cited lines.
sed -n '300,500p' rl_engine/kernels/ops/pytorch/attention/cp_attention.py

# Find the tests mentioned in the review comment and nearby masking/gradient coverage.
rg -n "test_key_padding_mask_and_all_masked_rows_are_stable|test_empty_kv_backward_returns_zero_grads|chunked_gradients_match_cp1_reference|all_masked|key_padding_mask" -S rl_engine tests . || true

Repository: RL-Align/RL-Kernel

Length of output: 26910


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the cp_attention tests around the cited cases.
sed -n '180,340p' tests/test_cp_attention.py

# Read the standard attention implementation's masked-row handling for comparison.
sed -n '150,190p' rl_engine/kernels/ops/pytorch/attention/standard_attn.py

# Run a minimal autograd repro for the exact pattern.
python3 - <<'PY'
import torch

torch.set_printoptions(sci_mode=False)

scores = torch.tensor([[[float('-inf'), float('-inf')]]], requires_grad=True)
lse = torch.logsumexp(scores, dim=-1)
finite = torch.isfinite(lse)
weights = torch.exp(scores - lse.unsqueeze(-1))
weights = torch.where(finite.unsqueeze(-1), weights, torch.zeros_like(weights))
loss = weights.sum()
loss.backward()
print("lse:", lse)
print("weights:", weights)
print("scores.grad:", scores.grad)
print("has_nan_grad:", torch.isnan(scores.grad).any().item())

scores2 = torch.tensor([[[0.0, float('-inf')]]], requires_grad=True)
lse2 = torch.logsumexp(scores2, dim=-1)
finite2 = torch.isfinite(lse2)
weights2 = torch.exp(scores2 - lse2.unsqueeze(-1))
weights2 = torch.where(finite2.unsqueeze(-1), weights2, torch.zeros_like(weights2))
loss2 = weights2.sum()
loss2.backward()
print("mixed lse:", lse2)
print("mixed weights:", weights2)
print("mixed grad:", scores2.grad)
print("mixed has_nan_grad:", torch.isnan(scores2.grad).any().item())

# Repro for the merge path.
lse_a = torch.tensor([float('-inf')], requires_grad=True)
lse_b = torch.tensor([float('-inf')], requires_grad=True)
merged = torch.logaddexp(lse_a, lse_b)
finite_m = torch.isfinite(merged)
wa = torch.where(finite_m, torch.exp(lse_a - merged), torch.zeros_like(merged))
wb = torch.where(finite_m, torch.exp(lse_b - merged), torch.zeros_like(merged))
loss3 = (wa + wb).sum()
loss3.backward()
print("merged:", merged)
print("weights:", wa, wb)
print("grad_a:", lse_a.grad, "grad_b:", lse_b.grad)
print("merge_has_nan_grad:", torch.isnan(lse_a.grad).any().item() or torch.isnan(lse_b.grad).any().item())
PY

Repository: RL-Align/RL-Kernel

Length of output: 8383


Guard the -inf LSE before subtracting in both attention merge paths.
Fully masked rows/blocks produce nan gradients here: torch.where(...) only fixes the forward value, while exp(scores - lse) and exp(lse_a - merged_lse) still backpropagate through -inf - (-inf). Use a finite placeholder LSE before the subtraction in local_partial_state and _merge_two_states.

🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py` around lines 340 -
353, In local_partial_state and _merge_two_states, replace non-finite LSE values
with a finite placeholder before any subtraction or exponentiation. Use the
guarded LSE for scores - lse and lse_a - merged_lse, while preserving zero
outputs for fully masked rows and the existing merge behavior for finite rows.

Comment thread rl_engine/kernels/ops/pytorch/attention/cp_attention.py Outdated

@zhangj1an zhangj1an left a comment

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.

LGTM. Thanks for your work!

This FP32 Ring CP golden reference is numerically equivalent with the PyTorch implementation of flash_attn_fwd_softmax_lse_correction / flash_attn_fwd_out_correction in Transformer Engine, https://github.com/NVIDIA/TransformerEngine/blob/main/transformer_engine/pytorch/attention/dot_product_attention/context_parallel.py. The softmax merging order is by KV block order.

Also the unit tests covered both forward and backward test cases (gradients via autograd).

Comment on lines +541 to +548
CPAttentionReferenceOp = DeterministicCPAttentionReferenceOp

__all__ = [
"AttentionPartialState",
"CPAttentionReferenceOp",
"DeterministicCPAttentionReferenceOp",
"merge_attention_partial_states",
]

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.

delete the alias DeterministicCPAttentionReferenceOp please

@Flink-ddd

Copy link
Copy Markdown
Collaborator

please resolve the code conflicts first, Thank you.

@inaniloquentee

Copy link
Copy Markdown
Collaborator Author

LGTM. Thanks for your work!

This FP32 Ring CP golden reference is numerically equivalent with the PyTorch implementation of flash_attn_fwd_softmax_lse_correction / flash_attn_fwd_out_correction in Transformer Engine, https://github.com/NVIDIA/TransformerEngine/blob/main/transformer_engine/pytorch/attention/dot_product_attention/context_parallel.py. The softmax merging order is by KV block order.

Also the unit tests covered both forward and backward test cases (gradients via autograd).

Thanks for the review! Removed the alias and kept only DeterministicCPAttentionReferenceOp.

Also agreed on the TE point: this PR keeps the PyTorch golden reference self-contained, while matching the same FP32 (out, lse) correction semantics as Transformer Engine.

@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.

🧹 Nitpick comments (1)
tests/test_cp_attention_transformer_engine.py (1)

18-24: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Guard the optional Transformer engine oracle against API drift.

This test only skips when the internal module cannot be imported. If an installed Transformer Engine release exposes the module but removes or renames the helper used at lines 43–45, the optional test raises AttributeError instead of skipping. Check that the required Flash attention helpers are present before calling them, or pin the supported Transformer Engine version.

🤖 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/test_cp_attention_transformer_engine.py` around lines 18 - 24, Update
_te_context_parallel_module to validate that the imported context-parallel
module exposes the required Flash attention helpers used by the optional test
before returning it. If any helper is missing or renamed, skip with a clear
unavailable message instead of allowing AttributeError during the test; preserve
the existing import-error skips.

Source: MCP tools

🤖 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 `@tests/test_cp_attention_transformer_engine.py`:
- Around line 18-24: Update _te_context_parallel_module to validate that the
imported context-parallel module exposes the required Flash attention helpers
used by the optional test before returning it. If any helper is missing or
renamed, skip with a clear unavailable message instead of allowing
AttributeError during the test; preserve the existing import-error skips.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b396caf-830f-45b7-8f6d-e4cd55c71ee5

📥 Commits

Reviewing files that changed from the base of the PR and between ed05a09 and 1c8e9db.

📒 Files selected for processing (6)
  • docs/operators/attention.md
  • rl_engine/kernels/gtest/operator_inputs.py
  • rl_engine/kernels/ops/pytorch/attention/cp_attention.py
  • rl_engine/kernels/registry.py
  • tests/test_cp_attention_transformer_engine.py
  • tests/test_operator_inputs.py
💤 Files with no reviewable changes (1)
  • rl_engine/kernels/ops/pytorch/attention/cp_attention.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/test_operator_inputs.py
  • rl_engine/kernels/registry.py
  • rl_engine/kernels/gtest/operator_inputs.py

@inaniloquentee

Copy link
Copy Markdown
Collaborator Author

code conflicts

Fixed code conflicts.

Signed-off-by: inaniloquentee <3051000145@qq.com>

@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: 1

🧹 Nitpick comments (1)
docs/operators/attention.md (1)

256-257: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify the compound modifier in the limitation.

Rewrite the sentence so that the relationship between production backends and fused RoPE+Attention kernels is explicit.

Proposed wording
-- `cp_attention` consumes post-RoPE Q/K for Qwen3 WS2; RoPE execution and fused
-- `RoPE+Attention` backend alignment are outside PR3.
+- `cp_attention` consumes post-RoPE Q/K for Qwen3 WS2. RoPE execution and alignment
+  with production `RoPE+Attention`-fused backends are outside PR3.
🤖 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 `@docs/operators/attention.md` around lines 256 - 257, Rewrite the cp_attention
limitation sentence in docs/operators/attention.md to explicitly state the
relationship between production backends and fused RoPE+Attention kernels, while
preserving that it consumes post-RoPE Q/K for Qwen3 WS2 and that RoPE execution
is outside PR3.

Source: Linters/SAST tools

🤖 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/test_cp_attention.py`:
- Around line 114-144: Strengthen the test around rope.forward_fp32 and
op.forward_fp32_with_lse by using different global starts for query and key
positions, then pass those distinct offsets to the attention call. Add a
continuation-case assertion against an independent position-aware reference,
rather than relying only on the CP1-versus-CP2 comparison, so implementations
that ignore query_position_offsets or key_position_offsets cannot pass.

---

Nitpick comments:
In `@docs/operators/attention.md`:
- Around line 256-257: Rewrite the cp_attention limitation sentence in
docs/operators/attention.md to explicitly state the relationship between
production backends and fused RoPE+Attention kernels, while preserving that it
consumes post-RoPE Q/K for Qwen3 WS2 and that RoPE execution is outside PR3.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 365e8ce0-81ac-4717-8aa8-a9717322436f

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8e9db and 0480ce8.

📒 Files selected for processing (3)
  • docs/operators/attention.md
  • rl_engine/kernels/ops/pytorch/attention/cp_attention.py
  • tests/test_cp_attention.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • rl_engine/kernels/ops/pytorch/attention/cp_attention.py

Comment on lines +114 to +144
position_offsets = torch.tensor([17, 103], dtype=torch.long)
positions = position_offsets[:, None] + torch.arange(pre_rope_q.size(2), dtype=torch.long)
q = rope.forward_fp32(pre_rope_q, positions, theta=1_000_000.0)
k = rope.forward_fp32(pre_rope_k, positions, theta=1_000_000.0)

assert not torch.equal(q, pre_rope_q.float())
assert not torch.equal(k, pre_rope_k.float())

with _single_thread():
out1, lse1 = op.forward_fp32_with_lse(
q,
k,
v,
causal=True,
query_position_offsets=position_offsets,
key_position_offsets=position_offsets,
cp_world_size=1,
)
out2, lse2 = op.forward_fp32_with_lse(
q,
k,
v,
causal=True,
query_position_offsets=position_offsets,
key_position_offsets=position_offsets,
cp_world_size=2,
kv_chunk_size=2,
)

torch.testing.assert_close(out2, out1, atol=_ATOL, rtol=0.0)
torch.testing.assert_close(lse2, lse1, atol=_ATOL, rtol=0.0)

Copy link
Copy Markdown

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

Make position-offset handling observable in this test.

Lines 114-117 apply the same offset to Q and K. Lines 128-129 pass the same offset to both attention inputs. Adding the same value to both sides does not change the causal relation. An implementation that ignores both offset arguments can therefore pass this test.

The torch.equal checks only prove that RoPE changed the tensors. The CP1-versus-CP2 comparison only proves partition equivalence. Add a continuation case with different query and key global starts, and compare the result with an independent position-aware reference.

🤖 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/test_cp_attention.py` around lines 114 - 144, Strengthen the test
around rope.forward_fp32 and op.forward_fp32_with_lse by using different global
starts for query and key positions, then pass those distinct offsets to the
attention call. Add a continuation-case assertion against an independent
position-aware reference, rather than relying only on the CP1-versus-CP2
comparison, so implementations that ignore query_position_offsets or
key_position_offsets cannot pass.

@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: 5

🧹 Nitpick comments (3)
rl_engine/kernels/ops/pytorch/attention/cp_attention.py (1)

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

Add strict=True to the zip call.

Ruff B905 flags this call. _split_bounds returns cp_world_size entries for both bounds, so strict=True does not change behavior and documents the invariant.

♻️ Proposed change
-    for rank, ((q_start, q_end), (kv_start, kv_end)) in enumerate(zip(q_bounds, kv_bounds)):
+    for rank, ((q_start, q_end), (kv_start, kv_end)) in enumerate(
+        zip(q_bounds, kv_bounds, strict=True)
+    ):
🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py` at line 767, Update
the zip call in the bounds iteration within the attention processing logic to
pass strict=True, preserving the existing loop behavior while explicitly
validating that q_bounds and kv_bounds have matching lengths.

Source: Linters/SAST tools

rl_engine/kernels/attention_contract.py (2)

1153-1162: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the identical causal_offsets count branches.

Both branches assign expected_causal_offsets = batch_size. Only offset_owner differs, and it is used for the message text. Simplify to one assignment plus a conditional label.

♻️ Proposed simplification
-            if self.sharding.packed_sequence_offsets is not None:
-                expected_causal_offsets = batch_size
-                offset_owner = "packed sequence"
-            else:
-                expected_causal_offsets = batch_size
-                offset_owner = "batch entry"
-            if len(causal_offsets) != expected_causal_offsets:
+            offset_owner = (
+                "packed sequence"
+                if self.sharding.packed_sequence_offsets is not None
+                else "batch entry"
+            )
+            if len(causal_offsets) != batch_size:
                 raise AttentionContractError(
                     f"causal_offsets must contain one entry per {offset_owner}"
                 )
🤖 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 `@rl_engine/kernels/attention_contract.py` around lines 1153 - 1162, In the
causal-offset validation block, collapse the duplicated branches assigning
expected_causal_offsets: assign it once to batch_size, then compute offset_owner
conditionally from packed_sequence_offsets while preserving the existing labels
and error behavior.

733-789: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Index entries by coordinate before the coverage and invariance scans.

Both _validate_owner_coverage and _validate_rank_invariance re-scan self.entries inside nested loops. The entry count is batch_size * tp_world_size * cp_world_size**2, so the total work is quadratic in the entry count. For a moderately large topology, such as batch_size=64, tp_world_size=8, and cp_world_size=8, this performs on the order of 10^8 comparisons per validation. Build one dictionary keyed by SplitKVRuntimeCoordinate in __post_init__ and look entries up directly.

♻️ Sketch of the indexed lookup
     def __post_init__(self) -> None:
         ...
         actual_coordinates = [entry.coordinate for entry in entries]
+        by_coordinate = {entry.coordinate: entry for entry in entries}
         ...
-        self._validate_owner_coverage()
-        self._validate_rank_invariance()
+        self._validate_owner_coverage(by_coordinate)
+        self._validate_rank_invariance(by_coordinate)

Then replace each list comprehension with by_coordinate[SplitKVRuntimeCoordinate(batch_index, tp_rank, cp_rank, owner_cp_rank)].

🤖 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 `@rl_engine/kernels/attention_contract.py` around lines 733 - 789, Build a
coordinate-to-entry dictionary keyed by SplitKVRuntimeCoordinate in
__post_init__. Update _validate_owner_coverage and _validate_rank_invariance to
retrieve entries directly from this index instead of filtering self.entries
inside nested loops, preserving the existing validation behavior and error
handling.
🤖 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 `@rl_engine/kernels/attention_contract.py`:
- Around line 1-2: Run Black on attention_contract.py using the repository’s
standard formatter command and commit the resulting formatting changes, without
modifying behavior.
- Around line 1078-1085: Rename the loop variable field to a non-conflicting
name in the shown normalization loop and the corresponding loops in
AttentionContract.__post_init__ and AttentionBackendCapability.__post_init__.
Update all references within each loop, preserving the existing behavior while
avoiding shadowing the imported dataclasses.field.

In `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py`:
- Around line 1019-1035: Remove the CPAttentionReferenceOp alias assignment and
its corresponding __all__ export; use DeterministicCPAttentionReferenceOp
directly and verify no remaining importers reference the removed alias.
- Around line 457-466: Update split_kv_execution_plan_provenance to return an
empty plan list when the KV sequence length is zero, before validation rejects
the input. Preserve existing planning behavior for positive lengths, and add a
regression test covering the backward_reference path with empty KV tensors.
- Around line 794-804: Update the percentile calculations in the
GradientDriftStats construction to avoid torch.quantile for tensors exceeding
2**24 elements, while preserving default linear interpolation semantics for
p95_abs and p99_abs. Use a large-tensor-safe reduction based on sorting and
interpolation, and keep the existing empty-tensor handling and other statistics
unchanged.

---

Nitpick comments:
In `@rl_engine/kernels/attention_contract.py`:
- Around line 1153-1162: In the causal-offset validation block, collapse the
duplicated branches assigning expected_causal_offsets: assign it once to
batch_size, then compute offset_owner conditionally from packed_sequence_offsets
while preserving the existing labels and error behavior.
- Around line 733-789: Build a coordinate-to-entry dictionary keyed by
SplitKVRuntimeCoordinate in __post_init__. Update _validate_owner_coverage and
_validate_rank_invariance to retrieve entries directly from this index instead
of filtering self.entries inside nested loops, preserving the existing
validation behavior and error handling.

In `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py`:
- Line 767: Update the zip call in the bounds iteration within the attention
processing logic to pass strict=True, preserving the existing loop behavior
while explicitly validating that q_bounds and kv_bounds have matching lengths.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cefd3690-2125-47b9-8634-25af36822583

📥 Commits

Reviewing files that changed from the base of the PR and between 0480ce8 and 9f46eff.

📒 Files selected for processing (4)
  • docs/operators/attention.md
  • rl_engine/kernels/attention_contract.py
  • rl_engine/kernels/ops/pytorch/attention/cp_attention.py
  • tests/test_cp_attention.py

Comment on lines +1 to +2
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 RL-Kernel Contributors

Copy link
Copy Markdown

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

Run black on this file.

The linting job reports that Black reformats this file. Run black rl_engine/kernels/attention_contract.py and commit the result.

🧰 Tools
🪛 GitHub Actions: CI-Pipeline / 2_linting.txt

[error] 1-1: Black formatting check failed and reformatted this file. Run 'pre-commit run --all-files' or 'black' locally, then commit the changes.

🪛 GitHub Actions: CI-Pipeline / linting

[error] 1-1: Black formatting check failed and reformatted this file. Run 'black rl_engine/kernels/attention_contract.py' and commit the changes.

🤖 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 `@rl_engine/kernels/attention_contract.py` around lines 1 - 2, Run Black on
attention_contract.py using the repository’s standard formatter command and
commit the resulting formatting changes, without modifying behavior.

Source: Pipeline failures

Comment thread rl_engine/kernels/attention_contract.py Outdated
Comment on lines +1078 to +1085
for field in ("position_ids", "query_position_offsets", "key_position_offsets"):
values = getattr(self, field)
if values is None:
continue
normalized = _integer_tuple(values, field)
if not normalized or any(value < 0 for value in normalized):
raise AttentionContractError(f"{field} must contain non-negative positions")
object.__setattr__(self, field, normalized)

Copy link
Copy Markdown

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

Rename the loop variable field to unblock CI.

Flake8 F402 and Ruff F402 fail here and at Line 1189 and Line 1339. The loop variable field shadows the dataclasses.field import at Line 14. The linting job fails the build. Rename the loop variable at all three sites.

🔧 Proposed fix for Line 1078
-        for field in ("position_ids", "query_position_offsets", "key_position_offsets"):
-            values = getattr(self, field)
+        for field_name in ("position_ids", "query_position_offsets", "key_position_offsets"):
+            values = getattr(self, field_name)
             if values is None:
                 continue
-            normalized = _integer_tuple(values, field)
+            normalized = _integer_tuple(values, field_name)
             if not normalized or any(value < 0 for value in normalized):
-                raise AttentionContractError(f"{field} must contain non-negative positions")
-            object.__setattr__(self, field, normalized)
+                raise AttentionContractError(f"{field_name} must contain non-negative positions")
+            object.__setattr__(self, field_name, normalized)

Also apply the same rename in AttentionContract.__post_init__ at Line 1189 and in AttentionBackendCapability.__post_init__ at Line 1339.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for field in ("position_ids", "query_position_offsets", "key_position_offsets"):
values = getattr(self, field)
if values is None:
continue
normalized = _integer_tuple(values, field)
if not normalized or any(value < 0 for value in normalized):
raise AttentionContractError(f"{field} must contain non-negative positions")
object.__setattr__(self, field, normalized)
for field_name in ("position_ids", "query_position_offsets", "key_position_offsets"):
values = getattr(self, field_name)
if values is None:
continue
normalized = _integer_tuple(values, field_name)
if not normalized or any(value < 0 for value in normalized):
raise AttentionContractError(f"{field_name} must contain non-negative positions")
object.__setattr__(self, field_name, normalized)
🧰 Tools
🪛 Ruff (0.16.1)

[error] 1078-1078: Import field from line 14 shadowed by loop variable

(F402)

🤖 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 `@rl_engine/kernels/attention_contract.py` around lines 1078 - 1085, Rename the
loop variable field to a non-conflicting name in the shown normalization loop
and the corresponding loops in AttentionContract.__post_init__ and
AttentionBackendCapability.__post_init__. Update all references within each
loop, preserving the existing behavior while avoiding shadowing the imported
dataclasses.field.

Sources: Linters/SAST tools, Pipeline failures

Comment on lines +457 to +466
"requested_split_kv_policy": (
"disabled" if kv_chunk_size is None else "fixed"
),
"requested_split_kv_size": kv_chunk_size,
"actual_split_kv_plans": split_kv_execution_plan_provenance(
k.size(2),
cp_world_size=cp_world_size,
kv_chunk_size=kv_chunk_size,
backend="deterministic_cp_backward_reference",
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C6 'empty_kv|size\(2\) == 0|skv == 0' rl_engine/kernels/ops/pytorch/attention/cp_attention.py tests/test_cp_attention.py

# Inspect the shared QKV validator to see whether an empty KV sequence is rejected earlier.
ast-grep run --pattern 'def _validate_qkv($$$) {
  $$$
}' --lang python rl_engine || rg -n -A30 'def _validate_qkv' rl_engine

Repository: RL-Align/RL-Kernel

Length of output: 6865


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- empty-KV backward test ---'
sed -n '271,315p' tests/test_cp_attention.py

printf '%s\n' '--- backward_reference and provenance ---'
rg -n -A45 -B12 'def backward_reference|split_kv_execution_plan_provenance|actual_split_kv_plans' \
  rl_engine/kernels/ops/pytorch/attention/cp_attention.py

printf '%s\n' '--- all provenance callers ---'
rg -n -C5 'split_kv_execution_plan_provenance\(' rl_engine tests

Repository: RL-Align/RL-Kernel

Length of output: 15777


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '409,480p' rl_engine/kernels/ops/pytorch/attention/cp_attention.py
sed -n '530,565p' rl_engine/kernels/ops/pytorch/attention/cp_attention.py
rg -n -C8 'backward_reference\(' tests rl_engine

Repository: RL-Align/RL-Kernel

Length of output: 10355


Handle empty-KV provenance in backward

backward_reference supports empty KV tensors through the forward and autograd steps, but split_kv_execution_plan_provenance rejects length 0. Return an empty plan list for empty KV input and add a regression test.

🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py` around lines 457 -
466, Update split_kv_execution_plan_provenance to return an empty plan list when
the KV sequence length is zero, before validation rejects the input. Preserve
existing planning behavior for positive lengths, and add a regression test
covering the backward_reference path with empty KV tensors.

Comment on lines +794 to +804
diff = (candidate.float() - reference.float()).abs().reshape(-1)
active_count = int(diff.numel())
if active_count == 0:
return GradientDriftStats(0.0, 0.0, 0.0, 0.0, 0)
return GradientDriftStats(
max_abs=float(diff.max().item()),
mean_abs=float(diff.mean().item()),
p95_abs=float(torch.quantile(diff, 0.95).item()),
p99_abs=float(torch.quantile(diff, 0.99).item()),
active_count=active_count,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

torch.quantile input tensor too large limit 16777216 PyTorch 2.4

💡 Result:

The RuntimeError: quantile input tensor is too large error occurs in PyTorch because torch.quantile historically imposed a limit of 2^24 (16,777,216) elements when processing float32 tensors [1][2]. This limitation existed because the calculation of quantile ranks—specifically the formula rank = q * (size - 1)—was performed using the input tensor's own dtype (float32) [1]. Since float32 can only exactly represent integers up to 2^24, values exceeding this limit led to loss of precision, where distinct ranks would alias to the same index and interpolation weights would quantize incorrectly [1]. To prevent producing silent, inaccurate results, PyTorch implemented a safety check that raised this error [1]. In PyTorch 2.4 and later versions, this behavior has been updated to support larger float32 tensors by performing rank calculations in float64 (double precision), which maintains precision well beyond the 2^24 limit [1]. Key points: - Historical Limit: The 16,777,216 element cap was a safety mechanism to avoid floating-point precision errors during rank calculations [1]. - Updated Behavior: Recent updates (targeting versions including and following the 2.4 release cycle) allow float32 tensors larger than 2^24 to be processed by using float64 for intermediate rank arithmetic [1]. - Device Specifics: The update is device-aware; while most devices benefit from this, hardware or backends lacking float64 support (such as certain MPS configurations) may still maintain the original cap [1]. If you are encountering this error on an older version of PyTorch, the standard workarounds included using float64 inputs (if memory allows) or manually splitting the tensor into smaller chunks [2]. However, upgrading to the latest stable version of PyTorch is the primary resolution for this issue [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(pyproject\.toml|setup\.cfg|setup\.py|requirements[^/]*|environment[^/]*|Dockerfile|[^/]*\.ya?ml)$|rl_engine/kernels/ops/pytorch/attention/cp_attention\.py'
printf '%s\n' '--- PyTorch version references ---'
rg -n -i 'torch(==|>=|<=|~=|>|<)|pytorch(==|>=|<=|~=|>|<)|torch:[[:space:]]|pytorch:' . --glob '!**/.git/**' --glob '!**/node_modules/**' | head -200
printf '%s\n' '--- relevant source context ---'
sed -n '760,820p' rl_engine/kernels/ops/pytorch/attention/cp_attention.py

Repository: RL-Align/RL-Kernel

Length of output: 3637


🌐 Web query:

PyTorch 2.4.1 torch.quantile "input tensor is too large" 2^24 source

💡 Result:

The RuntimeError: quantile input tensor is too large in PyTorch is caused by a historical limitation where the internal rank calculation for torch.quantile was performed using the input tensor's own dtype [1]. When using float32, ranks above 2^24 (approximately 16.7 million elements) could not be represented exactly, leading to potential inaccuracies in interpolation and index gathering [1]. To prevent silently incorrect results, PyTorch implemented an explicit cap that raised this error [1][2]. Recent developments, including pull request #187574, have addressed this by switching the internal rank calculations to float64, which allows for exact representation of indices up to 2^53 [1]. While this update improves support for large tensors, the following context remains important: 1. Device-specific limitations: On devices like MPS (Metal Performance Shaders) that lack float64 support, the 2^24 element cap remains in place for float32 tensors [1]. 2. Workarounds: If you encounter this error in older versions of PyTorch or on unsupported hardware/dtypes, common workarounds include: * Using torch.kthvalue, which does not share this limitation and is often more performant for single quantile calculations [2][3]. * Implementing custom quantile functions using torch.kthvalue or sorting (though sorting can be memory-intensive) [2][3]. * Reducing the size of the input tensor (e.g., through sampling or processing in chunks) if an exact full-tensor quantile is not required for the application [4][5]. As of August 2026, ensure your environment is updated to a recent PyTorch version to benefit from the improved large-tensor support [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
for path in [Path("pyproject.toml"), Path("setup.py"), Path("ci/run_gpu_ci.sh")]:
    print(f"--- {path} ---")
    lines = path.read_text().splitlines()
    for i, line in enumerate(lines, 1):
        if "torch" in line.lower() or "TORCH_SPEC" in line:
            lo, hi = max(1, i - 3), min(len(lines), i + 3)
            for n in range(lo, hi + 1):
                print(f"{n}: {lines[n-1]}")
PY
printf '%s\n' '--- percentile-related tests and callers ---'
rg -n '_drift_stats|p95_abs|p99_abs|GradientDriftStats|attention_backward_drift' . --glob '!**/.git/**'

Repository: RL-Align/RL-Kernel

Length of output: 15944


🏁 Script executed:

#!/bin/bash
set -eu
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
for ref in v2.4.1 v2.5.0 v2.6.0; do
  url="https://raw.githubusercontent.com/pytorch/pytorch/$ref/aten/src/ATen/native/Sorting.cpp"
  echo "--- $ref Sorting.cpp quantile guards ---"
  curl -fsSL "$url" | rg -n -C 8 'quantile input tensor is too large|2\^24|16777216|quantile|kthvalue' | head -180 || true
done
echo '--- upstream PR metadata ---'
curl -fsSL https://api.github.com/repos/pytorch/pytorch/pulls/187574 |
  jq '{number,title,state,merged_at,merge_commit_sha,base:.base.ref,head:.head.ref}'
echo '--- v2.4.1 quantile references in source tree listing ---'
curl -fsSL 'https://api.github.com/repos/pytorch/pytorch/git/trees/v2.4.1?recursive=1' |
  jq -r '.tree[].path' | rg 'quantile|Sorting\.(cpp|cu|h)$' | head -100

Repository: RL-Align/RL-Kernel

Length of output: 14645


Make percentile calculation safe for tensors larger than 2²⁴.

PyTorch 2.4.1 raises RuntimeError: quantile() input tensor is too large above 2**24 elements. CI installs torch==2.4.1, and realistic gradients can exceed this limit. Replace torch.quantile with a large-tensor-safe reduction. Preserve its default linear interpolation; rounded indexing after torch.sort changes p95 and p99 values.

🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py` around lines 794 -
804, Update the percentile calculations in the GradientDriftStats construction
to avoid torch.quantile for tensors exceeding 2**24 elements, while preserving
default linear interpolation semantics for p95_abs and p99_abs. Use a
large-tensor-safe reduction based on sorting and interpolation, and keep the
existing empty-tensor handling and other statistics unchanged.

Comment on lines +1019 to +1035
CPAttentionReferenceOp = DeterministicCPAttentionReferenceOp

__all__ = [
"AttentionBackwardComparisonReport",
"AttentionBackwardGradients",
"AttentionBackwardPathDrift",
"AttentionBackwardPathResult",
"AttentionBackwardRankDrift",
"AttentionPartialState",
"build_reference_split_kv_runtime_plan_set",
"CPAttentionReferenceOp",
"DeterministicCPAttentionReferenceOp",
"GradientDriftStats",
"compare_cp_attention_backward",
"merge_attention_partial_states",
"split_kv_execution_plan_provenance",
]

Copy link
Copy Markdown

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

The alias CPAttentionReferenceOp is still present.

A previous review asked to delete the alias, and the PR description states that the alias was removed. Line 1019 still defines it, and Line 1029 still exports it. Remove both lines, or state why the alias must stay.

♻️ Proposed removal
-CPAttentionReferenceOp = DeterministicCPAttentionReferenceOp
-
 __all__ = [
     "AttentionBackwardComparisonReport",
     "AttentionBackwardGradients",
     "AttentionBackwardPathDrift",
     "AttentionBackwardPathResult",
     "AttentionBackwardRankDrift",
     "AttentionPartialState",
     "build_reference_split_kv_runtime_plan_set",
-    "CPAttentionReferenceOp",
     "DeterministicCPAttentionReferenceOp",

Run the following script to find any remaining importers before removal:

#!/bin/bash
set -euo pipefail

rg -n --type=py -C2 '\bCPAttentionReferenceOp\b'
🧰 Tools
🪛 Ruff (0.16.1)

[warning] 1021-1035: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

🤖 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 `@rl_engine/kernels/ops/pytorch/attention/cp_attention.py` around lines 1019 -
1035, Remove the CPAttentionReferenceOp alias assignment and its corresponding
__all__ export; use DeterministicCPAttentionReferenceOp directly and verify no
remaining importers reference the removed alias.

@inaniloquentee
inaniloquentee changed the base branch from main to test August 12, 2026 16:13
Signed-off-by: lamentropetion <3051000145@qq.com>
Signed-off-by: lamentropetion <3051000145@qq.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.

3 participants