Skip to content

feat(kernels): add Triton batch-invariant attention#246

Open
EthanZero2Hero wants to merge 1 commit into
RL-Align:mainfrom
EthanZero2Hero:feat/issue-147-triton-attn
Open

feat(kernels): add Triton batch-invariant attention#246
EthanZero2Hero wants to merge 1 commit into
RL-Align:mainfrom
EthanZero2Hero:feat/issue-147-triton-attn

Conversation

@EthanZero2Hero

@EthanZero2Hero EthanZero2Hero commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #147.

This PR adds a Triton standard-softmax attention baseline for WS1 batch-invariant forward validation. The kernel uses a fixed key-order reduction for max, sum-exp/LSE, and P @ V, supports causal and key-padding masks, and is wired into the #108 operator harness as the attention/triton candidate.

Summary by CodeRabbit

  • New Features

    • Added a Triton-accelerated attention operator with support for causal and non-causal modes, configurable scaling, key padding masks, and grouped-query attention.
    • Supports returning both the attention output and log-sum-exp (LSE) statistics for improved workflows.
  • Tests

    • Added CUDA-focused coverage validating forward parity with the native implementation, LSE correctness, masking/padding-layout invariance, decode vs. prefill behavior, and gradient correctness (including invariance cases).

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e8048a9b-8db4-434f-9cb1-8cf393b3df15

📥 Commits

Reviewing files that changed from the base of the PR and between 323c840 and 71756b9.

📒 Files selected for processing (4)
  • rl_engine/kernels/gtest/operator_specs.py
  • rl_engine/kernels/ops/triton/attention/__init__.py
  • rl_engine/kernels/ops/triton/attention/standard_attn.py
  • tests/test_triton_batch_invariant_attention.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • rl_engine/kernels/ops/triton/attention/init.py
  • rl_engine/kernels/gtest/operator_specs.py
  • rl_engine/kernels/ops/triton/attention/standard_attn.py
  • tests/test_triton_batch_invariant_attention.py

📝 Walkthrough

Walkthrough

Adds a Triton batch-invariant standard-softmax attention operator with causal and padding masks, LSE output, native-autograd backward fallback, public exports, operator registration, and CUDA tests.

Changes

Attention operator

Layer / File(s) Summary
Triton attention execution
rl_engine/kernels/ops/triton/attention/standard_attn.py
Implements the Triton forward kernel, input validation, optional LSE output, scale handling, and native PyTorch gradient computation.
Operator and package wiring
rl_engine/kernels/ops/triton/attention/__init__.py, rl_engine/kernels/gtest/operator_specs.py
Exports the Triton attention API and registers native, PyTorch, and Triton attention implementations.
CUDA behavior validation
tests/test_triton_batch_invariant_attention.py
Tests forward and backward parity, LSE values, masking and padding-layout invariance, batch-position invariance, and decode/prefill equivalence.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant TritonBatchInvariantAttentionOp
  participant TritonAttentionKernel
  participant NativeAttentionOp
  Caller->>TritonBatchInvariantAttentionOp: submit q, k, v and masks
  TritonBatchInvariantAttentionOp->>TritonAttentionKernel: compute attention output and LSE
  TritonAttentionKernel-->>Caller: return output
  Caller->>TritonBatchInvariantAttentionOp: request backward gradients
  TritonBatchInvariantAttentionOp->>NativeAttentionOp: recompute forward with autograd
  NativeAttentionOp-->>Caller: return q, k, and v gradients
Loading

Possibly related issues

Possibly related PRs

  • RL-Align/RL-Kernel#188 — Provides the NativeAttentionOp.forward_fp32 reference used for registration and Triton backward computation.
  • RL-Align/RL-Kernel#236 — Relates to the attention LSE export and backend attention contract.
  • RL-Align/RL-Kernel#240 — Also updates the attention operator specification for the gtest harness.

Suggested labels: needs-gpu-ci

Suggested reviewers: kjldefeated, inaniloquentee, flink-ddd

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding Triton batch-invariant attention support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

🧹 Nitpick comments (3)
tests/test_triton_batch_invariant_attention.py (1)

46-58: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for a fully-masked query row.

The kernel has dedicated branches for that path — tl.where(max_score == -inf, 0.0, probs) (Line 128) and tl.where(denom > 0.0, acc / denom, 0.0) (Line 142) — but no test drives them. mask[1, 13:] = False still leaves valid keys in every row. A row whose every key is padded is exactly where NaN would leak, and it's also where the kernel's lse = -inf convention diverges from anything the native op returns.

