From 983e643854f15cf9ef4370a49dfd74b6af54c3e3 Mon Sep 17 00:00:00 2001 From: ollybbmonster <42735247+ollybbmonster@users.noreply.github.com> Date: Mon, 21 Sep 2026 07:13:59 +0800 Subject: [PATCH] [Feature] support bf16 MoE router and mxfp4 MoE for MiMo V2 (#40448) --- .../attention/flashattention_backend.py | 7 +++++- python/sglang/srt/models/dflash.py | 3 ++- python/sglang/srt/models/mimo_v2.py | 22 +++++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/python/sglang/srt/layers/attention/flashattention_backend.py b/python/sglang/srt/layers/attention/flashattention_backend.py index 67ba07afd..eb4204e38 100644 --- a/python/sglang/srt/layers/attention/flashattention_backend.py +++ b/python/sglang/srt/layers/attention/flashattention_backend.py @@ -1927,7 +1927,12 @@ class FlashAttentionBackend(AttentionBackend): is_swa_layer = ( layer.sliding_window_size is not None and layer.sliding_window_size > -1 ) - window_size = (layer.sliding_window_size, 0) if is_swa_layer else (-1, -1) + if is_swa_layer and layer.attn_type == AttentionType.ENCODER_ONLY: + window_size = (layer.sliding_window_size, layer.sliding_window_size) + elif is_swa_layer: + window_size = (layer.sliding_window_size, 0) + else: + window_size = (-1, -1) causal = True if layer.is_cross_attention or layer.attn_type == AttentionType.ENCODER_ONLY: diff --git a/python/sglang/srt/models/dflash.py b/python/sglang/srt/models/dflash.py index 7dc69d513..5722b43d4 100644 --- a/python/sglang/srt/models/dflash.py +++ b/python/sglang/srt/models/dflash.py @@ -250,7 +250,8 @@ class DFlashAttention(nn.Module): # Per-head sink bias; each TP rank owns its slice of the # all-heads checkpoint tensor. self.attention_sink_bias = nn.Parameter( - torch.empty(self.num_heads, dtype=torch.float32), requires_grad=False + torch.empty(self.num_heads, dtype=torch.float32 if _is_npu else None), + requires_grad=False, ) set_weight_attrs( self.attention_sink_bias, diff --git a/python/sglang/srt/models/mimo_v2.py b/python/sglang/srt/models/mimo_v2.py index c09e54b02..1354710e7 100644 --- a/python/sglang/srt/models/mimo_v2.py +++ b/python/sglang/srt/models/mimo_v2.py @@ -349,7 +349,7 @@ class MoEGate(nn.Module): ): super().__init__() self.is_nextn = is_nextn - self.dtype = torch.float32 + self.dtype = getattr(torch, getattr(config, "moe_router_dtype", "float32")) self.weight = nn.Parameter( torch.empty((config.n_routed_experts, config.hidden_size), dtype=self.dtype) ) @@ -359,7 +359,7 @@ class MoEGate(nn.Module): if quant_config is not None and quant_config.get_name() == "modelopt_fp4" and get_moe_runner_backend().is_flashinfer_trtllm() - else self.dtype + else torch.float32 ) self.e_score_correction_bias = nn.Parameter( torch.empty((config.n_routed_experts), dtype=correction_bias_dtype) @@ -368,9 +368,15 @@ class MoEGate(nn.Module): self.e_score_correction_bias = None def forward(self, hidden_states): - logits = F.linear(hidden_states.to(self.dtype), self.weight, None) + if self.dtype != torch.float32 and hidden_states.is_cuda: + return torch.mm( + hidden_states.to(self.dtype), + self.weight.t(), + out_dtype=torch.float32, + ) - return logits + logits = F.linear(hidden_states.to(self.dtype), self.weight, None) + return logits.to(torch.float32) class MiMoV2MoE(nn.Module): @@ -427,6 +433,7 @@ class MiMoV2MoE(nn.Module): num_expert_group=config.n_group, topk_group=config.topk_group, correction_bias=self.gate.e_score_correction_bias, + is_fp4_experts=getattr(quant_config, "is_fp4_experts", False), scoring_func=config.scoring_func, quant_config=quant_config, routed_scaling_factor=1.0, @@ -1591,6 +1598,13 @@ class MiMoV2ForCausalLM(nn.Module, AudioEncoderMixin): skipped_mtp_weights = True continue + if ".mlp.experts." in name and loaded_weight.dtype == torch.uint8: + if name.endswith(".weight_scale"): + name = name + "_inv" + loaded_weight = torch.exp2(loaded_weight.to(torch.float32) - 127.0) + elif name.endswith(".weight"): + loaded_weight = loaded_weight.view(torch.int8) + # Support fused qkv_proj checkpoint (Pro format) if "qkv_proj" in name: if name in params_dict: