Skip to content

fix: handle NVFP4 scale-only checkpoint shards - #17188

Open
zcxGGmu wants to merge 1 commit into
NVIDIA:mainfrom
zcxGGmu:fix/ad-sharded-safetensor-load-hook
Open

fix: handle NVFP4 scale-only checkpoint shards#17188
zcxGGmu wants to merge 1 commit into
NVIDIA:mainfrom
zcxGGmu:fix/ad-sharded-safetensor-load-hook

Conversation

@zcxGGmu

@zcxGGmu zcxGGmu commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • Allow the NVFP4 unified-HF checkpoint load hook to process scale-only state-dict shards.
  • Keep the existing packed-weight path intact, while no longer requiring the packed weight to be present before computing alpha, inverted input_scale, and swizzled weight_scale.
  • Add a regression test for a prefixed scale-only shard.

Fixes #11541

Test Plan

  • python -m ruff check tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py
  • python -m py_compile tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py
  • Not run locally: targeted pytest is blocked in this Windows checkout because the environment lacks a usable MPI runtime (impi.dll/msmpi.dll) and compiled TensorRT-LLM bindings.

Dev Engineer Review

  • Updated NVFP4 unified-HF checkpoint loading to process scale-only shards.
  • The hook now computes alpha, inverts input_scale, and swizzles weight_scale before requiring the packed weight.
  • The existing packed-weight conversion path remains unchanged.
  • No public API or configuration changes were found.
  • Ruff and Python compilation checks passed.
  • Targeted tests were not run because the Windows environment lacks usable MPI and compiled TensorRT-LLM bindings.

QA Engineer Review

  • Added test_nvfp4_load_hook_maps_scale_only_shard_with_prefix.
  • The test validates scale loading from a prefixed, scale-only state dictionary.
  • No test-list coverage was reported for this test.
  • Verdict: needs follow-up.

Signed-off-by: zq <zhouquan1511@163.com>
@zcxGGmu
zcxGGmu requested a review from a team as a code owner August 3, 2026 06:48
@zcxGGmu
zcxGGmu requested a review from suyoggupta August 3, 2026 06:48
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The NVFP4 load hook now processes unified Hugging Face scale tensors before packed weights are available. It computes and clamps alpha, inverts input_scale, reshapes weight scales, and adds prefixed scale-loading coverage.

Changes

NVFP4 sharded checkpoint loading

Layer / File(s) Summary
Unified scale loading path
tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py
The load hook processes unified Hugging Face scales independently of packed weights. It computes alpha, inverts input_scale, swizzles and reshapes weight scales, and removes the former nested conversion path.
Prefixed scale loading test
tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py
The test covers scale-only state-dict loading with a prefix and verifies alpha and reciprocal input_scale values.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: brnguyen2, bobboli

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the NVFP4 scale-only checkpoint shard fix.
Description check ✅ Passed The description explains the fix, references issue #11541, and lists validation commands and test limitations.
Linked Issues check ✅ Passed The changes address #11541 by processing NVFP4 scale-only shards and adding a regression test for prefixed state-dict shards.
Out of Scope Changes check ✅ Passed The code and test changes are limited to the linked issue's NVFP4 sharding behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py`:
- Around line 135-161: The
test_nvfp4_load_hook_maps_scale_only_shard_with_prefix test only verifies alpha
and input_scale, so it does not validate scale conversion. Annotate the test’s
monkeypatch parameter with pytest.MonkeyPatch and its unused third parameter
with None, make block_scale_interleave return distinguishable uint8 data, and
assert the resulting stored weight scale’s shape and byte values after
nvfp4_imp.load_hook.
🪄 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: 365b03f3-2e16-4e10-b3e8-ccded0cea512

📥 Commits

Reviewing files that changed from the base of the PR and between 1a00238 and 5f47edb.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py
  • tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py

Comment on lines +135 to +161
def test_nvfp4_load_hook_maps_scale_only_shard_with_prefix(monkeypatch):
from tensorrt_llm._torch.auto_deploy.transform.library.quantization import (
NVFP4LinearQuantizationFromConfig,
)

monkeypatch.setattr(
torch.ops.trtllm,
"block_scale_interleave",
lambda weight_scale: weight_scale,
raising=False,
)

config = TransformConfig(stage="pattern_matcher")
nvfp4_imp = NVFP4LinearQuantizationFromConfig(config)