Suggest a case with an all-False mask row asserting finite zero output (matching NativeAttentionOp's fully-masked-row convention) and -inf LSE. Want me to draft it?

Also applies to: 102-153

🤖 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_triton_batch_invariant_attention.py` around lines 46 - 58, Add
coverage in test_triton_attention_matches_native_forward for a query row whose
key_padding_mask is entirely False, using the existing Triton and
NativeAttentionOp paths. Assert that the fully masked row produces finite zero
output matching the native convention, and verify its LSE value is -inf while
preserving the existing dtype and tolerance checks.
rl_engine/kernels/ops/triton/attention/standard_attn.py (2)

239-256: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Document the fallback's memory profile and honor ctx.needs_input_grad.

NativeAttentionOp.forward materializes the full [B, Hq, Sq, Skv] score and probability tensors, so backward peak memory scales quadratically in sequence length even though the forward kernel does not — it will OOM at production prefill shapes where the forward succeeds. Also, grads are always computed for all three inputs regardless of needs_input_grad, and create_graph/double-backward isn't supported.

Suggest a class docstring stating this is a validation-only backward, plus:

♻️ Honor `needs_input_grad`
-        dq, dk, dv = torch.autograd.grad(out, (q_ref, k_ref, v_ref), grad_out)
-        return dq, dk, dv, None, None, None, None
+        wanted = [
+            t for t, need in zip((q_ref, k_ref, v_ref), ctx.needs_input_grad[:3]) if need
+        ]
+        grads = iter(torch.autograd.grad(out, wanted, grad_out) if wanted else ())
+        dq, dk, dv = (
+            next(grads) if need else None for need in ctx.needs_input_grad[:3]
+        )
+        return dq, dk, dv, None, None, None, None
🤖 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/triton/attention/standard_attn.py` around lines 239 -
256, Update StandardAttention’s backward fallback around
NativeAttentionOp.forward to document that it is validation-only, uses quadratic
[B, Hq, Sq, Skv] memory, and does not support create_graph or double backward.
Honor ctx.needs_input_grad by enabling gradients and requesting
torch.autograd.grad only for required q, k, and v inputs, while returning None
for inputs that do not need gradients and preserving the existing non-tensor
argument returns.

191-191: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Consider tiling queries instead of one program per query row.

grid = (q_len, q_heads, batch) with num_warps=8 gives each program a single query vector and a (BLOCK_N, BLOCK_D) tile, so all K/V traffic is re-read per row and the warps are likely underutilized on the D-reduction. A BLOCK_M query tile would amortize K/V loads across rows while keeping the key-order reduction fixed (batch invariance holds as long as BLOCK_M/BLOCK_N are shape-independent constants). Worth deferring if this baseline is only for validation, not throughput.

Also applies to: 228-228

🤖 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/triton/attention/standard_attn.py` at line 191, Update
the attention kernel launch and corresponding kernel indexing around the grid
definitions to process a shape-independent BLOCK_M tile of query rows rather
than one program per query row. Adjust the query, output, and masking logic to
handle the tiled M dimension while preserving the fixed key-order reduction and
batch/head behavior; apply the same change to the second grid definition
referenced in the review.
🤖 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/triton/attention/standard_attn.py`:
- Around line 6-13: Apply the isort-generated ordering to the import block in
rl_engine/kernels/ops/triton/attention/standard_attn.py (lines 6-13), then apply
the corresponding isort ordering to the import block in
tests/test_triton_batch_invariant_attention.py (lines 6-13). Run pre-commit
across all files to verify both import blocks pass linting.
- Around line 235-237: Update the autograd Function forward path around the
return_lse branch to call ctx.mark_non_differentiable on lse before returning it
with out. Keep out differentiable and preserve the existing return behavior when
return_lse is false.
- Around line 31-57: Update the kernel parameter declarations to remove
tl.constexpr from B, S_Q, S_KV, all 16 tensor stride parameters, and sm_scale;
leave BLOCK_N, BLOCK_D, and boolean flags such as CAUSAL and
HAS_KEY_PADDING_MASK constexpr. Since B is unused in the kernel, remove it from
the signature and update its launch arguments accordingly, while passing the
remaining shapes, strides, and scale as runtime values.

---

Nitpick comments:
In `@rl_engine/kernels/ops/triton/attention/standard_attn.py`:
- Around line 239-256: Update StandardAttention’s backward fallback around
NativeAttentionOp.forward to document that it is validation-only, uses quadratic
[B, Hq, Sq, Skv] memory, and does not support create_graph or double backward.
Honor ctx.needs_input_grad by enabling gradients and requesting
torch.autograd.grad only for required q, k, and v inputs, while returning None
for inputs that do not need gradients and preserving the existing non-tensor
argument returns.
- Line 191: Update the attention kernel launch and corresponding kernel indexing
around the grid definitions to process a shape-independent BLOCK_M tile of query
rows rather than one program per query row. Adjust the query, output, and
masking logic to handle the tiled M dimension while preserving the fixed
key-order reduction and batch/head behavior; apply the same change to the second
grid definition referenced in the review.

In `@tests/test_triton_batch_invariant_attention.py`:
- Around line 46-58: Add coverage in
test_triton_attention_matches_native_forward for a query row whose
key_padding_mask is entirely False, using the existing Triton and
NativeAttentionOp paths. Assert that the fully masked row produces finite zero
output matching the native convention, and verify its LSE value is -inf while
preserving the existing dtype and tolerance checks.
🪄 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: 4f5c745f-b212-4a99-8fa7-45457bf53c02

📥 Commits

Reviewing files that changed from the base of the PR and between 063a261 and 323c840.

📒 Files selected for processing (4)
  • rl_engine/kernels/gtest/operator_specs.py
  • rl_engine/kernels/ops/triton/attention/__init__.py
  • rl_engine/kernels/ops/triton/attention/standard_attn.py
  • tests/test_triton_batch_invariant_attention.py

Comment thread rl_engine/kernels/ops/triton/attention/standard_attn.py
Comment on lines +31 to +57
B: tl.constexpr,
H_Q: tl.constexpr,
H_KV: tl.constexpr,
S_Q: tl.constexpr,
S_KV: tl.constexpr,
D: tl.constexpr,
stride_qb: tl.constexpr,
stride_qh: tl.constexpr,
stride_qs: tl.constexpr,
stride_qd: tl.constexpr,
stride_kb: tl.constexpr,
stride_kh: tl.constexpr,
stride_ks: tl.constexpr,
stride_kd: tl.constexpr,
stride_vb: tl.constexpr,
stride_vh: tl.constexpr,
stride_vs: tl.constexpr,
stride_vd: tl.constexpr,
stride_ob: tl.constexpr,
stride_oh: tl.constexpr,
stride_os: tl.constexpr,
stride_od: tl.constexpr,
sm_scale: tl.constexpr,
BLOCK_N: tl.constexpr,
BLOCK_D: tl.constexpr,
CAUSAL: tl.constexpr,
HAS_KEY_PADDING_MASK: tl.constexpr,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="rl_engine/kernels/ops/triton/attention/standard_attn.py"

if [ -f "$file" ]; then
  echo "== file size =="
  wc -l "$file"
  echo
  echo "== outline =="
  ast-grep outline "$file" 2>/dev/null || true
  echo
  echo "== relevant source =="
  sed -n '1,240p' "$file" | cat -n
else
  echo "File not found: $file"
  echo "Matching files:"
  fd -i 'standard_attn.py' .
fi

echo
echo "== usages / launches of standard_attn or fwd function name =="
rg -n "def .*standard|standard_attn|triton\.jit|stride_qb|stride_qh|stride_qs|stride_qd|HAS_KEY_PADDING_MASK|CAUSAL" .

echo
echo "== triton dependency specification =="
rg -n "triton|vllm-ascend" pyproject.toml requirements*.txt setup.py setup.cfg 2>/dev/null || true

Repository: RL-Align/RL-Kernel

Length of output: 14722


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import ast, re
from pathlib import Path

path = Path("rl_engine/kernels/ops/triton/attention/standard_attn.py")
src = path.read_text()
tree = ast.parse(src)

fn = tree.body[1]
assert isinstance(fn, ast.FunctionDef) and fn.name == "_standard_attn_fwd_kernel"
params = [(a.arg, isinstance(a.annotation, ast.Attribute) and
          isinstance(a.annotation.value, ast.Name) and
          a.annotation.value.id == "tl" and
          a.annotation.attr == "constexpr")
          for a in fn.args.args]

name_count = {}
for node in ast.walk(fn):
    if isinstance(node, ast.Name):
        name_count[node.id] = name_count.get(node.id, 0) + 1

print("parameter_constexpr_dict=", dict(params))
print("unused_params=", [name for name, annotated in params if name not in name_count])
print("B_use_count=", name_count.get("B", 0))

launch = None
for node in tree.body:
    if isinstance(node, ast.ClassDef) and node.name == "_TritonBatchInvariantAttention":
        for item in node.body:
            if isinstance(item, ast.FunctionDef) and item.name == "forward":
                for subnode in ast.walk(item):
                    if isinstance(subnode, ast.Call):
                        try:
                            name = ast.unparse(subnode.func)
                        except Exception:
                            name = ""
                        if name == "_standard_attn_fwd_kernel[grid]":
                            launch = subnode
print("launch_found=", launch is not None)
if launch:
    positional_names = []
    for node in launch.args.args:
        try:
            positional_names.append(ast.unparse(node))
        except Exception:
            positional_names.append("<expr>")
    print("launch_positional_keywords=", [ast.unparse(node) for node in launch.keywords])
    print("launch_positional_args=", positional_names)
PY

Repository: RL-Align/RL-Kernel

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import ast, re
from pathlib import Path

path = Path("rl_engine/kernels/ops/triton/attention/standard_attn.py")
src = path.read_text()
tree = ast.parse(src)

for item in tree.body:
    if isinstance(item, ast.FunctionDef):
        print("FUNCTION", item.lineno, item.name, item.args.args[0].lineno if item.args.args else None)
    if isinstance(item, ast.ClassDef):
        print("CLASS", item.lineno, item.name)

# Locate target function by source text.
match = re.search(r'def\r?\n[^()]*\(_standard_attn_fwd_kernel\s*\(', src, re.S)
print("text_target_found=", bool(match))

fn = tree.body[1] if len(tree.body) > 1 else None
params = [(a.arg, isinstance(a.annotation, ast.Attribute) and
          isinstance(a.annotation.value, ast.Name) and
          a.annotation.value.id == "tl" and
          a.annotation.attr == "constexpr")
          for a in fn.args.args] if fn else []

name_count = {}
if fn:
    for node in ast.walk(fn):
        if isinstance(node, ast.Name):
            name_count[node.id] = name_count.get(node.id, 0) + 1

print("parameter_constexpr_dict=", dict(params))
print("unused_params=", [name for name, annotated in params if annotated and name not in name_count])
print("B_use_count=", name_count.get("B", 0))
PY

echo "== launch call structure =="
python3 - <<'PY'
import ast, re
from pathlib import Path
path = Path("rl_engine/kernels/ops/triton/attention/standard_attn.py")
src = path.read_text()
tree = ast.parse(src)

# Print the exact function signature as declared in source.
lines = src.splitlines()
for start in [idx for idx,l in enumerate(lines,1) if 'def _standard_attn_fwd_kernel(' in l][0:1]:
    print("\n".join(f"{i:4}: {lines[i-1]}" for i in range(start, start+45)))

# Scan source text for index call with no keyword matching B.
for i,line in enumerate(lines):
    if '_standard_attn_fwd_kernel[grid](' in line:
        print("INDEX_CALL_LINE", i+1, line.strip())
        brace=lines[i].index('_standard_attn_fwd_kernel[grid](')
        depth=0
        text=''; idx=-1
        end=len(lines)
        while idx < len(lines)-1:
            idx += 1
            chunk=lines[idx]
            depth += chunk.count('(') - chunk.count(')')
            text += '\n'+chunk
            if depth <= 0:
                break
        print("INDEX_CALL_BODY")
        print(text)
PY

Repository: RL-Align/RL-Kernel

Length of output: 608


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import ast
from pathlib import Path

path = Path("rl_engine/kernels/ops/triton/attention/standard_attn.py")
src = path.read_text()
tree = ast.parse(src)

# Find target function by name.
fn = next(
    item for item in tree.body if isinstance(item, ast.FunctionDef) and item.name == "_standard_attn_fwd_kernel"
)

params = dict(
    (a.arg, isinstance(a.annotation, ast.Attribute) and
      isinstance(a.annotation.value, ast.Name) and
      a.annotation.value.id == "tl" and a.annotation.attr == "constexpr")
    for a in fn.args.args
)

name_count = {}
for node in ast.walk(fn):
    if isinstance(node, ast.Name):
        name_count[node.id] = name_count.get(node.id, 0) + 1

print("parameter_constexpr_dict=")
for k, v in params.items():
    print(f"  {k}: {v}")
print("unused_params=", [name for name, annotated in params.items() if annotated and name not in name_count])
print("B_use_count=", name_count.get("B", 0))

# Find target launch call by exact function name.
launch = next(
    (node for node in ast.walk(tree)
     if isinstance(node, ast.Call)
     and all(getattr(getattr(node.func, "value", None), "id", None) == "_standard_attn_fwd_kernel"
             and getattr(node.func, "attr", None) == "__getitem__"
             and getattr(getattr(node.func, "slice", None), "id", None) == "grid")),
    None
)
print("launch_found=", launch is not None)
if launch:
    positional_names = []
    for node in launch.args.args:
        try:
            positional_names.append(ast.unparse(node))
        except Exception:
            positional_names.append("<expr>")
    print("launch_positional_args=", positional_names)
    print("launch_keyword_names=", [kw.arg for kw in launch.keywords])
    print("launch_keyword_names_without_B=", [kw.arg for kw in launch.keywords if kw.arg != "B"])
PY

Repository: RL-Align/RL-Kernel

Length of output: 921


🌐 Web query:

Triton jit constexpr parameters compile-time constants each distinct value creates separate LLVM IR specialization

💡 Result:

Yes, in Triton, parameters annotated with tl.constexpr are treated as compile-time constants, and each distinct value passed to these parameters triggers a separate kernel recompilation, resulting in a unique LLVM IR specialization [1][2]. Because tl.constexpr values are "baked into" the compiled program, they allow the Triton compiler to perform aggressive optimizations based on these fixed values [3][1]. Consequently, whenever a kernel is called with a new or different value for a constexpr parameter, the Triton JIT runtime detects this as a change to the function's signature and compiles a new version of the kernel [4][1]. This behavior is distinct from standard kernel parameters, which are designed to be dynamic; using tl.constexpr for values that change frequently can lead to a "compilation explosion," where excessive recompilation significantly increases the total execution time [5][1]. If a parameter needs to change frequently without triggering recompilation, developers should avoid the tl.constexpr annotation and instead use standard parameters or rely on the do_not_specialize flag, which prevents the compiler from baking the value into the IR [1][6].

Citations:


De-constexpr runtime shapes, strides, and sm_scale.

B, S_Q, S_KV, the 16 tensor strides, and sm_scale are runtime values that vary with rollout batch/sequence/layout/scale. Marking them tl.constexpr creates a separate specialized Triton binary for each distinct value, while B is not referenced in the kernel at all. Keep tile sizes and boolean compilation flags as constexpr; pass the rest as runtime arguments.

🤖 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/triton/attention/standard_attn.py` around lines 31 -
57, Update the kernel parameter declarations to remove tl.constexpr from B, S_Q,
S_KV, all 16 tensor stride parameters, and sm_scale; leave BLOCK_N, BLOCK_D, and
boolean flags such as CAUSAL and HAS_KEY_PADDING_MASK constexpr. Since B is
unused in the kernel, remove it from the signature and update its launch
arguments accordingly, while passing the remaining shapes, strides, and scale as
runtime values.

Comment on lines +235 to +237
if return_lse:
return out, lse
return out

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

Mark lse non-differentiable; grads flowing into it are silently dropped.

lse is returned from an autograd.Function without ctx.mark_non_differentiable, so it carries requires_grad=True when q/k/v do. backward only consumes grad_outputs[0], so if a caller uses lse in a loss the contribution is discarded silently instead of erroring.

🐛 Proposed fix
         if return_lse:
+            ctx.mark_non_differentiable(lse)
             return out, lse
         return out
📝 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
if return_lse:
return out, lse
return out
if return_lse:
ctx.mark_non_differentiable(lse)
return out, lse
return out
🤖 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/triton/attention/standard_attn.py` around lines 235 -
237, Update the autograd Function forward path around the return_lse branch to
call ctx.mark_non_differentiable on lse before returning it with out. Keep out
differentiable and preserve the existing return behavior when return_lse is
false.

@EthanZero2Hero
EthanZero2Hero force-pushed the feat/issue-147-triton-attn branch from 323c840 to 71756b9 Compare July 25, 2026 14:16
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.

[WS1][kernels] Batch-invariant attention (standard softmax)

1 participant