Skip to content

Add nvfp4 attention support for vLLM serving#1898

Merged
kaix-nv merged 24 commits into
mainfrom
kaix/sparse_attn_quant_compact
Jul 16, 2026
Merged

Add nvfp4 attention support for vLLM serving#1898
kaix-nv merged 24 commits into
mainfrom
kaix/sparse_attn_quant_compact

Conversation

@kaix-nv

@kaix-nv kaix-nv commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: New feature

This PR adds an NVFP4+sparse attention serving path for vLLM and consolidates it with the existing checkpoint-driven sparse-attention integration.

NVFP4 attention

  • Applies dynamic block-16 NVFP4 fake quantization to Q/K/P/V.
  • Quantizes K before the KV-cache write. V remains pristine until a complete 16-token group can be finalized once; an incomplete tail is QDQ on read.
  • Adds paged/chunked prefill support and a dedicated split-K decode kernel. Decode uses a fixed 32-split, 128-key-tile schedule because split-local P quantization is part of the numerical contract.

vLLM integration

  • Provides one consolidated worker module:
    • SparseAttnWorker for checkpoint-driven sparse attention.
    • QuantSparseAttnWorker for fixed NVFP4 Q/K/P/V plus optional checkpoint sparsity.
  • Supports the native vLLM FlashAttention and FlashInfer backends. The worker replaces only the selected implementation and delegates inactive launches back to that backend.
  • Preserves FlashInfer NHD/HND cache layouts and separates mixed decode/prefill launches so each phase uses the correct kernel contract.
  • Restores checkpoint-driven N:M sparse-softmax and skip-softmax metadata. N:M remains a prefill transform; calibrated skip-softmax decode uses the shared paged kernel.
  • Validates the complete attention plan before modifying the model and reports unsupported configurations without leaving a partially converted model.

Supported configurations are regular decoder self-attention with vLLM >= 0.15.0, FlashInfer or FlashAttention, fp16/bf16 model and KV cache, equal Q/K/V head dimensions divisible by 16, and DCP 1. The README documents unsupported features and CUDA-graph constraints.

Usage

Let vLLM select the backend for the model and platform. For example, Nemotron-H on Blackwell selects FlashInfer automatically.

cd examples/vllm_serve

python vllm_serve_sparse_attn.py <MODEL_PATH> -tp 8 \
  --no-enable-prefix-caching \
  --worker-cls sparse_attn_worker.QuantSparseAttnWorker

If <MODEL_PATH>/config.json contains sparse_attention_config, the same worker also applies its N:M or skip-softmax settings. Otherwise, it runs NVFP4 attention only.

Testing

Focused attention kernels:

PYTEST_VERSION=1 PYTHONPATH=$PWD python -m pytest -q \
  tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py \
  tests/gpu/torch/kernels/common/attention/test_decode_attention.py \
  tests/gpu/torch/kernels/common/attention/test_triton_fa_paged.py
  • B200 NVFP4 run before final test deduplication: 47 passed.
  • Current RTX A6000 run: 23 passed, 21 skipped. The skips require native E4M3 support (compute capability >= 8.9).

Focused vLLM worker and adapter tests:

PYTHONPATH=$PWD python -m pytest -q \
  tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py \
  tests/gpu_vllm/torch/sparsity/attention_sparsity/test_quant_sparse_attn_worker.py

Result: 43 passed.

CPU import and launch-contract tests:

PYTHONPATH=$PWD python -m pytest -q \
  tests/unit/torch/kernels/common/attention/test_triton_fa.py

Result: 9 passed.

GitHub Linux, Windows, multi-version, code-quality, documentation, and required unit checks pass.

Before your PR is "Ready for review"

Make sure you read and follow Contributor guidelines and your commits are signed (git commit -s -S).

Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded trust_remote_code=True, torch.load(..., weights_only=False), pickle, etc.).

  • Is this change backward compatible?: ✅ — both serving policies are opt-in; sparse-only remains the launcher default.
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A — no copied code or new dependency.
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: N/A — the example README documents this opt-in serving path; no stable public API changed.
  • Did you get Claude approval on this PR?: N/A

