From ef908aeb401dcd80d0252415bb5d2d63d17bf5e8 Mon Sep 17 00:00:00 2001 From: Khush Gupta <78624519+khushgx@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:49:21 -0500 Subject: [PATCH] fixed trtllm nvfp4 backend for moe (#15022) --- .../srt/layers/moe/fused_moe_triton/layer.py | 16 ++++++-- .../srt/layers/quantization/modelopt_quant.py | 40 ++++++++++++++----- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/python/sglang/srt/layers/moe/fused_moe_triton/layer.py b/python/sglang/srt/layers/moe/fused_moe_triton/layer.py index c72e11e06..71b8e661a 100644 --- a/python/sglang/srt/layers/moe/fused_moe_triton/layer.py +++ b/python/sglang/srt/layers/moe/fused_moe_triton/layer.py @@ -1177,10 +1177,12 @@ class FlashInferFP4MoE(FusedMoE): False, # is_sf_swizzled_layout ) - hs_fp4 = hs_fp4_bytes.reshape( - hidden_states.shape[0], hidden_states.shape[1] // 2 + seq_len, hidden_size = hidden_states.shape + hs_fp4 = hs_fp4_bytes.reshape(seq_len, hidden_size // 2) + # TRT-LLM expects hidden state scales shaped as [seq_len, hidden_size // 16] + hs_sf = hs_sf_bytes.view(torch.float8_e4m3fn).reshape( + seq_len, hidden_size // 16 ) - hs_sf = hs_sf_bytes.view(torch.float8_e4m3fn).reshape(-1) return hs_fp4, hs_sf @@ -1277,7 +1279,13 @@ class FlashInferFP4MoE(FusedMoE): local_num_experts=self.num_local_experts, routed_scaling_factor=self.moe_runner_config.routed_scaling_factor, tile_tokens_dim=None, - routing_method_type=routing_method_type, + # Respect the routing method configured for this layer (e.g., Renormalize for Qwen3), + # instead of always assuming DeepSeekV3. + routing_method_type=( + self.routing_method_type + if self.routing_method_type is not None + else RoutingMethodType.Default + ), do_finalize=True, tune_max_num_tokens=next_power_of_2(hs_fp4.shape[0]), output=symm_output, diff --git a/python/sglang/srt/layers/quantization/modelopt_quant.py b/python/sglang/srt/layers/quantization/modelopt_quant.py index d4faa2ddf..70070611c 100755 --- a/python/sglang/srt/layers/quantization/modelopt_quant.py +++ b/python/sglang/srt/layers/quantization/modelopt_quant.py @@ -950,12 +950,22 @@ class ModelOptFp4Config(ModelOptQuantConfig): if not kv_cache_quant_algo: # For config.json format, derive from kv_cache_scheme if available kv_cache_scheme = config.get("kv_cache_scheme") - if ( - kv_cache_scheme - and kv_cache_scheme.get("type") == "float" - and kv_cache_scheme.get("num_bits") == 8 - ): - kv_cache_quant_algo = "FP8" + if isinstance(kv_cache_scheme, dict): + if ( + kv_cache_scheme.get("type") == "float" + and kv_cache_scheme.get("num_bits") == 8 + ): + kv_cache_quant_algo = "FP8" + else: + kv_cache_quant_algo = "auto" + elif isinstance(kv_cache_scheme, str): + scheme_name = kv_cache_scheme.strip().upper() + if scheme_name in ("FP8", "FLOAT8"): + kv_cache_quant_algo = "FP8" + elif scheme_name in ("FP4", "FLOAT4", "NVFP4"): + kv_cache_quant_algo = "NVFP4" + else: + kv_cache_quant_algo = "auto" else: kv_cache_quant_algo = "auto" @@ -1485,15 +1495,27 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase): ) } ) + block_size = 16 # Validate weight scales assert_dim = 2 if layer.moe_runner_config.is_gated else 1 for name, weight_scale in [ ("w13", layer.w13_weight_scale), ("w2", layer.w2_weight_scale), ]: - assert ( - weight_scale.shape[assert_dim] % 16 == 0 - ), f"Expected {name}_weight_scale.dim({assert_dim}) to be divisible by 16" + # For NVFP4 TRTLLM we require one scale per 16 inputs (last dim == expected_blocks[name]). + if get_moe_runner_backend().is_flashinfer_trtllm(): + expected_blocks = { + "w13": layer.w13_weight.shape[2] * 2 // block_size, + "w2": layer.w2_weight.shape[2] * 2 // block_size, + } + assert ( + weight_scale.shape[-1] == expected_blocks[name] + ), f"Expected {name}_weight_scale.dim(2) == {expected_blocks[name]}, got {weight_scale.shape[-1]}" + else: + # For other backends, ensure the per-input block dimension is aligned to 16. + assert ( + weight_scale.shape[assert_dim] % block_size == 0 + ), f"Expected {name}_weight_scale.dim({assert_dim}) to be divisible by {block_size}" assert ( weight_scale.dtype == torch.float8_e4m3fn ), f"{name} Weight Blockscale must be represented as FP8-E4M3"