TOTORONG commited on
Commit
c41cf6f
·
verified ·
1 Parent(s): 8c38beb

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. pyproject.toml +11 -0
  2. vllm_solon_moe.py +132 -0
pyproject.toml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "vllm-solon-moe"
3
+ version = "1.0.0"
4
+ description = "vLLM serving plugin for Solon-MoE (per-layer MoE over native Gemma4)"
5
+ requires-python = ">=3.10"
6
+
7
+ [project.entry-points."vllm.general_plugins"]
8
+ solon_moe = "vllm_solon_moe:register"
9
+
10
+ [tool.setuptools]
11
+ py-modules = ["vllm_solon_moe"]
vllm_solon_moe.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ vllm_solon_moe.py -- Solon-MoE support for vLLM via a thin shim over the
4
+ native Gemma4 implementation.
5
+
6
+ Background: Solon-MoE checkpoints use the *native* transformers Gemma4 MoE
7
+ block (packed gate_up_proj [E, 2*ffn, hidden], router.proj +
8
+ per_expert_scale + scale, post_feedforward_layernorm_2 as the lambda scale),
9
+ applied selectively to a subset of layers listed in ``config.moe_layers``.
10
+ vLLM's gemma4.py already implements the full MoE path but only supports a
11
+ *global* ``enable_moe_block`` flag. This shim makes the flag per-layer --
12
+ the exact same trick as the 57-line HF-side shim.
13
+
14
+ Usage (pick one):
15
+
16
+ A. vLLM plugin entry point (recommended for a package):
17
+ [project.entry-points."vllm.general_plugins"]
18
+ solon_moe = "vllm_solon_moe:register"
19
+
20
+ B. Ad-hoc, no packaging:
21
+ VLLM_PLUGINS="" python -c "
22
+ import vllm_solon_moe; vllm_solon_moe.register()
23
+ from vllm import LLM; ..."
24
+ or put this file on PYTHONPATH and add to VLLM_PLUGINS mechanism.
25
+
26
+ Serving-time lambda override (no checkpoint copy needed):
27
+ SOLON_LAMBDA=0.10 vllm serve /path/to/solon-moe --trust-remote-code
28
+ The checkpoint stores the *training* lambda (0.15); inference is validated
29
+ at 0.10. If SOLON_LAMBDA is unset the checkpoint value is used as-is.
30
+
31
+ ADAPT notes (vLLM version drift):
32
+ - Constructor signatures of Gemma4DecoderLayer / Gemma4ForCausalLM vary
33
+ across vLLM versions (vllm_config vs individual args). The shim passes
34
+ through *args/**kwargs untouched, so it survives most drifts; if the
35
+ decoder-layer class is named differently, fix DECODER_CLS_NAME below.
36
+ - Verify once that vLLM's RMSNorm here is plain ``x*w`` (matching
37
+ transformers Gemma4), not the legacy Gemma ``x*(1+w)``:
38
+ grep -n "from.*layernorm import" .../models/gemma4.py
39
+ If it imports GemmaRMSNorm for these norms, lambda semantics break --
40
+ that must be patched before serving.
41
+ """
42
+ import os
43
+ import re
44
+
45
+ from vllm.model_executor.models import gemma4 as _g4
46
+
47
+ DECODER_CLS_NAME = "Gemma4DecoderLayer" # ADAPT if named differently
48
+ _LAYER_RE = re.compile(r"layers\.(\d+)")
49
+
50
+
51
+ def _make_selective_layer(base_cls):
52
+ class SolonMoEDecoderLayer(base_cls):
53
+ def __init__(self, *args, **kwargs):
54
+ # Locate config and prefix among the args regardless of signature.
55
+ config = kwargs.get("config")
56
+ prefix = kwargs.get("prefix", "")
57
+ if config is None:
58
+ for a in args:
59
+ if hasattr(a, "num_hidden_layers"):
60
+ config = a
61
+ break
62
+ if not prefix:
63
+ for a in args:
64
+ if isinstance(a, str) and "layers." in a:
65
+ prefix = a
66
+ break
67
+ m = _LAYER_RE.search(prefix or "")
68
+ layer_idx = int(m.group(1)) if m else -1
69
+
70
+ moe_layers = getattr(config, "moe_layers", None)
71
+ if moe_layers is None:
72
+ want = bool(getattr(config, "enable_moe_block", False))
73
+ else:
74
+ want = layer_idx in set(moe_layers)
75
+
76
+ saved = getattr(config, "enable_moe_block", False)
77
+ saved2 = getattr(config, "use_second_mlp_block", False)
78
+ config.enable_moe_block = bool(want)
79
+ if hasattr(config, "use_second_mlp_block"):
80
+ config.use_second_mlp_block = False # single source of truth
81
+ try:
82
+ super().__init__(*args, **kwargs)
83
+ finally:
84
+ config.enable_moe_block = saved
85
+ if hasattr(config, "use_second_mlp_block"):
86
+ config.use_second_mlp_block = saved2
87
+
88
+ return SolonMoEDecoderLayer
89
+
90
+
91
+ class SolonMoEForCausalLM(_g4.Gemma4ForCausalLM):
92
+ """Gemma4ForCausalLM with per-layer MoE selection + lambda override."""
93
+
94
+ def __init__(self, *, vllm_config, prefix: str = ""):
95
+ # NOTE: signature must be explicit new-style (*, vllm_config, prefix) —
96
+ # vLLM introspects it to choose the construction path. A generic
97
+ # (*args, **kwargs) passthrough makes vLLM fall back to old-style
98
+ # argument guessing, which then omits vllm_config. (Observed 2026-08.)
99
+ base = getattr(_g4, DECODER_CLS_NAME)
100
+ patched = _make_selective_layer(base)
101
+ setattr(_g4, DECODER_CLS_NAME, patched)
102
+ try:
103
+ super().__init__(vllm_config=vllm_config, prefix=prefix)
104
+ finally:
105
+ setattr(_g4, DECODER_CLS_NAME, base)
106
+
107
+ def load_weights(self, *args, **kwargs):
108
+ out = super().load_weights(*args, **kwargs)
109
+ self._apply_lambda_override()
110
+ return out
111
+
112
+ def _apply_lambda_override(self):
113
+ lam = os.environ.get("SOLON_LAMBDA")
114
+ if lam is None:
115
+ return
116
+ lam = float(lam)
117
+ n = 0
118
+ for name, module in self.named_modules():
119
+ if name.endswith("post_feedforward_layernorm_2") and hasattr(module, "weight"):
120
+ module.weight.data.fill_(lam)
121
+ n += 1
122
+ print(f"[solon_moe] SOLON_LAMBDA={lam} applied to {n} layer(s)"
123
+ + ("" if n else " -- WARNING: no lambda norms found (naming drift?)"))
124
+
125
+
126
+ def register():
127
+ from vllm import ModelRegistry
128
+ if "SolonMoEForCausalLM" not in ModelRegistry.get_supported_archs():
129
+ ModelRegistry.register_model(
130
+ "SolonMoEForCausalLM",
131
+ "vllm_solon_moe:SolonMoEForCausalLM",
132
+ )