Additional Information

The fixed attention recipe intentionally does not expose the integration branch's environment-variable matrix. Backend selection is automatic unless an explicit vLLM backend override is needed.

Summary by CodeRabbit

  • New Features
    • Added quant+sparse vLLM serving worker support and clarified compact NVFP4 attention worker behavior.
    • Introduced paged split-K decode and NVFP4 QDQ, including on-write V-cache quantization and new decode attention API.
    • Expanded vLLM runtime installation to support NVFP4 quantization and sparse attention with FlashInfer metadata compatibility.
  • Bug Fixes
    • Improved NVFP4 degenerate/underflow scale handling to reliably zero out blocks.
    • Strengthened validation for paged-cache and NVFP4 quantization contracts.
  • Documentation
    • Updated the vLLM sparse-attention example docs with tested versions and explicit limitations.
  • Tests
    • Expanded GPU correctness and integration coverage for split-K decode, NVFP4 QDQ, vLLM workers, and runtime installation paths.

@copy-pr-bot

copy-pr-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

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 split-K NVFP4 paged decode attention, V QDQ and dense-token-aware sparsity in Triton FlashAttention, quantized V-cache writes, vLLM sparse/NVFP4 runtime installation with FlashInfer support, and updated serving workers, documentation, and tests.

Changes

NVFP4 quant-sparse attention kernels and vLLM integration

