[Quantization][Qwen3-Omni] Enable NVFP4 W4A4 serving on Blackwell (#4025)

Signed-off-by: Yihong Jin <yihongjin404@gmail.com>
This commit is contained in:
Yihong Jin authored and GitHub committed 2026-06-18 10:01:46 +08:00
1 parent 65bc737fd4
commit 2f64c712fd
4 files changed
+605

No files matched your search

+43
View File
@@ -89,11 +89,54 @@ and
| Model | Scope | Status |
|-------|-------|--------|
| Qwen3-Omni | Thinker language-model stage | ModelOpt FP8 checkpoint path |
| Qwen3-Omni | Thinker language-model stage (W4A4 NVFP4) | Validated; see [Qwen3-Omni NVFP4 W4A4](#qwen3-omni-nvfp4-w4a4-thinker) below |
| Qwen3-TTS | TTS language-model stage | Not validated |
Audio encoder, vision encoder, talker, and code2wav stages stay in BF16 unless
a model-specific guide documents otherwise.
#### Qwen3-Omni NVFP4 W4A4 (thinker)
vLLM-Omni serves ModelOpt NVFP4 W4A4 quantizations of the
Qwen3-Omni-30B-A3B-Instruct thinker language model. The thinker text body
(attention + MoE experts) is quantized to NVFP4 with FP8 per-tensor input
scales; the audio encoder, vision encoder, talker, and code2wav stay in BF16.
| Variant | HF checkpoint | Hardware |
|---------|---------------|----------|
| W4A4 NVFP4 (full thinker) | `YihongJin/Qwen3-Omni-30B-A3B-Instruct-NVFP4-W4A4-full-thinker-awqclip` | sm_100+ (Blackwell, FlashInfer FP4 GEMM) |
Calibration uses ModelOpt `mtq.NVFP4_DEFAULT_CFG` with the `awq_clip` algorithm
on 1024 ultrachat samples chat-templated through the Qwen3-Omni tokenizer.
Excluded modules: `*audio_tower*`, `*visual*`, `*talker*`, `*code2wav*`,
`*lm_head*`, `*mlp.gate*`. See `scripts/nvfp4/calibrate.py` for the reference
recipe.
!!! note "ModelOpt 0.44 NaN regression workaround"
ModelOpt 0.44's float32 -> FP8 E4M3 cast of per-block weight scales
occasionally emits literal NaN bytes (E4M3 encoding 0x7F / 0xFF) for
blocks whose pre-cast scale rounds above the FP8 max of 448 after the
global-scale division. A single NaN byte in any `weight_scale`
propagates through the FP4 GEMM and collapses the served model output
to `!!!!`. vLLM-Omni's `vllm_omni.patch` installs a defensive override
of `ModelOptNvFp4LinearMethod.process_weights_after_loading` that
clamps these bytes to the FP8 E4M3 max at load time. The override
self-extinguishes once vllm-omni's vllm pin moves to a release
containing the upstream fix. Set `VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP=1`
to disable the override for diagnostics.
Serving:
```bash
vllm serve YihongJin/Qwen3-Omni-30B-A3B-Instruct-NVFP4-W4A4-full-thinker-awqclip \
--omni --port 8000
```
> Do **not** pass `--enforce-eager` for production / benchmarks. CUDA graphs
> amortize launch overhead and unlock the FP4 throughput wins; with
> `--enforce-eager` set, W4A4 TPOT degrades ~10x relative to the CUDA-graph
> configuration.
### Multi-Stage Diffusion Model
ModelOpt checkpoints must be routed to the stage whose checkpoint contains the
@@ -0,0 +1,100 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""E2E tests for Qwen3-Omni ModelOpt NVFP4 W4A4 — Marlin fallback path.
Loads the published full-thinker NVFP4 W4A4 checkpoint on H100 (sm_90),
where ``init_nvfp4_linear_kernel()`` selects the ``MarlinNvFp4LinearKernel``
weight-only fallback. The Marlin kernel's PWAL casts ``weight_scale``
FP8 -> bf16 and permutes; the patched
``ModelOptNvFp4LinearMethod.process_weights_after_loading`` in
``vllm_omni.patch`` therefore clamps NaN bytes BEFORE delegating to the
original PWAL. This test asserts the served response is coherent, i.e.
no `!!!!` token cascade caused by NaN bytes in per-block ``weight_scale``.
Hardware coverage caveat: this DOES NOT exercise the native FlashInfer
FP4 GEMM (Cutlass / TRT-LLM / cuDNN) that is selected on Blackwell
sm_100+ — those kernel PWALs swizzle/shuffle FP8 bytes without dtype
casting, a different code path from Marlin. TODO(B200): add a companion
Blackwell smoke once a Blackwell runner is wired into ``tests.helpers.mark``.
"""
import os
import re
import pytest
from tests.helpers.mark import hardware_test
from tests.helpers.stage_config import get_deploy_config_path, modify_stage_config
pytestmark = [
pytest.mark.full_model,
pytest.mark.omni,
]
QUANTIZED_MODEL = os.environ.get(
"QWEN3_OMNI_W4A4_MODEL",
"YihongJin/Qwen3-Omni-30B-A3B-Instruct-NVFP4-W4A4-full-thinker-awqclip",
)
_CI_DEPLOY = get_deploy_config_path("ci/qwen3_omni_moe.yaml")
# `!!!!` (4+ exclamation marks) is the signature degenerate output the
# NaN-clamp patch defends against. Any test that loads a W4A4 checkpoint
# must reject this.
_NAN_COLLAPSE_RE = re.compile(r"!{4,}")
@pytest.fixture(scope="module", autouse=True)
def _qwen3_omni_env():
with pytest.MonkeyPatch.context() as mp:
mp.setenv("VLLM_WORKER_MULTIPROC_METHOD", "spawn")
yield
def _stage_config():
# enforce_eager=True here to keep CI memory + warm-up time bounded for the
# smoke test, matching the existing Qwen3-Omni e2e pattern (see
# test_qwen3_omni_autoround_w4a16_expansion.py). The throughput claims in
# the PR body are from explicit no-enforce-eager runs, not from this test.
#
# TODO: the production code path uses FULL_AND_PIECEWISE CUDA graphs.
# The NaN clamp itself is graph-mode-independent so this enforce-eager
# smoke is still a valid regression guard against `!!!!`, but a future
# CUDA-graph-specific regression would not be caught here. If/when CI
# gains enough VRAM headroom for a no-eager run, add a companion test.
return modify_stage_config(
_CI_DEPLOY,
updates={"stages": {0: {"enforce_eager": True}, 1: {"enforce_eager": True}}},
)
quant_params = [(QUANTIZED_MODEL, _stage_config())]
@hardware_test(res={"cuda": "H100"}, num_cards=2)
@pytest.mark.parametrize("omni_runner", quant_params, indirect=True)
def test_marlin_fallback_no_nan_collapse(omni_runner, omni_runner_handler):
"""H100 Marlin FP4 fallback: text in -> coherent text out.
On H100 the upstream NVFP4 PWAL routes through
``MarlinNvFp4LinearKernel.process_weights_after_loading`` ->
``prepare_fp4_layer_for_marlin``, which casts FP8 weight_scale to
bf16 and permutes. The patched PWAL's NaN clamp runs BEFORE this
transform, so this test is the regression guard for the Marlin
code path specifically (Blackwell sm_100+ takes the FlashInfer
Cutlass/TRT-LLM/cuDNN path and is NOT covered by this test).
Asserts the response is non-empty and does NOT match the `!!!!`
degenerate-output signature that indicates NaN propagation through
the FP4 GEMM.
"""
response = omni_runner_handler.send_omni_request({"prompts": "Why is the sky blue?", "modalities": ["text"]})
assert response.success, "request failed"
text = (response.text_content or "").strip()
assert text, "empty response — likely server returned only EOS"
assert not _NAN_COLLAPSE_RE.search(text), (
f"degenerate `!!!!` output detected — NaN propagated through FP4 GEMM. "
f"This means vllm_omni.patch's NVFP4 weight_scale NaN clamp did not install or "
f"did not run. Full response: {text!r}"
)
@@ -0,0 +1,279 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Tests for the NVFP4 W4A4 weight_scale NaN clamp installed by
``vllm_omni.patch``.
ModelOpt 0.44's FP32->FP8 E4M3 cast of per-block weight scales can emit
literal NaN bytes (E4M3 encoding 0x7F / 0xFF) when the pre-cast scale
rounds above the FP8 max of 448 after the global-scale division. A
single NaN byte in any per-block weight_scale collapses the FlashInfer
FP4 GEMM output to NaN and the served model emits `!!!!`. The patch
overrides ``ModelOptNvFp4LinearMethod.process_weights_after_loading``
to scan + clamp those bytes at load time.
These tests cover the behavior of the override on a synthetic layer:
- the NaN bytes get clamped to the FP8 E4M3 max byte (0x7E)
- a finite weight_scale is left untouched (no spurious writes)
- the install asserts itself so silent fallback is impossible
- the env-var escape hatch path is reachable
No GPU is required — the tests construct an in-memory FP8 ``weight_scale``
tensor with planted NaN bytes and call the patched PWAL directly.
"""
import pytest
import torch
# Importing vllm_omni runs vllm_omni.patch, which installs the override
# we are testing. The import must come before any direct reference to
# ``ModelOptNvFp4LinearMethod`` below.
import vllm_omni # noqa: F401
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
FP8_NAN_BYTES = (0x7F, 0xFF)
FP8_MAX_BYTE = 0x7E # encoding of finfo(float8_e4m3fn).max
def _fp8_tensor_with_nan_bytes(num_blocks: int, nan_indices: list[int]) -> torch.Tensor:
"""Return an FP8 E4M3 tensor where every position is the FP8 max byte
except ``nan_indices``, which carry NaN bytes (alternating 0x7F/0xFF)."""
raw = bytearray([FP8_MAX_BYTE] * num_blocks)
for i, idx in enumerate(nan_indices):
raw[idx] = FP8_NAN_BYTES[i % len(FP8_NAN_BYTES)]
# Materialize on a writable storage so masked_fill_ on the uint8 view
# can write back in-place. ``frombuffer`` produces a read-only view
# that PyTorch warns about on first use.
return torch.tensor(list(raw), dtype=torch.uint8).view(torch.float8_e4m3fn)
@pytest.fixture
def clamp_fn():
"""Return the top-level NaN-clamp helper exposed by vllm_omni.patch.
Skipped when vllm doesn't expose ``ModelOptNvFp4LinearMethod`` (older
pin / unsupported build) and vllm_omni.patch consequently didn't
install the helper either.
"""
try:
from vllm_omni.patch import _clamp_nvfp4_weight_scale_nans
except ImportError:
pytest.skip("vllm_omni.patch._clamp_nvfp4_weight_scale_nans not exposed")
return _clamp_nvfp4_weight_scale_nans
def test_clamps_nan_bytes_in_weight_scale(clamp_fn):
weight_scale = _fp8_tensor_with_nan_bytes(num_blocks=8, nan_indices=[2, 5])
assert torch.isnan(weight_scale).sum().item() == 2
layer = torch.nn.Module()
layer.weight_scale = torch.nn.Parameter(weight_scale, requires_grad=False)
n_clamped = clamp_fn(layer)
assert n_clamped == 2
assert torch.isnan(layer.weight_scale).sum().item() == 0, (
"NaN bytes in weight_scale should have been clamped to FP8 E4M3 max"
)
# Every byte should now be the FP8 max byte.
bytes_after = layer.weight_scale.data.view(torch.uint8).tolist()
assert bytes_after == [FP8_MAX_BYTE] * 8
def test_finite_weight_scale_untouched(clamp_fn):
# All-finite weight_scale should leave bytes exactly as written.
raw = bytes([0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x7E])
weight_scale = torch.frombuffer(raw, dtype=torch.uint8).clone().view(torch.float8_e4m3fn)
assert torch.isnan(weight_scale).sum().item() == 0
before = weight_scale.data.view(torch.uint8).clone()
layer = torch.nn.Module()
layer.weight_scale = torch.nn.Parameter(weight_scale, requires_grad=False)
n_clamped = clamp_fn(layer)
assert n_clamped == 0
after = layer.weight_scale.data.view(torch.uint8)
assert torch.equal(before, after), "clamp must not modify a finite weight_scale tensor"
def test_clamp_is_noop_when_weight_scale_missing(clamp_fn):
# Defensive guard: subclasses without weight_scale should not crash.
layer = torch.nn.Module()
n_clamped = clamp_fn(layer)
assert n_clamped == 0
def test_clamps_non_contiguous_weight_scale(clamp_fn):
# A non-contiguous weight_scale would make `.view(torch.uint8)` raise.
# The clamp must materialize a contiguous copy, write it back to the
# Parameter, and still land the in-place clamp where the kernel reads.
# NaN bytes at even indices so they survive the stride-2 slice below
# (base[::2] keeps original indices 0, 2, 4, ...).
base = _fp8_tensor_with_nan_bytes(num_blocks=16, nan_indices=[4, 12])
# Slice every other element -> stride 2 -> non-contiguous view.
non_contig = base[::2]
assert not non_contig.is_contiguous()
assert torch.isnan(non_contig).sum().item() >= 1
layer = torch.nn.Module()
layer.weight_scale = torch.nn.Parameter(non_contig, requires_grad=False)
n_clamped = clamp_fn(layer)
assert n_clamped >= 1
assert layer.weight_scale.is_contiguous(), "weight_scale should be contiguous after clamp"
assert torch.isnan(layer.weight_scale).sum().item() == 0, (
"NaN bytes in a non-contiguous weight_scale should have been clamped"
)
def test_install_is_self_asserting():
# If the install assert is removed, a future regression could let the
# override silently drop. There are three legitimate end states:
# (a) upstream vLLM already has its own NaN clamp and vllm_omni.patch
# intentionally skipped installing the override (self-extinguish),
# (b) upstream is unpatched and our wrapper is installed,
# (c) the env-var escape hatch is set OR the modelopt import failed,
# so nothing is installed by design — nothing to assert.
try:
from vllm.model_executor.layers.quantization.modelopt import (
ModelOptNvFp4LinearMethod,
)
except ImportError:
pytest.skip("vllm.modelopt.ModelOptNvFp4LinearMethod not available")
from vllm_omni.patch import _already_patched_upstream, _clamp_installed
current = ModelOptNvFp4LinearMethod.process_weights_after_loading
name = getattr(current, "__qualname__", "") or getattr(current, "__name__", "")
if _already_patched_upstream:
# Self-extinguish path: presence of our marker here would mean the
# heuristic in vllm_omni.patch failed to detect upstream's clamp.
assert "_patched_nvfp4_pwal" not in name, (
f"vllm_omni override installed despite upstream PWAL already containing a "
f"NaN clamp ({name!r}); self-extinguish heuristic in vllm_omni.patch needs review"
)
elif _clamp_installed:
assert "patched_nvfp4_pwal" in name or "_patched_nvfp4_pwal" in name, (
f"NVFP4 NaN-clamp PWAL override does not appear installed (saw {name!r}); "
"another plugin may have re-patched ModelOptNvFp4LinearMethod"
)
else:
pytest.skip("NaN-clamp not installed by design (env-var escape hatch or import failure)")
def test_reload_does_not_nest_wrapper():
"""Reloading vllm_omni.patch must not wrap our own wrapper a second time.
On reload the class attribute already holds our wrapper; capturing it as
the "original" and re-wrapping would run the clamp twice per load. The
patch recovers the genuine upstream method via a sentinel, so after a
reload the installed wrapper's ``_vllm_omni_wrapped_pwal`` must point at
the real upstream method (which itself carries no sentinel), not at
another wrapper. Runs in a subprocess: importlib.reload mutates global
interpreter state we don't want leaking into other tests."""
import subprocess
import sys
import textwrap
code = textwrap.dedent(
"""
import importlib
import vllm_omni.patch as p
try:
from vllm.model_executor.layers.quantization.modelopt import (
ModelOptNvFp4LinearMethod,
)
except ImportError:
print("SKIP: modelopt not available")
raise SystemExit(0)
if not getattr(p, "_clamp_installed", False):
print("SKIP: clamp not installed (upstream patched or escape hatch)")
raise SystemExit(0)
importlib.reload(p)
fn = ModelOptNvFp4LinearMethod.process_weights_after_loading
wrapped = getattr(fn, "_vllm_omni_wrapped_pwal", None)
# The recovered upstream must NOT itself be one of our wrappers.
nested = hasattr(wrapped, "_vllm_omni_wrapped_pwal")
print("NESTED" if nested else "FLAT")
"""
)
result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, timeout=120)
if result.stdout.strip().startswith("SKIP:"):
pytest.skip(result.stdout.strip())
assert "FLAT" in result.stdout, (
f"reload nested the NVFP4 clamp wrapper — clamp would run twice per load.\n"
f"stdout: {result.stdout}\nstderr: {result.stderr}"
)
@pytest.mark.parametrize(
("env_value", "acceptable"),
[
# Truthy values activate the escape hatch: the import short-circuits
# before the upstream-detection runs, so nothing is installed.
("1", frozenset({"NOT_INSTALLED"})),
("true", frozenset({"NOT_INSTALLED"})),
# Falsy values mean "do NOT use the escape hatch" — the repo-wide
# bool-env idiom in vllm_omni.patch guards against a naive
# `if os.environ.get(...)` wrongly skipping on "0"/"false". The clamp
# must therefore be in force, which is satisfied EITHER by our wrapper
# being installed OR by upstream vLLM already carrying its own clamp
# (self-extinguish, `_already_patched_upstream`), in which case skipping
# our wrapper is the correct behavior. Accept both so this test keeps
# passing once the upstream fix lands.
("0", frozenset({"INSTALLED", "UPSTREAM_PATCHED"})),
("false", frozenset({"INSTALLED", "UPSTREAM_PATCHED"})),
],
)
def test_env_var_escape_hatch_disables_install(env_value, acceptable):
"""Spawn a subprocess with VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP set and verify
the override install state matches the bool-env convention. The patch
module runs at ``vllm_omni`` import time, so we can't toggle it via
monkeypatch in the same process.
The subprocess reports the authoritative state from vllm_omni.patch's
module flags (the same source of truth as ``test_install_is_self_asserting``)
rather than introspecting the method qualname — both because that is the
real contract and because ``"INSTALLED"`` is a substring of
``"NOT_INSTALLED"`` and naive substring matching would be vacuous."""
import os
import subprocess
import sys
import textwrap
code = textwrap.dedent(
"""
import vllm_omni # noqa: F401
import vllm_omni.patch as p
try:
from vllm.model_executor.layers.quantization.modelopt import (
ModelOptNvFp4LinearMethod, # noqa: F401
)
except ImportError:
print("SKIP: modelopt not available")
raise SystemExit(0)
if getattr(p, "_already_patched_upstream", False):
# Upstream vLLM already clamps; vllm_omni.patch deliberately
# skipped installing our wrapper (self-extinguish).
print("UPSTREAM_PATCHED")
elif getattr(p, "_clamp_installed", False):
print("INSTALLED")
else:
print("NOT_INSTALLED")
"""
)
env = os.environ.copy()
env["VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP"] = env_value
# 120s rather than 60s: cold CI containers can take 30-50s just to
# import vllm_omni → vllm → torch before the subprocess does any work.
result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, timeout=120, env=env)
if result.stdout.strip().startswith("SKIP:"):
pytest.skip("modelopt not available in subprocess")
# Exact token match on the final stdout line — not a substring test, since
# "INSTALLED" is a substring of "NOT_INSTALLED".
state = result.stdout.strip().splitlines()[-1] if result.stdout.strip() else ""
assert state in acceptable, (
f"VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP={env_value!r} expected one of {set(acceptable)!r} "
f"but got {state!r}:\nstdout: {result.stdout}\nstderr: {result.stderr}"
)
+183
View File
@@ -1,7 +1,9 @@
import logging
import os
import sys
from functools import cached_property
import torch
from aenum import extend_enum
from vllm.config import ModelConfig as _OriginalModelConfig
from vllm.inputs import TokensPrompt as _OriginalTokensPrompt
@@ -21,6 +23,8 @@ from vllm_omni.inputs.data import OmniTokensPrompt
from vllm_omni.model_executor.layers.rotary_embedding import OmniMRotaryEmbedding
from vllm_omni.request import OmniRequest, OmniStreamingUpdate
_PATCH_LOGGER = logging.getLogger("vllm_omni.patch")
# =============================================================================
# Patch ModelConfig.is_mm_prefix_lm to support omni-specific models
# =============================================================================
@@ -71,6 +75,185 @@ assert _installed is _patched_cp, (
"for HunyuanImage3 will not work. Check vLLM ModelConfig changes."
)
# =============================================================================
# Patch ModelOptNvFp4LinearMethod.process_weights_after_loading to clamp NaN
# bytes in per-block FP8 weight_scale tensors at load time.
# =============================================================================
# WHY: ModelOpt 0.44's float32 -> torch.float8_e4m3fn cast of per-block weight
# scales occasionally emits NaN bytes (E4M3 encoding 0x7F / 0xFF) when the
# pre-cast scale rounds above the FP8 E4M3 max of 448 after the global-scale
# division. Any single NaN byte in weight_scale propagates through the
# FlashInfer FP4 GEMM and the served model collapses to `!!!!`. NVFP4 W4A4
# Qwen3-Omni checkpoints published by the community — exported with stock
# ModelOpt 0.44 — currently fail this way the moment they're served.
#
# WHY here (vllm-omni, not upstream): the root cause sits in ModelOpt; the
# analogous fix in vLLM's own PWAL is also pending. Both are planned as
# follow-up PRs. Until those land in vllm-omni's pinned vLLM, this load-time
# clamp keeps the user-visible failure from looking like a vllm-omni
# inference bug. Newly calibrated, clean checkpoints pay no runtime cost
# (the clamp is a no-op when no NaN bytes are present).
#
# SCOPE: ModelOptNvFp4LinearMethod (W4A4 NVFP4 Linear) only. NvFp4FusedMoE /
# NvFp4W4A16 / CompressedTensors / Quark NVFP4 paths are not covered.
#
# SELF-EXTINGUISH: `_already_patched_upstream` heuristically detects when
# vLLM's own PWAL contains an in-place `masked_fill_` against `weight_scale`
# / `isnan` — the structure the upstream fix is expected to take when it is
# filed (planned as a follow-up PR after this one merges). Once vllm-omni's
# vllm pin moves to a release with that upstream fix, the override is
# skipped at import and this block can be deleted. NOTE: the heuristic only
# matches "vLLM PWAL clamps NaN in-place with `masked_fill_`"; if the
# upstream fix lands as `nan_to_num_` or as a clamp before the FP32→FP8
# cast, the check won't fire and this override stays active. The override
# is idempotent so the overlap is a warning log, not a correctness issue —
# but the heuristic should be revisited when the upstream PR is filed.
#
# ORDERING: the clamp must run BEFORE the original PWAL. The non-Blackwell
# Marlin fallback (sm_<100) casts weight_scale FP8 -> bf16/fp16 and permutes
# inside its kernel PWAL; an after-PWAL clamp would either trip the
# byte-view shape assertion (FP8 1B/elem -> bf16 2B/elem) or operate on
# already-transformed bytes where NaN has propagated through permute. On
# Blackwell (Cutlass/TRTLLM/cuDNN) the kernel PWAL only swizzles/shuffles
# FP8 bytes, so clamp-before is equivalent to clamp-after there.
def _clamp_nvfp4_weight_scale_nans(layer) -> int:
"""Scan ``layer.weight_scale`` for NaN bytes and clamp them to the FP8
E4M3 max byte (0x7E) in place. Returns the number of bytes clamped.
Scoped to NVFP4 ``float8_e4m3fn`` weight_scale only — a future MXFP4
weight-scale clamp would need its own helper (MXFP4 uses
``float8_e8m0fnu`` for scales, and the byte encoding of NaN differs).
Exposed at module scope so tests can exercise the clamp directly
without monkey-patching + reloading the parent ``vllm_omni.patch``
module.
"""
# Defensive: a stray subclass without weight_scale would crash inside
# the override; just bail out.
weight_scale = getattr(layer, "weight_scale", None)
if weight_scale is None:
return 0
# Re-entry safety: this clamp expects raw on-disk FP8 weight_scale (1
# byte/elem). If we are ever invoked after the original PWAL ran (e.g.
# a caller re-issues process_weights_after_loading on the same layer),
# weight_scale may have been cast to bf16/fp16 by the Marlin path, in
# which case the uint8 byte view below would have a different shape
# than the NaN mask. Bail out cleanly rather than trip the assert.
if weight_scale.dtype != torch.float8_e4m3fn:
return 0
# `.view(torch.uint8)` below requires a contiguous tensor and raises on a
# non-contiguous one. A checkpoint whose per-block scale tensor isn't
# contiguous would otherwise crash here. Materialize a contiguous copy and
# write it back to the Parameter so the in-place byte clamp lands on the
# tensor the kernel will actually read (a bare `.contiguous()` copy would
# be discarded and the original left un-clamped).
weight_scale_data = weight_scale.data
if not weight_scale_data.is_contiguous():
weight_scale_data = weight_scale_data.contiguous()
weight_scale.data = weight_scale_data
nan_mask = torch.isnan(weight_scale_data)
if not nan_mask.any():
return 0
# PyTorch does not implement masked_fill_ for float8_e4m3fn, so view
# the storage as uint8 and write the FP8 max byte (0x7E) directly.
# float8_e4m3fn is 1 byte per element so the byte view is shape-
# for-shape with the original tensor; pin that here so a future
# packed scale format would fail loudly rather than silently corrupt
# unrelated bytes.
byte_view = weight_scale_data.view(torch.uint8)
if byte_view.shape != nan_mask.shape:
raise RuntimeError(
f"NVFP4 weight_scale uint8 view shape {byte_view.shape} != NaN mask shape "
f"{nan_mask.shape}; per-block scale layout changed — recheck the clamp."
)
fp8_max_byte = (
torch.tensor(torch.finfo(torch.float8_e4m3fn).max, dtype=torch.float8_e4m3fn).view(torch.uint8).item()
)
byte_view.masked_fill_(nan_mask, fp8_max_byte)
n = int(nan_mask.sum().item())
_PATCH_LOGGER.warning("Clamped %d NaN entries in NVFP4 per-block weight_scale.", n)
return n
# Module-level defaults so downstream code (and tests) can import these names
# without guarding for the import-failure / escape-hatch branches below.
# `_already_patched_upstream` = upstream PWAL contains its own NaN clamp.
# `_clamp_installed` = our wrapper was installed on the upstream class.
# These are independent: the env-var escape hatch and the import-failure path
# both leave the wrapper uninstalled WITHOUT upstream being patched, so the
# right check for "we own NaN-clamp behavior" is `_clamp_installed`.
_already_patched_upstream = False
_clamp_installed = False
try:
# Escape hatch — set VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP=1 to skip installing
# the patch (e.g. to confirm a `!!!!` failure is the NaN-byte case).
# The escape-hatch deliberately raises ImportError so the not-installed
# warning below logs through the same path a real ImportError would.
# Use the repo-wide bool-env idiom so values like `0`, `false`, `no`,
# `off` correctly mean "do not skip" rather than tripping naive
# truthiness on the non-empty string.
if os.environ.get("VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP", "").lower() in ("1", "true", "yes", "on"):
raise ImportError("VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP is set; skipping NaN-clamp install")
from vllm.model_executor.layers.quantization.modelopt import (
ModelOptNvFp4LinearMethod as _OriginalModelOptNvFp4LinearMethod,
)
except ImportError as _nan_clamp_import_err:
_PATCH_LOGGER.warning(
"NVFP4 weight_scale NaN-clamp patch could NOT install: %s. NVFP4 W4A4 "
"checkpoints with NaN bytes in per-block weight_scale will serve `!!!!`.",
_nan_clamp_import_err,
)
else:
_current_nvfp4_pwal = _OriginalModelOptNvFp4LinearMethod.process_weights_after_loading
# Reload idempotency: on a module reload (importlib.reload in a test, or a
# second import path) the class attribute already holds OUR wrapper, so
# capturing it as the "original" and re-wrapping would nest the clamp and
# run it twice per load. Recover the genuine upstream method from the
# sentinel we stamp below; the heuristic + a fresh single-level wrapper are
# then computed against the real upstream, not against our own wrapper.
_original_nvfp4_pwal = getattr(_current_nvfp4_pwal, "_vllm_omni_wrapped_pwal", _current_nvfp4_pwal)
_upstream_pwal_names = set(_original_nvfp4_pwal.__code__.co_names or ())
# Require ALL three names — `weight_scale` alone is too loose because the
# current upstream PWAL already references `weight_scale_2` (close prefix
# collisions aside, future PWALs may legitimately reference `weight_scale`
# without a NaN clamp). `masked_fill_` + `isnan` + `weight_scale` together
# are the structural signature of an in-place NaN clamp on weight_scale.
# KNOWN BLIND SPOTS (false-negatives, safe direction — we install a
# redundant but idempotent clamp): upstream using
# `getattr(layer, "weight_scale")` (string lands in co_consts, not
# co_names), or factoring the clamp into a helper called from PWAL
# (none of the three names appear in the top-level co_names). Revisit
# this heuristic when the upstream PR is actually filed.
_already_patched_upstream = all(n in _upstream_pwal_names for n in ("masked_fill_", "isnan", "weight_scale"))
def _patched_nvfp4_pwal(self, layer, *args, **kwargs):
# Clamp BEFORE the original PWAL — see ORDERING note above.
_clamp_nvfp4_weight_scale_nans(layer)
_original_nvfp4_pwal(self, layer, *args, **kwargs)
# Sentinel so a later reload of this module recovers the genuine upstream
# method above instead of wrapping this wrapper (see reload-idempotency note).
_patched_nvfp4_pwal._vllm_omni_wrapped_pwal = _original_nvfp4_pwal
if not _already_patched_upstream:
_OriginalModelOptNvFp4LinearMethod.process_weights_after_loading = _patched_nvfp4_pwal
# Fail loudly if install dropped silently (e.g. another plugin
# patched the same class) rather than degrade to `!!!!` at decode.
# Use raise, not assert: asserts are compiled out under `python -O` /
# PYTHONOPTIMIZE, which would silently disable this guard in exactly
# the optimized runs where a conflicting plugin must still be caught.
if _OriginalModelOptNvFp4LinearMethod.process_weights_after_loading is not _patched_nvfp4_pwal:
raise RuntimeError("NVFP4 weight_scale NaN-clamp install failed — check for conflicting plugins.")
_clamp_installed = True
_PATCH_LOGGER.info(
"NVFP4 W4A4 weight_scale NaN-clamp: %s.",
"skipped (upstream already patched)" if _already_patched_upstream else "installed",
)
# =============================================================================
# Patch GlmImageTextConfig to expose mrope_section in rope_parameters
# =============================================================================