From 93e9db5eb89d51e6818d43062d0296612bf53061 Mon Sep 17 00:00:00 2001 From: zijiec Date: Tue, 11 Aug 2026 21:50:05 -0400 Subject: [PATCH] [Fix][Qwen]: fused shared-expert detection PP-safe protection (#34447) --- python/sglang/srt/models/qwen3_5.py | 12 ++-- .../models/test_qwen3_5_pipeline_parallel.py | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 test/registered/unit/models/test_qwen3_5_pipeline_parallel.py diff --git a/python/sglang/srt/models/qwen3_5.py b/python/sglang/srt/models/qwen3_5.py index 1a15ffc67..40d1810f9 100644 --- a/python/sglang/srt/models/qwen3_5.py +++ b/python/sglang/srt/models/qwen3_5.py @@ -1961,13 +1961,13 @@ class Qwen3_5MoeForConditionalGeneration(Qwen3VLForConditionalGeneration): return module_name.startswith("model.layers.") def _get_num_fused_shared_experts(self): - if not ( - hasattr(self.model, "layers") - and len(self.model.layers) > 0 - and hasattr(self.model.layers[0].mlp, "num_fused_shared_experts") - ): + if not hasattr(self.model, "layers"): return 0 - return self.model.layers[0].mlp.num_fused_shared_experts + for layer_id in range(self.model.start_layer, self.model.end_layer): + mlp = getattr(self.model.layers[layer_id], "mlp", None) + if hasattr(mlp, "num_fused_shared_experts"): + return mlp.num_fused_shared_experts + return 0 def get_embed_and_head(self): embed = self.model.embed_tokens.weight if self.pp_group.is_first_rank else None diff --git a/test/registered/unit/models/test_qwen3_5_pipeline_parallel.py b/test/registered/unit/models/test_qwen3_5_pipeline_parallel.py new file mode 100644 index 000000000..f239893a3 --- /dev/null +++ b/test/registered/unit/models/test_qwen3_5_pipeline_parallel.py @@ -0,0 +1,69 @@ +import unittest +from types import SimpleNamespace + +from sglang.srt.layers.utils import PPMissingLayer +from sglang.srt.models.qwen3_5 import Qwen3_5MoeForConditionalGeneration +from sglang.test.ci.ci_register import register_cpu_ci +from sglang.test.test_utils import CustomTestCase + +register_cpu_ci(est_time=2, suite="base-a-test-cpu") + + +class TestQwen3_5PipelineParallel(CustomTestCase): + @staticmethod + def _get_num_fused_shared_experts(layers, start_layer, end_layer): + model = SimpleNamespace( + model=SimpleNamespace( + layers=layers, + start_layer=start_layer, + end_layer=end_layer, + ) + ) + return Qwen3_5MoeForConditionalGeneration._get_num_fused_shared_experts(model) + + def test_get_num_fused_shared_experts_returns_zero_without_layers(self): + model = SimpleNamespace(model=SimpleNamespace()) + + num_fused_shared_experts = ( + Qwen3_5MoeForConditionalGeneration._get_num_fused_shared_experts(model) + ) + + self.assertEqual(num_fused_shared_experts, 0) + + def test_get_num_fused_shared_experts_uses_local_pp_layers(self): + layers = [ + PPMissingLayer(), + PPMissingLayer(), + SimpleNamespace( + mlp=SimpleNamespace(num_fused_shared_experts=1), + ), + SimpleNamespace( + mlp=SimpleNamespace(num_fused_shared_experts=1), + ), + ] + + num_fused_shared_experts = self._get_num_fused_shared_experts( + layers, + start_layer=2, + end_layer=4, + ) + + self.assertEqual(num_fused_shared_experts, 1) + + def test_get_num_fused_shared_experts_returns_zero_without_local_fusion(self): + layers = [ + PPMissingLayer(), + SimpleNamespace(mlp=SimpleNamespace()), + ] + + num_fused_shared_experts = self._get_num_fused_shared_experts( + layers, + start_layer=1, + end_layer=2, + ) + + self.assertEqual(num_fused_shared_experts, 0) + + +if __name__ == "__main__": + unittest.main()