Layer / File(s) Summary
Split-K decode and V-cache quantization
modelopt/torch/kernels/common/attention/decode_attention.py, modelopt/torch/kernels/quantization/attention/*, tests/gpu/torch/kernels/common/attention/*
Adds split-K paged decode, NVFP4 P/V QDQ, on-write V-cache quantization, and correctness coverage.
Triton attention QDQ and sparsity
modelopt/torch/kernels/common/attention/triton_fa.py, tests/gpu/torch/kernels/common/attention/*, tests/unit/torch/kernels/common/attention/test_triton_fa.py
Adds V QDQ and quantized-cache support, dense-token-aware N:M sparsity, paged index widening, and unified launch validation.
Quantization contracts and scale handling
modelopt/torch/quantization/plugins/vllm.py, modelopt/torch/kernels/quantization/common/nvfp4_quant.py, tests/gpu/torch/quantization/*, tests/gpu_vllm/torch/quantization/*
Updates vLLM attention quantizer configuration, KV defaults, in-kernel Q/V behavior, and zero-scale NVFP4 handling.
vLLM sparse routing and FlashInfer
modelopt/torch/sparsity/attention_sparsity/plugins/*, tests/gpu_vllm/torch/sparsity/attention_sparsity/test_*
Adds runtime planning and installation, transform resolution, native fallback, phase-aware dispatch, FlashInfer metadata patching, and worker behavior tests.
Serving worker and documentation
examples/vllm_serve/*
Adds sparse and quant-sparse worker installation flows and documents supported configurations, limitations, and launcher options.

Estimated code review effort: 5 (Critical) | ~100 minutes

Suggested reviewers: meenchen, gcunhase, AAnoosheh, jenchen13, ChenhanYu, rohansjoshi, mxinO, kevalmorabia97, shengliangxu

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 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.
Security Anti-Patterns ✅ Passed Scanned all PR Python changes; found no torch.load(weights_only=False), numpy.load(allow_pickle=True), trust_remote_code=True, eval/exec, or # nosec, and no dependency-file changes.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding NVFP4 attention support to vLLM serving.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kaix/sparse_attn_quant_compact

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-16 06:17 UTC

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.71279% with 232 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.67%. Comparing base (f479e78) to head (c4e2146).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...torch/kernels/common/attention/decode_attention.py 15.57% 103 Missing ⚠️
...parsity/attention_sparsity/plugins/vllm_runtime.py 82.68% 49 Missing ⚠️
...t/torch/kernels/quantization/attention/bmm2_qdq.py 29.78% 33 Missing ⚠️
...delopt/torch/kernels/common/attention/triton_fa.py 41.50% 31 Missing ⚠️
.../torch/sparsity/attention_sparsity/plugins/vllm.py 95.60% 9 Missing ⚠️
...t/torch/kernels/quantization/common/nvfp4_quant.py 0.00% 4 Missing ⚠️
modelopt/torch/quantization/plugins/vllm.py 94.23% 3 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (f479e78) and HEAD (c4e2146). Click for more details.

HEAD has 4 uploads less than BASE
Flag BASE (f479e78) HEAD (c4e2146)
unit 2 1
gpu 4 3
examples 12 10
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1898      +/-   ##
==========================================
- Coverage   77.87%   68.67%   -9.21%     
==========================================
  Files         522      524       +2     
  Lines       58452    59132     +680     
==========================================
- Hits        45522    40606    -4916     
- Misses      12930    18526    +5596     
Flag Coverage Δ
examples 40.19% <6.78%> (-3.25%) ⬇️
gpu 32.56% <69.71%> (-26.01%) ⬇️
regression 14.90% <1.04%> (-0.10%) ⬇️
unit 54.88% <2.08%> (-0.52%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kaix-nv kaix-nv force-pushed the kaix/sparse_attn_quant_compact branch 5 times, most recently from 62c1a25 to 5ef7dea Compare July 7, 2026 23:09
@kaix-nv kaix-nv marked this pull request as ready for review July 8, 2026 20:51
@kaix-nv kaix-nv requested review from a team as code owners July 8, 2026 20:51
@kaix-nv kaix-nv requested review from Edwardf0t1, realAsma, rohansjoshi and sugunav14 and removed request for Edwardf0t1, realAsma, rohansjoshi and sugunav14 July 8, 2026 20:51

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

Bot review — DM the bot to share feedback.

Large NVFP4+sparse-attention vLLM serving PR (+3585/-617, 19 files). It extends the existing unified Triton FA kernel (adds symmetric V_QDQ next to the existing P_QDQ), adds a new split-K decode kernel, and adds a FlashInfer adapter alongside the existing FlashAttention adapter in the sparse-attn vLLM plugin. Design review: this is an extension of established in-repo subsystems (the unified triton_fa.py kernel and the attention_sparsity/plugins/vllm.py adapter) rather than a competing new system, and the new split-K decode kernel is justified by the split-local-P quantization contract in both the README and the module docstring. License headers on the new files match the canonical LICENSE_HEADER. Test coverage is extensive (GPU kernel tests, CPU wrapper tests, vLLM worker/adapter tests).

One correctness regression to fix, plus a couple of consistency questions.

Blocking / correctness:

  • fp4_kernel_hopper.py: removing the block_max_quant = tl.where(block_max_quant >= 1e-5, block_max_quant, 1.0) guard reintroduces a 0/0 = NaN for all-zero (or FP8-underflowing) blocks, since global_scale_safe only guards the global scale, not the per-block scale. This kernel has its own inline quant (it does NOT call nvfp4_scalar_quant), so the paired change in nvfp4_scalar_quant (which now correctly returns 0.0 for zero scale) does not cover it. This also diverges from the cuda_fp4 CUDA kernel and the Python reference _py_fp4_fake_quant_ref, both of which still clamp tiny scales to 1.0 — the test_vs_triton* cross-checks use randn*10 inputs that rarely hit zero/underflow blocks, so CI likely won't catch the divergence.

I did not exhaustively verify the mixed decode/prefill FlashInfer split math or the split-K online-softmax combine on-device; those paths are GPU-only and the unit tests cover the routing/metadata contracts well, but numerical correctness relies on the B200/A6000 runs reported in the PR body.

Comment thread modelopt/torch/kernels/quantization/gemm/fp4_kernel_hopper.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 2

🧹 Nitpick comments (1)
modelopt/torch/quantization/plugins/vllm.py (1)

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

Narrow the caught exception to avoid masking real import failures.

Catching bare ImportError also swallows failures caused by a genuinely broken vllm install (e.g. a transitive import error inside vllm.attention.layer itself), silently falling through to the next candidate and ultimately raising a generic "No supported vLLM Attention module was found" that hides the real root cause.

♻️ Suggested narrowing
         try:
             module = importlib.import_module(module_name)
-        except ImportError:
+        except ModuleNotFoundError:
             continue
🤖 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 `@modelopt/torch/quantization/plugins/vllm.py` around lines 34 - 48, The
_import_attention_module helper is catching all ImportError cases, which can
hide real failures from a broken vllm installation. Narrow the exception
handling around importlib.import_module so only missing-module cases are treated
as fallthrough, and let genuine import-time errors from modules like
vllm.attention.layer surface instead of being replaced by the generic fallback
ImportError.
🤖 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 `@modelopt/torch/kernels/common/attention/triton_fa.py`:
- Line 284: Update the backward attention kernels in triton_fa.py so they mirror
the FP32 QK path used in forward for NVFP4. In `_attn_bwd_dq` and
`_attn_bwd_dkdv`, detect the same `Q_IS_FP32` condition and make the `tl.dot(q,
kT)` QK recomputation use the IEEE precision path, matching the forward
attention logic. Keep the change localized to the backward score recomputation
and preserve the existing `Q_IS_FP32` symbol as the selector.

In `@tests/unit/torch/kernels/common/attention/test_triton_fa.py`:
- Around line 16-23: Update the module docstring in the test_triton_fa module to
point to the correct GPU test locations. The existing reference in the top-level
docstring is wrong; change the path text near the descriptions of the Triton
kernels and wrappers so it names the GPU coverage under the test_triton_fa and
test_triton_fa_calibrate suites. Use the docstring in
tests/unit/torch/kernels/common/attention/test_triton_fa.py as the place to
edit.

---

Nitpick comments:
In `@modelopt/torch/quantization/plugins/vllm.py`:
- Around line 34-48: The _import_attention_module helper is catching all
ImportError cases, which can hide real failures from a broken vllm installation.
Narrow the exception handling around importlib.import_module so only
missing-module cases are treated as fallthrough, and let genuine import-time
errors from modules like vllm.attention.layer surface instead of being replaced
by the generic fallback ImportError.
🪄 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: a948de07-7f82-46c0-af1f-be5e2d2c1642

📥 Commits

Reviewing files that changed from the base of the PR and between d290839 and 25ed74a.

📒 Files selected for processing (19)
  • examples/vllm_serve/README.md
  • examples/vllm_serve/sparse_attn_worker.py
  • examples/vllm_serve/vllm_serve_sparse_attn.py
  • modelopt/torch/kernels/common/attention/decode_attention.py
  • modelopt/torch/kernels/common/attention/triton_fa.py
  • modelopt/torch/kernels/quantization/attention/v_qdq.py
  • modelopt/torch/kernels/quantization/common/nvfp4_quant.py
  • modelopt/torch/kernels/quantization/gemm/fp4_kernel_hopper.py
  • modelopt/torch/kernels/sparsity/attention/skip_softmax_helpers.py
  • modelopt/torch/quantization/plugins/vllm.py
  • modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py
  • tests/gpu/torch/kernels/common/attention/test_decode_attention.py
  • tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py
  • tests/gpu/torch/kernels/common/attention/test_triton_fa_paged.py
  • tests/gpu/torch/quantization/test_tensor_quant_cuda.py
  • tests/gpu_vllm/torch/quantization/test_vllm_dynamic_modules.py
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_quant_sparse_attn_worker.py
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py
  • tests/unit/torch/kernels/common/attention/test_triton_fa.py
💤 Files with no reviewable changes (2)
  • modelopt/torch/kernels/quantization/gemm/fp4_kernel_hopper.py
  • modelopt/torch/kernels/sparsity/attention/skip_softmax_helpers.py

IS_CAUSAL: tl.constexpr, # Whether to apply causal mask
HEAD_DIM: tl.constexpr, # Actual head dimension (for d_mask)
STORE_LSE: tl.constexpr, # Whether to save LSE for backward pass
Q_IS_FP32: tl.constexpr, # Dynamic NVFP4 QDQ carrier uses FP32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if any caller uses p_qdq="nvfp4" with fp32 q and requires_grad together.
rg -nP 'p_qdq\s*=\s*"nvfp4"' modelopt tests -C3
rg -nP 'requires_grad' tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py -C3

Repository: NVIDIA/Model-Optimizer

Length of output: 3916


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant attention kernel sections.
sed -n '250,420p' modelopt/torch/kernels/common/attention/triton_fa.py
printf '\n---\n'
sed -n '540,780p' modelopt/torch/kernels/common/attention/triton_fa.py
printf '\n---\n'
sed -n '960,1105p' modelopt/torch/kernels/common/attention/triton_fa.py

# Inspect the tests that exercise p_qdq="nvfp4" and autograd behavior.
printf '\n=== test_triton_fa_p_qdq.py ===\n'
sed -n '1,220p' tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py
printf '\n---\n'
sed -n '220,420p' tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py
printf '\n=== test_decode_attention.py ===\n'
sed -n '160,250p' tests/gpu/torch/kernels/common/attention/test_decode_attention.py

Repository: NVIDIA/Model-Optimizer

Length of output: 42962


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the backward/autograd tests for P-QDQ and any explicit fp32-Q cases.
rg -n "requires_grad|backward\(|p_qdq=\"nvfp4\"|dtype=torch.float32" tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py -C 3

# Inspect the remainder of the P-QDQ test file around backward coverage.
sed -n '420,760p' tests/gpu/torch/kernels/common/attention/test_triton_fa_p_qdq.py

# Check whether the wrapper blocks autograd for p_qdq/v_qdq modes anywhere in the implementation.
rg -n "NotImplementedError|autograd|v_qdq|p_qdq" modelopt/torch/kernels/common/attention/triton_fa.py -C 3

Repository: NVIDIA/Model-Optimizer

Length of output: 19086


Mirror FP32 QK precision in backward
attention(..., p_qdq="nvfp4", q.dtype=torch.float32) is exercised in TestSoftmaxQdqBackward, but _attn_bwd_dq and _attn_bwd_dkdv still recompute scores = tl.dot(q, kT) * qk_scale without the input_precision="ieee" path used in forward. That leaves the forward/backward QK numerics inconsistent for the same FP32-Q NVFP4 case.

🤖 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 `@modelopt/torch/kernels/common/attention/triton_fa.py` at line 284, Update the
backward attention kernels in triton_fa.py so they mirror the FP32 QK path used
in forward for NVFP4. In `_attn_bwd_dq` and `_attn_bwd_dkdv`, detect the same
`Q_IS_FP32` condition and make the `tl.dot(q, kT)` QK recomputation use the IEEE
precision path, matching the forward attention logic. Keep the change localized
to the backward score recomputation and preserve the existing `Q_IS_FP32` symbol
as the selector.

Comment thread tests/unit/torch/kernels/common/attention/test_triton_fa.py
@kaix-nv kaix-nv force-pushed the kaix/sparse_attn_quant_compact branch from 25ed74a to 37d1b17 Compare July 8, 2026 21:16
@sychen52

sychen52 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

I guess after you investigating the v_bmm_quantizer, you still decided to add a v_qdq into the kernel. Can you explain why?
Assume v_qdq has to be in the kernel, it should be the same as p_qdq, can we reuse that and maybe give it a better name instead of creating a new v_qdq file. Sharing can make it easier to extend to more quantization dtype in the future.

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

Bot review — DM the bot to share feedback.

Re-review of the NVFP4+sparse-attention vLLM serving PR (+3585/-616, 18 files, kaix-nv). Design review was applied previously: this extends established in-repo subsystems (unified triton_fa.py kernel + attention_sparsity/plugins/vllm.py adapter) rather than introducing a competing system, and the new split-K decode kernel is justified by the split-local-P quantization contract in the README/docstring. License headers on new files match the canonical NVIDIA Apache-2.0 header. No prompt-injection attempts in the untrusted content.

Critical comment status — RESOLVED:

  • The blocking fp4_kernel_hopper.py regression is fixed: the per-block guard block_max_quant = tl.where(block_max_quant >= 1e-5, block_max_quant, 1.0) is present in the current file, so the 0/0=NaN for all-zero/underflowing blocks is guarded. nvfp4_scalar_quant now correctly zeroes zero-scale blocks (return tl.where(zero_scale, 0.0, x_quant)), and both _p_qdq_nvfp4/_v_qdq_nvfp4 route through it. New tests test_native_block_scale_underflows_to_zero and test_zero_block_scale_zeroes_block cover this. Good.

Why nudge (remaining items for owner sign-off):

  • 💬 Prior CodeRabbit comment (minor): asked to mirror the forward FP32-Q input_precision="ieee" path into the backward kernels. Still unaddressed_attn_bwd_dq/_attn_bwd_dkdv recompute tl.dot(q, kT) * qk_scale without a Q_IS_FP32 branch. Impact is limited: the serving path is inference-only (v_qdq rejects autograd), so this only affects forward/backward numeric consistency in the p_qdq="nvfp4" + FP32-Q QAT training case. Low severity but worth an owner call.
  • 💬 sychen52 asked why a new v_qdq.py was added rather than reusing/renaming the p_qdq helper. The current diff keeps them separate (_v_qdq_nvfp4 uses abs() for signed V and adds the fake_quant_v_onwrite cache-finalize kernel). Reasonable technical divergence, but the design question doesn't appear resolved in-code — human reviewer should confirm the author's rationale.
  • Minor doc nit (CodeRabbit): tests/unit/torch/kernels/common/attention/test_triton_fa.py module docstring still points to the wrong GPU path tests/gpu/torch/sparsity/attention_sparsity/test_triton_fa*.py; actual coverage lives under tests/gpu/torch/kernels/common/attention/. Not blocking.
  • Nitpick (CodeRabbit, unaddressed): _import_attention_module still catches ImportError (could mask a genuinely broken vllm install); ModuleNotFoundError would be tighter. Trivial.
  • Numerical correctness of the mixed decode/prefill FlashInfer split math and the split-K online-softmax combine is GPU-only; unit tests cover routing/metadata/cache-layout contracts well, but numerics rely on the B200/A6000 runs reported in the PR body.

Overall the blocking regression is fixed and test coverage is extensive; remaining items are minor/design-level plus GPU-only numerics, so a human with architectural context should give final sign-off.

@kaix-nv

kaix-nv commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

I guess after you investigating the v_bmm_quantizer, you still decided to add a v_qdq into the kernel. Can you explain why? Assume v_qdq has to be in the kernel, it should be the same as p_qdq, can we reuse that and maybe give it a better name instead of creating a new v_qdq file. Sharing can make it easier to extend to more quantization dtype in the future.

In-kernel quant for V is necessary. V must be quantized in 16-token blocks along the sequence dimension. Because a block can span multiple decode steps or cache pages, the kernel quantizes completed blocks once after caching V and quantizes the remaining partial block on read.

I agree that this should be consolidated. I'll move their thin wrappers into a neutrally named shared module (e.g., bmm2_qdq).

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.

We maintain our fake-quantized kernels in modelopt/torch/quantization/plugins/vllm. It looks to me that some of the contents of this file should be moved to either modelopt/torch/quantization/plugins/vllm or modelopt/torch/sparsity/plugins/vllm.

This way we can avoid workarounds like _install_quant_plans; We should use modelopt high-level APIs like mtq.quantize / mts.sparsify to automatically insert and initialize quantizers.

This is particularly important because our vLLM fakequant path is used in many places like Nemo-RL. In the current design, fake sparse kernels are not available outside of this example folder. Hence can we please move relevant contents from this folder to modelopt library?

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.

RB: Can you take a look at my above comment and propose a new design?

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.

🤖 Bot comment.

🐝 Looking at the current design and the surrounding vLLM plugin code now.

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.

🤖 Bot comment.

I agree with the direction in your comment. I would make the example worker a thin consumer of library APIs and move the reusable serving path into the vLLM plugins.

Proposed shape:

  1. Move the vLLM sparse serving planner out of examples/vllm_serve/sparse_attn_worker.py into modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py. That module should own loading sparse_attention_config from checkpoint metadata, matching layer names, validating supported vLLM backends, cloning the native impl, and attaching sparse_kw. The worker should call one helper such as mtsa.plugins.vllm.install_sparse_attention_from_checkpoint(model, hf_config, vllm_config=...) rather than open-coding _sparse_plans / _install_sparse_plans.

  2. Move the fixed NVFP4 attention quantizer setup into modelopt/torch/quantization/plugins/vllm.py. In particular, _QuantVLLMAttention should own the Q/K/V/P BMM quantizer attributes, and the plugin should expose a reusable config/helper for dynamic block-16 NVFP4 attention. Then the worker can use the normal path, e.g. mtq.quantize(model, mtq.VLLM_NVFP4_ATTENTION_CFG, forward_loop=...), instead of manually calling QuantModuleRegistry.convert, adding p_bmm_quantizer, and running set_quantizer_by_cfg in _install_quant_plans.

  3. Keep only runtime worker details in the example: unwrap the vLLM model, provide the dummy calibration loop if quantizing, disable compilation around mtq.quantize, call the sparse install helper, and set vLLM-only runtime flags such as cascade_attn_enabled = False if the plugin helper reports that ModelOpt attention transforms are active.

  4. Add tests at the library level, not only under the example: one test that mtq.quantize inserts/enables Q/K/P/V attention quantizers for vLLM Attention, one test that the sparse plugin installs the backend-matched impl from exported checkpoint metadata, and one combined sparse+NVFP4 test proving the example worker no longer owns the conversion logic.

That gives Nemo-RL and other vLLM fakequant users an importable ModelOpt path, and the example becomes just a reference for wiring it into vLLM worker lifecycle.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I'll have a refactor for the orchestration (layer discovery, validation, and the module.impl swap) and it should live in the vLLM plugins, with a supported activation entry point so NeMo-RL and others don't have to copy the worker.

kaix-nv added 14 commits July 15, 2026 14:56
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
…prefill, share sparse layer validation, disable sparse cascade, require vLLM>=0.15, reject sm<8.9

Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
@kaix-nv kaix-nv force-pushed the kaix/sparse_attn_quant_compact branch from ab6dd3e to f3021f9 Compare July 15, 2026 21:57
Signed-off-by: Kai Xu <kaix@nvidia.com>
@kaix-nv kaix-nv force-pushed the kaix/sparse_attn_quant_compact branch from f3021f9 to c4e2146 Compare July 15, 2026 22:52
@kaix-nv kaix-nv requested review from a team, ChenhanYu and kevalmorabia97 and removed request for a team July 15, 2026 23:52

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

LGTM for torch/sparsity

@sychen52 sychen52 self-requested a review July 16, 2026 00:40

@kevalmorabia97 kevalmorabia97 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. Good test coverage. You can ignore the failing codecov check (its sometimes not accurate)

@kaix-nv kaix-nv merged commit 85bc559 into main Jul 16, 2026
97 of 102 checks passed
@kaix-nv kaix-nv deleted the kaix/sparse_attn_quant_compact branch July 16, 2026 06:17
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.

8 participants