Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions python/sglang/srt/models/glm5_next.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
GlmOcrVisionPatchEmbed,
GlmOcrVisionPatchMerger,
)
from sglang.srt.models.utils import WeightsMapper
from sglang.srt.multimodal.mm_utils import (
run_dp_presharded_mrope_vision_model,
run_dp_sharded_mrope_vision_model,
Expand Down Expand Up @@ -1077,6 +1078,13 @@ def forward(


class Glm5NextForConditionalGeneration(nn.Module):
hf_to_sglang_mapper = WeightsMapper(
orig_to_new_substr={
"model.language_model.": "model.",
"model.visual": "visual",
}
)

packed_modules_mapping = {
"fused_qkv_a_proj_with_mqa": ["q_a_proj", "kv_a_proj_with_mqa"],
"fused_qkvbfg_a_proj": [
Expand Down Expand Up @@ -1200,6 +1208,17 @@ def shared_experts_fusion_disable_reason(cls, hf_config, quant_config):
text_config = getattr(hf_config, "text_config", hf_config)
if not getattr(text_config, "n_shared_experts", None):
return "No shared experts are defined in the config."
if quant_config is not None and quant_config.get_name() == "modelopt_fp4":
Comment thread
nvpohanh marked this conversation as resolved.
first_sparse_layer = getattr(text_config, "first_k_dense_replace", 0)
for layer_id in range(first_sparse_layer, text_config.num_hidden_layers):
moe_prefix = f"model.layers.{layer_id}.mlp"
if quant_config.is_layer_excluded(
f"{moe_prefix}.shared_experts"
) and not quant_config.is_layer_excluded(f"{moe_prefix}.experts"):
return (
"ModelOpt FP4 keeps shared experts unquantized while routed "
"experts are quantized."
)
if not _is_cuda:
return "Shared experts fusion currently requires CUDA devices."
if _device_sm is not None and _device_sm < 80:
Expand Down
104 changes: 104 additions & 0 deletions test/registered/unit/models/test_glm5_next_modelopt.py
Comment thread
YAMY1234 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import unittest
from types import SimpleNamespace
from unittest.mock import patch

from sglang.srt.layers.quantization.modelopt_quant import ModelOptFp4Config
from sglang.srt.models import glm5_next
from sglang.srt.models.glm5_next import Glm5NextForConditionalGeneration
from sglang.srt.runtime_context import get_parallel
from sglang.test.ci.ci_register import register_cpu_ci
from sglang.test.test_utils import CustomTestCase

register_cpu_ci(est_time=5, suite="base-a-test-cpu")

NVIDIA_EXCLUDE_MODULES = [
"model.language_model.embed_tokens",
"model.language_model.layers.11.self_attn*",
"model.language_model.layers.11.mlp.gate",
"model.language_model.layers.11.mlp.shared_experts*",
"model.visual*",
]

RADIXARK_EXCLUDE_MODULES = [
"model.language_model.embed_tokens",
"model.language_model.layers.11.self_attn*",
"model.language_model.layers.11.mlp.gate",
"model.visual*",
"model.embed_tokens",
"model.layers.11.self_attn*",
"model.layers.11.mlp.gate",
"model.language_model.layers.45*",
"model.layers.45*",
"visual*",
]


class TestGlm5NextModelOpt(CustomTestCase):
Comment thread
nvpohanh marked this conversation as resolved.
def _config(self, exclude_modules):
config = ModelOptFp4Config(
is_checkpoint_nvfp4_serialized=True,
group_size=16,
exclude_modules=exclude_modules,
)
config.apply_weight_name_mapper(
Glm5NextForConditionalGeneration.hf_to_sglang_mapper
)
return config

def _hf_config(self):
return SimpleNamespace(
text_config=SimpleNamespace(
first_k_dense_replace=3,
num_hidden_layers=45,
n_shared_experts=1,
)
)

def test_checkpoint_exclusions_match_sglang_module_names(self):
checkpoints = {
"nvidia/GLM-5.3-Flash-NVFP4": NVIDIA_EXCLUDE_MODULES,
"RadixArk/GLM-5.3-Flash-NVFP4": RADIXARK_EXCLUDE_MODULES,
}

for checkpoint, exclude_modules in checkpoints.items():
with self.subTest(checkpoint=checkpoint):
config = self._config(exclude_modules)
self.assertTrue(config.is_layer_excluded("model.embed_tokens"))
self.assertTrue(
config.is_layer_excluded("model.layers.11.self_attn.kv_b_proj")
)
self.assertTrue(config.is_layer_excluded("model.layers.11.mlp.gate"))
self.assertTrue(
config.is_layer_excluded("visual.blocks.0.attn.qkv_proj")
)

def test_nvidia_mixed_precision_shared_experts_disable_fusion(self):
config = self._config(NVIDIA_EXCLUDE_MODULES)

reason = Glm5NextForConditionalGeneration.shared_experts_fusion_disable_reason(
self._hf_config(), config
)

self.assertIn("shared experts unquantized", reason)

def test_radixark_uniform_fp4_shared_experts_keep_fusion(self):
config = self._config(RADIXARK_EXCLUDE_MODULES)
a2a_backend = SimpleNamespace(is_deepep=lambda: False)

with (
patch.object(glm5_next, "_is_cuda", True),
patch.object(glm5_next, "_device_sm", 100),
patch.object(glm5_next, "get_moe_a2a_backend", return_value=a2a_backend),
get_parallel().override(moe_ep_size=1),
):
reason = (
Glm5NextForConditionalGeneration.shared_experts_fusion_disable_reason(
self._hf_config(), config
)
)

self.assertIsNone(reason)


if __name__ == "__main__":
unittest.main()
Loading