@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 739e5945dc
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 5, 06:00 UTC

[Quantization] [Refactor] Create special "GptOssMxfp4MoeMethod" (#39604)

Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>

Yongye Zhu committed Apr 13, 2026 at 12:53 UTC 739e5945dc4b3ba30d84bdf6e637657abd4136b8
18 files changed +129 -34
docs/design/moe_kernel_features.md
+1 -1
@@ -59,7 +59,7 @@ Modular kernels are supported by the following `FusedMoEMethodBase` classes.
59 - [`Fp8MoEMethod`][vllm.model_executor.layers.quantization.fp8.Fp8MoEMethod]
60 - [`CompressedTensorsW4A4Nvfp4MoEMethod`][vllm.model_executor.layers.quantization.compressed_tensors.compressed_tensors_moe.compressed_tensors_moe_w4a4_nvfp4.CompressedTensorsW4A4Nvfp4MoEMethod]
61 - [`CompressedTensorsW8A8Fp8MoEMethod`][vllm.model_executor.layers.quantization.compressed_tensors.compressed_tensors_moe.compressed_tensors_moe_w8a8_fp8.CompressedTensorsW8A8Fp8MoEMethod]
62 -- [`Mxfp4MoEMethod`][vllm.model_executor.layers.quantization.mxfp4.Mxfp4MoEMethod]
62 +- [`GptOssMxfp4MoEMethod`][vllm.model_executor.layers.quantization.mxfp4.GptOssMxfp4MoEMethod]
63 - [`UnquantizedFusedMoEMethod`][vllm.model_executor.layers.fused_moe.layer.UnquantizedFusedMoEMethod]
64
65 ## Fused Experts Kernels
vllm/config/model.py
+2 -1
@@ -951,6 +951,7 @@ class ModelConfig:
951 # Ensure heavy backends are probed last to avoid unnecessary
952 # imports during override detection (e.g., MXFP4 imports Triton)
953 "mxfp4",
954 + "gpt_oss_mxfp4",
955 "cpu_awq",
956 "gguf",
957 ]
@@ -966,7 +967,7 @@ class ModelConfig:
967 for name in quantization_methods:
968 method = me_quant.get_quantization_config(name)
969 quantization_override = method.override_quantization_method(
969 - quant_cfg, self.quantization
970 + quant_cfg, self.quantization, hf_config=self.hf_config
971 )
972 if quantization_override is not None:
973 # Raise error if the override is not custom (custom would
vllm/model_executor/layers/fused_moe/layer.py
+1 -1
@@ -1063,7 +1063,7 @@ class FusedMoE(CustomOp):
1063 expert_id: int,
1064 return_success: bool = False,
1065 ) -> bool | None:
1066 - if self.quant_config and self.quant_config.get_name() == "mxfp4":
1066 + if self.quant_config and self.quant_config.get_name() == "gpt_oss_mxfp4":
1067 # (FIXME) for gpt-oss all experts are combined
1068 if "bias" in weight_name:
1069 dim1 = loaded_weight.shape[1]
vllm/model_executor/layers/fused_moe/oracle/mxfp4.py
+6 -3
@@ -194,7 +194,7 @@ def _backend_activation_key(backend: Mxfp4MoeBackend) -> QuantKey | None:
194 return None
195
196
197 -def select_mxfp4_moe_backend(
197 +def select_gpt_oss_mxfp4_moe_backend(
198 config: FusedMoEConfig,
199 ) -> tuple[Mxfp4MoeBackend, type[mk.FusedMoEExperts] | None]:
200 """
@@ -400,7 +400,7 @@ def mxfp4_round_up_hidden_size_and_intermediate_size(
400 return hidden_size, intermediate_size
401
402
403 -def convert_to_mxfp4_moe_kernel_format(
403 +def convert_gpt_oss_weight_to_mxfp4_moe_kernel_format(
404 mxfp4_backend: Mxfp4MoeBackend,
405 layer: torch.nn.Module,
406 w13_weight: torch.Tensor,
@@ -426,7 +426,10 @@ def convert_to_mxfp4_moe_kernel_format(
426
427 sf_block_size = 32 # mxfp4 block size
428
429 - if mxfp4_backend in (Mxfp4MoeBackend.MARLIN, Mxfp4MoeBackend.BATCHED_MARLIN):
429 + if mxfp4_backend in (
430 + Mxfp4MoeBackend.MARLIN,
431 + Mxfp4MoeBackend.BATCHED_MARLIN,
432 + ):
433 from vllm.model_executor.layers.quantization.utils.marlin_utils_fp4 import (
434 prepare_moe_mxfp4_layer_for_marlin,
435 )
vllm/model_executor/layers/quantization/__init__.py
+3 -1
@@ -30,6 +30,7 @@ QuantizationMethods = Literal[
30 "torchao",
31 "inc",
32 "mxfp4",
33 + "gpt_oss_mxfp4",
34 "mxfp8",
35 "cpu_awq",
36 "online",
@@ -133,7 +134,7 @@ def get_quantization_config(quantization: str) -> type[QuantizationConfig]:
134 ModelOptNvFp4Config,
135 )
136 from .moe_wna16 import MoeWNA16Config
136 - from .mxfp4 import Mxfp4Config
137 + from .mxfp4 import GptOssMxfp4Config, Mxfp4Config
138 from .mxfp8 import Mxfp8Config
139 from .online.base import OnlineQuantizationConfig
140 from .torchao import TorchAOConfig
@@ -160,6 +161,7 @@ def get_quantization_config(quantization: str) -> type[QuantizationConfig]:
161 "auto-round": INCConfig,
162 "inc": INCConfig,
163 "mxfp4": Mxfp4Config,
164 + "gpt_oss_mxfp4": GptOssMxfp4Config,
165 "mxfp8": Mxfp8Config,
166 "cpu_awq": CPUAWQConfig,
167 "online": OnlineQuantizationConfig,
vllm/model_executor/layers/quantization/awq_marlin.py
+1 -1
@@ -232,7 +232,7 @@ class AWQMarlinConfig(QuantizationConfig):
232
233 @classmethod
234 def override_quantization_method(
235 - cls, hf_quant_cfg, user_quant
235 + cls, hf_quant_cfg, user_quant, hf_config=None
236 ) -> "QuantizationMethods | None":
237 # Skip override to marlin kernels, as they are not
238 # batch invariant
vllm/model_executor/layers/quantization/base_config.py
+11 -2
@@ -110,13 +110,22 @@ class QuantizationConfig(ABC):
110
111 @classmethod
112 def override_quantization_method(
113 - cls, hf_quant_cfg, user_quant
113 + cls,
114 + hf_quant_cfg: dict[str, Any],
115 + user_quant: str | None,
116 + hf_config: Any = None,
117 ) -> QuantizationMethods | None:
118 """
119 Detects if this quantization method can support a given checkpoint
120 format by overriding the user specified quantization method --
121 this method should only be overwritten by subclasses in exceptional
119 - circumstances
122 + circumstances.
123 +
124 + Args:
125 + hf_quant_cfg: The checkpoint's quantization config dict.
126 + user_quant: The user-specified quantization method string.
127 + hf_config: The HuggingFace model config object (e.g. for
128 + model_type checks). May be None if not available.
129 """
130 return None
131
vllm/model_executor/layers/quantization/cpu_wna16.py
+1 -1
@@ -104,7 +104,7 @@ class CPUAWQConfig(QuantizationConfig):
104
105 @classmethod
106 def override_quantization_method(
107 - cls, hf_quant_cfg, user_quant
107 + cls, hf_quant_cfg, user_quant, hf_config=None
108 ) -> "QuantizationMethods | None":
109 quant_method = hf_quant_cfg.get("quant_method", "").lower()
110 if current_platform.is_cpu() and (quant_method == "awq"):
vllm/model_executor/layers/quantization/gguf.py
+1 -1
@@ -84,7 +84,7 @@ class GGUFConfig(QuantizationConfig):
84
85 @classmethod
86 def override_quantization_method(
87 - cls, hf_quant_cfg: dict[str, Any], user_quant: str | None
87 + cls, hf_quant_cfg: dict[str, Any], user_quant: str | None, hf_config=None
88 ) -> "QuantizationMethods | None":
89 # When user explicitly specifies --quantization gguf, override
90 # whatever quantization method is in the HF model config (e.g. fp8).
vllm/model_executor/layers/quantization/gptq_marlin.py
+1 -1
@@ -214,7 +214,7 @@ class GPTQMarlinConfig(QuantizationConfig):
214
215 @classmethod
216 def override_quantization_method(
217 - cls, hf_quant_cfg, user_quant
217 + cls, hf_quant_cfg, user_quant, hf_config=None
218 ) -> QuantizationMethods | None:
219 can_convert = cls.is_gptq_marlin_compatible(hf_quant_cfg)
220
vllm/model_executor/layers/quantization/inc.py
+1 -1
@@ -453,7 +453,7 @@ class INCConfig(QuantizationConfig):
453
454 @classmethod
455 def override_quantization_method(
456 - cls, hf_quant_cfg, user_quant
456 + cls, hf_quant_cfg, user_quant, hf_config=None
457 ) -> "QuantizationMethods | None":
458 """Override the `auto-round` method to `inc`."""
459 is_auto_round_format = hf_quant_cfg.get("quant_method", None) == "auto-round"
vllm/model_executor/layers/quantization/modelopt.py
+4 -4
@@ -406,7 +406,7 @@ class ModelOptFp8Config(ModelOptQuantConfigBase):
406
407 @classmethod
408 def override_quantization_method(
409 - cls, hf_quant_cfg, user_quant
409 + cls, hf_quant_cfg, user_quant, hf_config=None
410 ) -> QuantizationMethods | None:
411 algo = cls._extract_modelopt_quant_algo(hf_quant_cfg)
412 if algo is not None and algo == "FP8":
@@ -1028,7 +1028,7 @@ class ModelOptNvFp4Config(ModelOptQuantConfigBase):
1028
1029 @classmethod
1030 def override_quantization_method(
1031 - cls, hf_quant_cfg, user_quant
1031 + cls, hf_quant_cfg, user_quant, hf_config=None
1032 ) -> QuantizationMethods | None:
1033 algo = cls._extract_modelopt_quant_algo(hf_quant_cfg)
1034 if algo is not None and ("NVFP4" in algo or "FP4" in algo):
@@ -1525,7 +1525,7 @@ class ModelOptMxFp8Config(ModelOptQuantConfigBase):
1525
1526 @classmethod
1527 def override_quantization_method(
1528 - cls, hf_quant_cfg, user_quant
1528 + cls, hf_quant_cfg, user_quant, hf_config=None
1529 ) -> QuantizationMethods | None:
1530 algo = cls._extract_modelopt_quant_algo(hf_quant_cfg)
1531 if algo is not None and "MXFP8" in algo:
@@ -2052,7 +2052,7 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfigBase):
2052
2053 @classmethod
2054 def override_quantization_method(
2055 - cls, hf_quant_cfg, user_quant
2055 + cls, hf_quant_cfg, user_quant, hf_config=None
2056 ) -> QuantizationMethods | None:
2057 algo = cls._extract_modelopt_quant_algo(hf_quant_cfg)
2058 if algo is not None and algo == "MIXED_PRECISION":
vllm/model_executor/layers/quantization/moe_wna16.py
+1 -1
@@ -130,7 +130,7 @@ class MoeWNA16Config(QuantizationConfig):
130
131 @classmethod
132 def override_quantization_method(
133 - cls, hf_quant_cfg, user_quant
133 + cls, hf_quant_cfg, user_quant, hf_config=None
134 ) -> QuantizationMethods | None:
135 can_convert = cls.is_moe_wna16_compatible(hf_quant_cfg)
136 if can_convert and user_quant == "moe_wna16":
vllm/model_executor/layers/quantization/mxfp4.py
+48 -7
@@ -19,11 +19,11 @@ from vllm.model_executor.layers.fused_moe.config import (
19 from vllm.model_executor.layers.fused_moe.oracle.mxfp4 import (
20 TRITON_BACKENDS,
21 Mxfp4MoeBackend,
22 - convert_to_mxfp4_moe_kernel_format,
22 + convert_gpt_oss_weight_to_mxfp4_moe_kernel_format,
23 make_mxfp4_moe_kernel,
24 make_mxfp4_moe_quant_config,
25 mxfp4_round_up_hidden_size_and_intermediate_size,
26 - select_mxfp4_moe_backend,
26 + select_gpt_oss_mxfp4_moe_backend,
27 )
28 from vllm.model_executor.layers.linear import LinearBase, UnquantizedLinearMethod
29 from vllm.model_executor.layers.quantization import QuantizationMethods
@@ -38,6 +38,12 @@ logger = init_logger(__name__)
38
39
40 class Mxfp4Config(QuantizationConfig):
41 + """Canonical base config for MXFP4 quantization.
42 +
43 + Subclasses override get_name() and override_quantization_method() to
44 + register themselves as the handler for a specific checkpoint format.
45 + """
46 +
47 def __init__(self, ignored_layers: list[str] | None = None):
48 super().__init__()
49 self.ignored_layers = ignored_layers
@@ -62,6 +68,8 @@ class Mxfp4Config(QuantizationConfig):
68 def get_config_filenames(cls) -> list[str]:
69 return []
70
71 + # TODO (zyongye) This is only temporaty fallback.
72 + # We should have `Mxfp4MoEMethod` after this migration is complete.
73 def get_quant_method(
74 self, layer: torch.nn.Module, prefix: str
75 ) -> "QuantizeMethodBase | None":
@@ -79,7 +87,7 @@ class Mxfp4Config(QuantizationConfig):
87 )
88 return UnquantizedLinearMethod()
89 elif isinstance(layer, FusedMoE):
82 - return Mxfp4MoEMethod(layer.moe_config)
90 + return GptOssMxfp4MoEMethod(layer.moe_config)
91 elif isinstance(layer, Attention):
92 logger.debug_once(
93 "MXFP4 attention layer is not implemented. "
@@ -93,13 +101,46 @@ class Mxfp4Config(QuantizationConfig):
101 return True
102
103
96 -class Mxfp4MoEMethod(FusedMoEMethodBase):
104 +class GptOssMxfp4Config(Mxfp4Config):
105 + """MXFP4 config for GPT-OSS checkpoints.
106 +
107 + Checkpoints carry ``"quant_method": "mxfp4"`` in their JSON config.
108 + override_quantization_method() maps that to the canonical internal name
109 + so that the rest of the loading path uses "gpt_oss_mxfp4" consistently.
110 + """
111 +
112 + @classmethod
113 + def get_name(cls) -> QuantizationMethods:
114 + return "gpt_oss_mxfp4"
115 +
116 + @classmethod
117 + def override_quantization_method(
118 + cls, hf_quant_cfg, user_quant, hf_config=None
119 + ) -> QuantizationMethods | None:
120 + # Match both "mxfp4" (original checkpoint value) and "gpt_oss_mxfp4"
121 + # (already normalized by verify_and_update_model_config) so that
122 + # explicit --quantization mxfp4 from the user doesn't cause a mismatch.
123 + if not (
124 + isinstance(hf_quant_cfg, dict)
125 + and hf_quant_cfg.get("quant_method") in ("mxfp4", "gpt_oss_mxfp4")
126 + ):
127 + return None
128 + # Require explicit confirmation that this is a GPT-OSS model.
129 + # Do NOT fall back to returning the override when hf_config is None,
130 + # as that would silently claim all mxfp4 checkpoints.
131 + model_type = getattr(hf_config, "model_type", None)
132 + if model_type != "gpt_oss":
133 + return None
134 + return "gpt_oss_mxfp4"
135 +
136 +
137 +class GptOssMxfp4MoEMethod(FusedMoEMethodBase):
138 """MXFP4 MoE quantization method."""
139
140 def __init__(self, moe: FusedMoEConfig):
141 super().__init__(moe)
101 - self.weight_dtype = "mxfp4"
102 - self.mxfp4_backend, self.experts_cls = select_mxfp4_moe_backend(moe)
142 + self.weight_dtype = "gpt_oss_mxfp4"
143 + self.mxfp4_backend, self.experts_cls = select_gpt_oss_mxfp4_moe_backend(moe)
144
145 self.max_capture_size = (
146 get_current_vllm_config().compilation_config.max_cudagraph_capture_size
@@ -281,7 +322,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
322
323 # Convert weights to kernel format
324 w13, w2, w13_scale, w2_scale, w13_bias, w2_bias = (
284 - convert_to_mxfp4_moe_kernel_format(
325 + convert_gpt_oss_weight_to_mxfp4_moe_kernel_format(
326 mxfp4_backend=self.mxfp4_backend,
327 layer=layer,
328 w13_weight=w13,
vllm/model_executor/layers/quantization/quark/quark_moe.py
+4 -4
@@ -30,11 +30,11 @@ from vllm.model_executor.layers.fused_moe.fused_marlin_moe import fused_marlin_m
30 from vllm.model_executor.layers.fused_moe.oracle.mxfp4 import (
31 TRITON_BACKENDS,
32 Mxfp4MoeBackend,
33 - convert_to_mxfp4_moe_kernel_format,
33 + convert_gpt_oss_weight_to_mxfp4_moe_kernel_format,
34 make_mxfp4_moe_kernel,
35 make_mxfp4_moe_quant_config,
36 mxfp4_round_up_hidden_size_and_intermediate_size,
37 - select_mxfp4_moe_backend,
37 + select_gpt_oss_mxfp4_moe_backend,
38 )
39 from vllm.model_executor.layers.quantization.utils.marlin_utils_fp8 import (
40 prepare_fp8_moe_layer_for_marlin,
@@ -995,7 +995,7 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
995 self.w2_precision_config = None
996
997 if self.ocp_mx_scheme == "w_mxfp4":
998 - self.mxfp4_backend, self.experts_cls = select_mxfp4_moe_backend(moe)
998 + self.mxfp4_backend, self.experts_cls = select_gpt_oss_mxfp4_moe_backend(moe)
999 elif self.ocp_mx_scheme.startswith("w_mxfp4"):
1000 # TODO(bowenbao): refactor and introduce backends for other OCP MX schemes.
1001 self.mxfp4_backend = Mxfp4MoeBackend.NONE
@@ -1300,7 +1300,7 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1300
1301 # Convert weights to kernel format
1302 w13, w2, w13_scale, w2_scale, w13_bias, w2_bias = (
1303 - convert_to_mxfp4_moe_kernel_format(
1303 + convert_gpt_oss_weight_to_mxfp4_moe_kernel_format(
1304 mxfp4_backend=self.mxfp4_backend,
1305 layer=layer,
1306 w13_weight=w13,
vllm/model_executor/models/config.py
+17
@@ -108,6 +108,23 @@ class Gemma4Config(VerifyAndUpdateConfig):
108
109
110 class GptOssForCausalLMConfig(VerifyAndUpdateConfig):
111 + @staticmethod
112 + def verify_and_update_model_config(model_config: "ModelConfig") -> None:
113 + quant_config = getattr(model_config.hf_config, "quantization_config", None)
114 + if quant_config is not None and quant_config.get("quant_method") == "mxfp4":
115 + model_config.hf_config.quantization_config["quant_method"] = "gpt_oss_mxfp4"
116 +
117 + hf_text_quant_config = getattr(
118 + model_config.hf_text_config, "quantization_config", None
119 + )
120 + if (
121 + hf_text_quant_config is not None
122 + and hf_text_quant_config.get("quant_method") == "mxfp4"
123 + ):
124 + model_config.hf_text_config.quantization_config["quant_method"] = (
125 + "gpt_oss_mxfp4"
126 + )
127 +
128 @staticmethod
129 def verify_and_update_config(vllm_config: "VllmConfig") -> None:
130 structured_outputs_config = vllm_config.structured_outputs_config
vllm/model_executor/models/gpt_oss.py
+25 -3
@@ -560,6 +560,14 @@ class GptOssModel(nn.Module, EagleModelMixin):
560 pcp_rank=get_pcp_group().rank_in_group,
561 )
562
563 + def _is_mxfp4(weight_dtype: str | None) -> bool:
564 + """Return True for any MXFP4 weight-dtype variant.
565 +
566 + Covers "gpt_oss_mxfp4" (GptOssMxfp4MoEMethod) and "mxfp4"
567 + (QuarkMoEMethod with fp4 weights) and any future variants.
568 + """
569 + return weight_dtype is not None and "mxfp4" in weight_dtype
570 +
571 def _get_moe_weight_dtype(layer_id: int = 0) -> str | None:
572 """Helper function to get MoE quantization weight dtype.
573
@@ -578,7 +586,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
586
587 moe_weight_dtype = _get_moe_weight_dtype(layer_id=0)
588
581 - if moe_weight_dtype == "mxfp4":
589 + if _is_mxfp4(moe_weight_dtype):
590 # MXFP4 requires OCP_MX_BLOCK_SIZE alignment
591 intermediate_size_block = intermediate_size // OCP_MX_BLOCK_SIZE
592 per_rank_intermediate_size_block = cdiv(intermediate_size_block, tp_size)
@@ -682,7 +690,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
690 continue
691
692 # Unified handler for mxfp4 weights and scales
685 - elif moe_quant_method == "mxfp4" and any(
693 + elif _is_mxfp4(moe_quant_method) and any(
694 name.endswith(suffix)
695 for suffix in [
696 ".w13_weight_scale",
@@ -1116,8 +1124,22 @@ class GptOssModel(nn.Module, EagleModelMixin):
1124 if hasattr(self.config, "quantization_config")
1125 else None
1126 )
1119 -
1127 + # Normalize the checkpoint's quant_method to the internal name.
1128 + # Note: there are three places where "mxfp4" -> "gpt_oss_mxfp4"
1129 + # normalization occurs, each serving a different data path:
1130 + # 1. GptOssMxfp4Config.override_quantization_method() — sets
1131 + # ModelConfig.quantization (used to select the QuantizationConfig
1132 + # class at model init time), reading from model_arch_config which
1133 + # is a snapshot taken before verify_and_update_model_config runs.
1134 + # 2. GptOssForCausalLMConfig.verify_and_update_model_config() —
1135 + # patches hf_config.quantization_config in-place (a separate copy
1136 + # of the dict from model_arch_config) for later hf_config lookups.
1137 + # 3. Here — reads directly from self.config (the raw HF config) which
1138 + # may still carry the original "mxfp4" string from the checkpoint.
1139 if quant_method == "mxfp4":
1140 + quant_method = "gpt_oss_mxfp4"
1141 +
1142 + if quant_method == "gpt_oss_mxfp4":
1143 return self._load_weights_mxfp4(
1144 ep_rank_end,
1145 ep_rank_start,
vllm/model_executor/models/transformers/base.py
+1 -1
@@ -150,7 +150,7 @@ class Base(
150 if self.quant_config:
151 quant_method_name = self.quant_config.get_name()
152 # Check for unsupported quantization methods.
153 - if quant_method_name == "mxfp4":
153 + if quant_method_name in ("mxfp4", "gpt_oss_mxfp4"):
154 raise NotImplementedError(
155 "Transformers modeling backend does "
156 "not support MXFP4 quantization yet."