weight_name = "layer.proj.weight"
prefix = "nested."
mock_state_dict = {
prefix + weight_name + "_scale_2": torch.tensor(2.0, dtype=torch.float32),
prefix + weight_name + "_scale": torch.ones(128, 4, dtype=torch.float8_e4m3fn),
prefix + "layer.proj.input_scale": torch.tensor(3.0, dtype=torch.float32),
}

nvfp4_imp.load_hook(mock_state_dict, prefix, None, weight_name=weight_name)

assert mock_state_dict[prefix + "layer.proj.alpha"] == torch.tensor(6.0)
assert mock_state_dict[prefix + "layer.proj.input_scale"] == torch.tensor(1.0 / 3.0)

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

if ! rg -n --glob '*.{yml,yaml}' \
  'test_nvfp4_load_hook_maps_scale_only_shard_with_prefix|test_quantization_utils\.py' \
  tests/integration/test_lists; then
  echo "No matching test-list entry found."
fi

Repository: NVIDIA/TensorRT-LLM

Length of output: 192


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- candidate test file ---'
sed -n '1,220p' tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py

echo '--- quantization implementation references ---'
rg -n -C 8 \
  'class NVFP4LinearQuantizationFromConfig|block_scale_interleave|weight_scale|load_hook' \
  tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py

echo '--- test-list files and matching references ---'
find tests/integration/test_lists -maxdepth 2 -type f \( -name '*.yml' -o -name '*.yaml' -o -name '*.sqlite' \) -print | sort
rg -n -i \
  'quantization_utils|nvfp4|auto_deploy|singlegpu' \
  tests/integration/test_lists || true

echo '--- coding guidance and test configuration ---'
find . -maxdepth 3 -iname 'CODING_GUIDELINES.md' -o -iname 'pyproject.toml' -o -iname 'setup.cfg' -o -iname 'tox.ini' | sort

Repository: NVIDIA/TensorRT-LLM

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- candidate test file ---'
sed -n '1,220p' tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py

echo '--- quantization implementation references ---'
rg -n -C 8 \
  'class NVFP4LinearQuantizationFromConfig|block_scale_interleave|weight_scale|load_hook' \
  tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py

echo '--- test-list files and matching references ---'
find tests/integration/test_lists -maxdepth 2 -type f \( -name '*.yml' -o -name '*.yaml' -o -name '*.sqlite' \) -print | sort
rg -n -i \
  'quantization_utils|nvfp4|auto_deploy|singlegpu' \
  tests/integration/test_lists || true

echo '--- coding guidance and test configuration ---'
find . -maxdepth 3 \( -iname 'CODING_GUIDELINES.md' -o -iname 'pyproject.toml' -o -iname 'setup.cfg' -o -iname 'tox.ini' \) -print | sort

Repository: NVIDIA/TensorRT-LLM

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

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

test_path = Path("tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py")
impl_path = Path("tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py")

test_source = test_path.read_text()
test_tree = ast.parse(test_source)
test_fn = next(
    node for node in test_tree.body
    if isinstance(node, ast.FunctionDef)
    and node.name == "test_nvfp4_load_hook_maps_scale_only_shard_with_prefix"
)

lambda_nodes = [
    node for node in ast.walk(test_fn)
    if isinstance(node, ast.Lambda)
]
assert len(lambda_nodes) == 1
identity_lambda = (
    len(lambda_nodes[0].args.args) == 1
    and isinstance(lambda_nodes[0].body, ast.Name)
    and lambda_nodes[0].body.id == lambda_nodes[0].args.args[0].arg
)

assert_texts = [
    ast.get_source_segment(test_source, node)
    for node in ast.walk(test_fn)
    if isinstance(node, ast.Assert)
]
scale_asserts = [
    text for text in assert_texts
    if text and "_scale" in text
]
print(f"test_function_arguments={[arg.arg for arg in test_fn.args.args]}")
print(f"mock_is_identity={identity_lambda}")
print(f"assertions={assert_texts}")
print(f"assertions_observing_weight_scale={scale_asserts}")

impl_source = impl_path.read_text()
impl_tree = ast.parse(impl_source)
impl_fn = next(
    node for node in ast.walk(impl_tree)
    if isinstance(node, ast.FunctionDef)
    and node.name == "load_hook"
    and any(
        isinstance(parent, ast.ClassDef)
        and parent.name == "NVFP4LinearQuantizationFromConfig"
        for parent in ast.walk(impl_tree)
        if isinstance(parent, ast.ClassDef)
        and node in parent.body
    )
)
impl_text = ast.get_source_segment(impl_source, impl_fn)
assert impl_text is not None
print(f"implementation_calls_block_scale_interleave={'block_scale_interleave' in impl_text}")
print(f"implementation_stores_transformed_scale='weight_scale_swizzled' in state_dict={\"weight_scale_swizzled\" in impl_text}")
PY

echo '--- exact test-list membership ---'
rg -n -F \
  'unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py' \
  tests/integration/test_lists || true
rg -n \
  '(^|[/ ])auto_deploy(/|$)|(^|[/ ])singlegpu(/|$)' \
  tests/integration/test_lists/test-db tests/integration/test_lists/qa \
  --glob '*.yml' --glob '*.yaml' --glob '*.txt' || true

echo '--- repository guidance files ---'
find . -type f \( -name 'CODING_GUIDELINES.md' -o -name 'pyproject.toml' \) -print | sort
echo '--- changed-file summary ---'
git diff --numstat -- tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 514


🏁 Script executed:

#!/bin/bash
set -euo pipefail

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

test_path = Path("tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py")
impl_path = Path("tensorrt_llm/_torch/auto_deploy/transform/library/quantization.py")

test_source = test_path.read_text()
test_tree = ast.parse(test_source)
test_fn = next(
    node for node in test_tree.body
    if isinstance(node, ast.FunctionDef)
    and node.name == "test_nvfp4_load_hook_maps_scale_only_shard_with_prefix"
)

lambda_nodes = [node for node in ast.walk(test_fn) if isinstance(node, ast.Lambda)]
assert len(lambda_nodes) == 1
identity_lambda = (
    len(lambda_nodes[0].args.args) == 1
    and isinstance(lambda_nodes[0].body, ast.Name)
    and lambda_nodes[0].body.id == lambda_nodes[0].args.args[0].arg
)

assert_texts = [
    ast.get_source_segment(test_source, node)
    for node in ast.walk(test_fn)
    if isinstance(node, ast.Assert)
]
scale_asserts = [
    text for text in assert_texts
    if text and "_scale" in text
]
print(f"test_function_arguments={[arg.arg for arg in test_fn.args.args]}")
print(f"mock_is_identity={identity_lambda}")
print(f"assertions={assert_texts}")
print(f"assertions_observing_weight_scale={scale_asserts}")

impl_source = impl_path.read_text()
print(
    "implementation_calls_block_scale_interleave="
    f"{'block_scale_interleave' in impl_source}"
)
print(
    "implementation_stores_transformed_scale="
    f"{'weight_scale_swizzled' in impl_source}"
)
PY

echo '--- exact test-list membership ---'
rg -n -F \
  'unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py' \
  tests/integration/test_lists || true
rg -n \
  '(^|[/ ])auto_deploy(/|$)|(^|[/ ])singlegpu(/|$)' \
  tests/integration/test_lists/test-db tests/integration/test_lists/qa \
  --glob '*.yml' --glob '*.yaml' --glob '*.txt' || true

echo '--- repository guidance files ---'
find . -type f \( -name 'CODING_GUIDELINES.md' -o -name 'pyproject.toml' \) -print | sort
echo '--- changed-file summary ---'
git diff --numstat -- tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 6598


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- applicable Python and test guidance ---'
rg -n -C 3 \
  'Annotate every function|test coverage|test list|test-db|qa/|directory|recursive' \
  CODING_GUIDELINES.md

echo '--- CI entries covering the changed test directory ---'
sed -n '72,94p' tests/integration/test_lists/test-db/l0_a30.yml
sed -n '486,502p' tests/integration/test_lists/test-db/l0_h100.yml

Repository: NVIDIA/TensorRT-LLM

Length of output: 201


Strengthen the NVFP4 scale assertion.

test_nvfp4_load_hook_maps_scale_only_shard_with_prefix is covered by the utils directory entries in l0_a30.yml and l0_h100.yml. The identity mock and missing weight_scale assertion leave the conversion untested. Return distinguishable uint8 data from block_scale_interleave, then assert the stored scale shape and bytes. Add pytest.MonkeyPatch and None annotations to the test function.

Coverage verdict: insufficient.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/auto_deploy/singlegpu/utils/test_quantization_utils.py` around
lines 135 - 161, The test_nvfp4_load_hook_maps_scale_only_shard_with_prefix test
only verifies alpha and input_scale, so it does not validate scale conversion.
Annotate the test’s monkeypatch parameter with pytest.MonkeyPatch and its unused
third parameter with None, make block_scale_interleave return distinguishable
uint8 data, and assert the resulting stored weight scale’s shape and byte values
after nvfp4_imp.load_hook.

Source: Path instructions

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.

[Bug]: Support parameters sharding across safetensors

2 participants