A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 7, 16:30 UTC
[Model] Ring 2.5 (#35102)
Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
Jiangyun Zhu committed
Feb 26, 2026 at 18:17 UTC
ab87f85231b07b107f16ddb9e985deb0d83975ae
8 files changed
+1407
-70
docs/models/supported_models.md
+1
@@ -372,6 +372,7 @@ th {
372
| `BaiChuanForCausalLM` | Baichuan2, Baichuan | `baichuan-inc/Baichuan2-13B-Chat`, `baichuan-inc/Baichuan-7B`, etc. | ✅︎ | ✅︎ |
373
| `BailingMoeForCausalLM` | Ling | `inclusionAI/Ling-lite-1.5`, `inclusionAI/Ling-plus`, etc. | ✅︎ | ✅︎ |
374
| `BailingMoeV2ForCausalLM` | Ling | `inclusionAI/Ling-mini-2.0`, etc. | ✅︎ | ✅︎ |
375
+| `BailingMoeV2_5ForCausalLM` | Ling | `inclusionAI/Ling-2.5-1T`, `inclusionAI/Ring-2.5-1T` | | ✅︎ |
376
| `BambaForCausalLM` | Bamba | `ibm-ai-platform/Bamba-9B-fp8`, `ibm-ai-platform/Bamba-9B` | ✅︎ | ✅︎ |
377
| `BloomForCausalLM` | BLOOM, BLOOMZ, BLOOMChat | `bigscience/bloom`, `bigscience/bloomz`, etc. | | ✅︎ |
378
| `ChatGLMModel`, `ChatGLMForConditionalGeneration` | ChatGLM | `zai-org/chatglm2-6b`, `zai-org/chatglm3-6b`, `thu-coai/ShieldLM-6B-chatglm3`, etc. | ✅︎ | ✅︎ |
tests/models/registry.py
+3
@@ -206,6 +206,9 @@ _TEXT_GENERATION_EXAMPLE_MODELS = {
206
"BailingMoeV2ForCausalLM": _HfExamplesInfo(
207
"inclusionAI/Ling-mini-2.0", trust_remote_code=True
208
),
209
+ "BailingMoeV2_5ForCausalLM": _HfExamplesInfo(
210
+ "inclusionAI/Ring-2.5-1T", trust_remote_code=True
211
+ ),
212
"BambaForCausalLM": _HfExamplesInfo(
213
"ibm-ai-platform/Bamba-9B-v1",
214
extras={"tiny": "hmellor/tiny-random-BambaForCausalLM"},
vllm/model_executor/layers/fla/ops/layernorm_guard.py
+30
-5
@@ -84,6 +84,7 @@ def layer_norm_fwd_kernel(
84
HAS_Z: tl.constexpr,
85
NORM_BEFORE_GATE: tl.constexpr,
86
IS_RMS_NORM: tl.constexpr,
87
+ ACTIVATION: tl.constexpr,
88
):
89
# Map the program id to the starting row of X and Y it should compute.
90
row_start = tl.program_id(0) * ROWS_PER_BLOCK
@@ -112,7 +113,10 @@ def layer_norm_fwd_kernel(
113
if HAS_Z and not NORM_BEFORE_GATE:
114
Z_base = Z + rows[:, None] * stride_z_row + col_offsets
115
z = tl.load(Z_base, mask=mask, other=0.0).to(tl.float32)
115
- x *= z * tl.sigmoid(z)
116
+ if ACTIVATION == "swish" or ACTIVATION == "silu":
117
+ x *= z * tl.sigmoid(z)
118
+ elif ACTIVATION == "sigmoid":
119
+ x *= tl.sigmoid(z)
120
121
# Compute mean and variance per row (reduce along axis 1)
122
if not IS_RMS_NORM:
@@ -155,7 +159,10 @@ def layer_norm_fwd_kernel(
159
if HAS_Z and NORM_BEFORE_GATE:
160
Z_base = Z + rows[:, None] * stride_z_row + col_offsets
161
z = tl.load(Z_base, mask=mask, other=0.0).to(tl.float32)
158
- y *= z * tl.sigmoid(z)
162
+ if ACTIVATION == "swish" or ACTIVATION == "silu":
163
+ y *= z * tl.sigmoid(z)
164
+ elif ACTIVATION == "sigmoid":
165
+ y *= tl.sigmoid(z)
166
167
# Write output
168
tl.store(Y_base, y, mask=mask)
@@ -178,6 +185,7 @@ def layer_norm_fwd(
185
group_size: int = None,
186
norm_before_gate: bool = True,
187
is_rms_norm: bool = False,
188
+ activation: str = "swish",
189
):
190
M, N = x.shape
191
if group_size is None:
@@ -232,9 +240,12 @@ def layer_norm_fwd(
240
eps,
241
BLOCK_N=BLOCK_N,
242
ROWS_PER_BLOCK=rows_per_block,
243
+ HAS_BIAS=bias is not None,
244
+ HAS_Z=z is not None,
245
NORM_BEFORE_GATE=norm_before_gate,
246
IS_RMS_NORM=is_rms_norm,
247
num_warps=num_warps,
248
+ ACTIVATION=activation,
249
)
250
return out, mean, rstd
251
@@ -252,6 +263,7 @@ class LayerNormFn(torch.autograd.Function):
263
group_size=None,
264
norm_before_gate=True,
265
is_rms_norm=False,
266
+ activation: str = "swish",
267
):
268
"""If z is not None, we do norm(x) * silu(z) if norm_before_gate, else norm(x * silu(z))"""
269
@@ -277,6 +289,7 @@ class LayerNormFn(torch.autograd.Function):
289
group_size=group_size,
290
norm_before_gate=norm_before_gate,
291
is_rms_norm=is_rms_norm,
292
+ activation=activation,
293
)
294
ctx.save_for_backward(x, weight, bias, mean, rstd, z)
295
ctx.x_shape_og = x_shape_og
@@ -284,6 +297,7 @@ class LayerNormFn(torch.autograd.Function):
297
ctx.group_size = group_size
298
ctx.norm_before_gate = norm_before_gate
299
ctx.is_rms_norm = is_rms_norm
300
+ ctx.activation = activation
301
return y.reshape(x_shape_og)
302
303
@@ -296,17 +310,25 @@ def layernorm_fn(
310
group_size=None,
311
norm_before_gate=True,
312
is_rms_norm=False,
313
+ activation: str = "swish",
314
):
315
return LayerNormFn.apply(
301
- x, weight, bias, z, eps, group_size, norm_before_gate, is_rms_norm
316
+ x, weight, bias, z, eps, group_size, norm_before_gate, is_rms_norm, activation
317
)
318
319
320
def rmsnorm_fn(
306
- x, weight, bias, z=None, eps=1e-6, group_size=None, norm_before_gate=True
321
+ x,
322
+ weight,
323
+ bias,
324
+ z=None,
325
+ eps=1e-6,
326
+ group_size=None,
327
+ norm_before_gate=True,
328
+ activation: str = "swish",
329
):
330
return LayerNormFn.apply(
309
- x, weight, bias, z, eps, group_size, norm_before_gate, True
331
+ x, weight, bias, z, eps, group_size, norm_before_gate, True, activation
332
)
333
334
@@ -359,6 +381,7 @@ class RMSNormGated(nn.Module):
381
norm_before_gate: bool = False,
382
device: torch.device | None = None,
383
dtype: torch.dtype | None = None,
384
+ activation: str = "swish",
385
):
386
"""If group_size is not None, we do GroupNorm with each group having group_size elements.
387
group_size=None is equivalent to group_size=hidden_size (i.e. there's only 1 group).
@@ -366,6 +389,7 @@ class RMSNormGated(nn.Module):
389
factory_kwargs = {"device": device, "dtype": dtype}
390
super().__init__()
391
self.eps = eps
392
+ self.activation = activation
393
self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
394
self.register_parameter("bias", None)
395
self.group_size = group_size
@@ -385,4 +409,5 @@ class RMSNormGated(nn.Module):
409
eps=self.eps,
410
group_size=self.group_size,
411
norm_before_gate=self.norm_before_gate,
412
+ activation=self.activation,
413
)
vllm/model_executor/layers/layernorm.py
+1
@@ -592,6 +592,7 @@ class RMSNormGated(CustomOp):
592
eps=self.eps,
593
group_size=self.group_size,
594
norm_before_gate=self.norm_before_gate,
595
+ activation=self.activation,
596
)
597
598
vllm/model_executor/layers/mamba/linear_attn.py
+124
-65
@@ -2,6 +2,7 @@
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4
import math
5
+from collections.abc import Callable
6
7
import torch
8
import torch.nn.functional as F
@@ -43,7 +44,6 @@ class MiniMaxText01RMSNormTP(CustomOp):
44
45
self.weight.weight_loader = self.weight_loader
46
self.variance_epsilon = eps
46
- return
47
48
@staticmethod
49
def weight_loader(
@@ -56,7 +56,6 @@ class MiniMaxText01RMSNormTP(CustomOp):
56
shard_size = loaded_weight.shape[0] // tp_world
57
shard = slice(tp_rank * shard_size, (tp_rank + 1) * shard_size)
58
param.data.copy_(loaded_weight[shard])
59
- return
59
60
def _forward(
61
self,
@@ -102,6 +101,101 @@ class MiniMaxText01RMSNormTP(CustomOp):
101
return q, k
102
103
104
+def clear_linear_attention_cache_for_new_sequences(
105
+ kv_cache: torch.Tensor,
106
+ state_indices_tensor: torch.Tensor,
107
+ attn_metadata: LinearAttentionMetadata,
108
+) -> None:
109
+ num_prefills = getattr(attn_metadata, "num_prefills", 0)
110
+ if num_prefills <= 0:
111
+ return
112
+
113
+ num_decode_tokens = getattr(attn_metadata, "num_decode_tokens", 0)
114
+ for prefill_idx in range(num_prefills):
115
+ q_start = attn_metadata.query_start_loc[num_decode_tokens + prefill_idx]
116
+ q_end = attn_metadata.query_start_loc[num_decode_tokens + prefill_idx + 1]
117
+ query_len = q_end - q_start
118
+ context_len = (
119
+ attn_metadata.seq_lens[num_decode_tokens + prefill_idx] - query_len
120
+ )
121
+ if context_len == 0:
122
+ block_to_clear = state_indices_tensor[num_decode_tokens + prefill_idx]
123
+ kv_cache[block_to_clear, ...] = 0
124
+
125
+
126
+def linear_attention_decode(
127
+ q: torch.Tensor,
128
+ k: torch.Tensor,
129
+ v: torch.Tensor,
130
+ kv_cache: torch.Tensor,
131
+ slope_rate: torch.Tensor,
132
+ state_indices_tensor: torch.Tensor,
133
+ q_start: int = 0,
134
+ q_end: int | None = None,
135
+ slot_start: int = 0,
136
+ slot_end: int | None = None,
137
+ block_size: int = 32,
138
+) -> torch.Tensor:
139
+ q = q[q_start:q_end].unsqueeze(2).contiguous()
140
+ k = k[q_start:q_end].unsqueeze(2).contiguous()
141
+ v = v[q_start:q_end].unsqueeze(2).contiguous()
142
+ slot_id = state_indices_tensor[slot_start:slot_end]
143
+ return linear_decode_forward_triton(
144
+ q, k, v, kv_cache, slope_rate, slot_id, block_size
145
+ )
146
+
147
+
148
+def linear_attention_prefill_and_mix(
149
+ q: torch.Tensor,
150
+ k: torch.Tensor,
151
+ v: torch.Tensor,
152
+ kv_cache: torch.Tensor,
153
+ state_indices_tensor: torch.Tensor,
154
+ attn_metadata: LinearAttentionMetadata,
155
+ slope_rate: torch.Tensor,
156
+ block_size: int,
157
+ decode_fn: Callable[..., torch.Tensor],
158
+ prefix_fn: Callable[..., torch.Tensor],
159
+ layer_idx: int | None = None,
160
+) -> torch.Tensor:
161
+ hidden = []
162
+ for _prefill_idx in range(getattr(attn_metadata, "num_prefills", 0)):
163
+ if _prefill_idx >= len(attn_metadata.query_start_loc):
164
+ break
165
+ if _prefill_idx >= len(state_indices_tensor):
166
+ break
167
+ offset = attn_metadata.num_decode_tokens
168
+ _start = attn_metadata.query_start_loc[offset + _prefill_idx]
169
+ _end = attn_metadata.query_start_loc[offset + _prefill_idx + 1]
170
+ slot_id = state_indices_tensor[offset + _prefill_idx]
171
+ qs = q[_start:_end].transpose(0, 1).contiguous()
172
+ ks = k[_start:_end].transpose(0, 1).contiguous()
173
+ vs = v[_start:_end].transpose(0, 1).contiguous()
174
+ slice_layer_cache = kv_cache[slot_id, ...]
175
+ out_slice = prefix_fn(
176
+ qs,
177
+ ks,
178
+ vs,
179
+ slice_layer_cache,
180
+ slope_rate,
181
+ block_size,
182
+ layer_idx=layer_idx,
183
+ )
184
+ hidden.append(out_slice.contiguous())
185
+
186
+ if attn_metadata.num_decode_tokens > 0:
187
+ hidden_decode = decode_fn(
188
+ q, k, v, kv_cache, state_indices_tensor, attn_metadata
189
+ )
190
+ hidden.insert(0, hidden_decode)
191
+
192
+ if not hidden:
193
+ return torch.empty((0, q.size(-1)), device=q.device, dtype=q.dtype)
194
+
195
+ hidden = torch.concat(hidden, dim=0).contiguous()
196
+ return hidden
197
+
198
+
199
class MiniMaxText01LinearKernel:
200
@staticmethod
201
def jit_linear_forward_prefix(
@@ -258,50 +352,33 @@ class MiniMaxText01LinearAttention(nn.Module, MambaBase):
352
def _prefill_and_mix_infer(
353
self, q, k, v, kv_cache, state_indices_tensor, attn_metadata
354
):
261
- hidden = []
262
- for _prefill_idx in range(getattr(attn_metadata, "num_prefills", 0)):
263
- if _prefill_idx >= len(attn_metadata.query_start_loc):
264
- break
265
- if _prefill_idx >= len(state_indices_tensor):
266
- break
267
- offset = attn_metadata.num_decode_tokens
268
- _start = attn_metadata.query_start_loc[offset + _prefill_idx]
269
- _end = attn_metadata.query_start_loc[offset + _prefill_idx + 1]
270
- slot_id = state_indices_tensor[offset + _prefill_idx]
271
- qs = q[_start:_end].transpose(0, 1).contiguous()
272
- ks = k[_start:_end].transpose(0, 1).contiguous()
273
- vs = v[_start:_end].transpose(0, 1).contiguous()
274
- slice_layer_cache = kv_cache[slot_id, ...]
275
-
276
- out_slice = MiniMaxText01LinearKernel.jit_linear_forward_prefix(
277
- qs,
278
- ks,
279
- vs,
280
- slice_layer_cache,
281
- self.tp_slope,
282
- self.BLOCK,
283
- layer_idx=self.layer_idx,
284
- )
285
- hidden.append(out_slice.contiguous())
286
- if attn_metadata.num_decode_tokens > 0:
287
- hidden_decode = self._decode_infer(
288
- q, k, v, kv_cache, state_indices_tensor, attn_metadata
289
- )
290
- hidden.insert(0, hidden_decode)
291
-
292
- if not hidden:
293
- return torch.empty((0, q.size(-1)), device=q.device, dtype=q.dtype)
294
-
295
- hidden = torch.concat(hidden, dim=0).contiguous()
296
- return hidden
355
+ return linear_attention_prefill_and_mix(
356
+ q=q,
357
+ k=k,
358
+ v=v,
359
+ kv_cache=kv_cache,
360
+ state_indices_tensor=state_indices_tensor,
361
+ attn_metadata=attn_metadata,
362
+ slope_rate=self.tp_slope,
363
+ block_size=self.BLOCK,
364
+ decode_fn=self._decode_infer,
365
+ prefix_fn=MiniMaxText01LinearKernel.jit_linear_forward_prefix,
366
+ layer_idx=self.layer_idx,
367
+ )
368
369
def _decode_infer(self, q, k, v, kv_cache, state_indices_tensor, attn_metadata):
299
- q = q[: attn_metadata.num_decode_tokens].unsqueeze(2).contiguous()
300
- k = k[: attn_metadata.num_decode_tokens].unsqueeze(2).contiguous()
301
- v = v[: attn_metadata.num_decode_tokens].unsqueeze(2).contiguous()
302
- slot_id = state_indices_tensor[: attn_metadata.num_decodes]
303
- hidden = linear_decode_forward_triton(
304
- q, k, v, kv_cache, self.tp_slope, slot_id, 32
370
+ hidden = linear_attention_decode(
371
+ q,
372
+ k,
373
+ v,
374
+ kv_cache,
375
+ self.tp_slope,
376
+ state_indices_tensor,
377
+ q_start=0,
378
+ q_end=attn_metadata.num_decode_tokens,
379
+ slot_start=0,
380
+ slot_end=attn_metadata.num_decodes,
381
+ block_size=32,
382
)
383
return hidden
384
@@ -338,27 +415,9 @@ class MiniMaxText01LinearAttention(nn.Module, MambaBase):
415
if attn_metadata is not None:
416
kv_cache = self.kv_cache[forward_context.virtual_engine][0]
417
state_indices_tensor = attn_metadata.state_indices_tensor
341
-
342
- num_prefills = getattr(attn_metadata, "num_prefills", 0)
343
- if num_prefills > 0:
344
- num_decode_tokens = getattr(attn_metadata, "num_decode_tokens", 0)
345
- for prefill_idx in range(num_prefills):
346
- q_start = attn_metadata.query_start_loc[
347
- num_decode_tokens + prefill_idx
348
- ]
349
- q_end = attn_metadata.query_start_loc[
350
- num_decode_tokens + prefill_idx + 1
351
- ]
352
- query_len = q_end - q_start
353
- context_len = (
354
- attn_metadata.seq_lens[num_decode_tokens + prefill_idx]
355
- - query_len
356
- )
357
- if context_len == 0:
358
- block_to_clear = state_indices_tensor[
359
- num_decode_tokens + prefill_idx
360
- ]
361
- kv_cache[block_to_clear, ...] = 0
418
+ clear_linear_attention_cache_for_new_sequences(
419
+ kv_cache, state_indices_tensor, attn_metadata
420
+ )
421
422
decode_only = getattr(attn_metadata, "num_prefills", 0) == 0
423
if attn_metadata is None:
vllm/model_executor/models/bailing_moe_linear.py
new
+1246
@@ -0,0 +1,1246 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+import copy
4
+from collections.abc import Iterable
5
+
6
+import torch
7
+import torch.nn as nn
8
+import torch.nn.functional as F
9
+from transformers.configuration_utils import PretrainedConfig
10
+
11
+from vllm.compilation.decorators import support_torch_compile
12
+from vllm.config import CacheConfig, ModelConfig, VllmConfig, get_current_vllm_config
13
+from vllm.distributed import (
14
+ get_pp_group,
15
+ get_tensor_model_parallel_rank,
16
+ get_tensor_model_parallel_world_size,
17
+)
18
+from vllm.forward_context import get_forward_context
19
+from vllm.logger import init_logger
20
+from vllm.model_executor.layers.fla.ops.layernorm_guard import (
21
+ RMSNormGated,
22
+ layernorm_fn,
23
+)
24
+from vllm.model_executor.layers.fused_moe import FusedMoE, SharedFusedMoE
25
+from vllm.model_executor.layers.layernorm import RMSNorm
26
+from vllm.model_executor.layers.linear import (
27
+ ColumnParallelLinear,
28
+ MergedColumnParallelLinear,
29
+ QKVParallelLinear,
30
+ ReplicatedLinear,
31
+ RowParallelLinear,
32
+)
33
+from vllm.model_executor.layers.logits_processor import LogitsProcessor
34
+from vllm.model_executor.layers.mamba.abstract import MambaBase
35
+from vllm.model_executor.layers.mamba.linear_attn import (
36
+ MiniMaxText01LinearAttention,
37
+ MiniMaxText01LinearKernel,
38
+ MiniMaxText01RMSNormTP,
39
+ clear_linear_attention_cache_for_new_sequences,
40
+ linear_attention_decode,
41
+ linear_attention_prefill_and_mix,
42
+)
43
+from vllm.model_executor.layers.mamba.mamba_utils import (
44
+ MambaStateCopyFuncCalculator,
45
+ MambaStateDtypeCalculator,
46
+ MambaStateShapeCalculator,
47
+)
48
+from vllm.model_executor.layers.mla import MLAModules, MultiHeadLatentAttentionWrapper
49
+from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
50
+from vllm.model_executor.layers.rotary_embedding import get_rope
51
+from vllm.model_executor.layers.vocab_parallel_embedding import (
52
+ ParallelLMHead,
53
+ VocabParallelEmbedding,
54
+)
55
+from vllm.model_executor.model_loader.weight_utils import (
56
+ default_weight_loader,
57
+ maybe_remap_kv_scale_name,
58
+)
59
+from vllm.model_executor.models.bailing_moe import BailingMLP
60
+from vllm.sequence import IntermediateTensors
61
+from vllm.v1.attention.backend import AttentionMetadata
62
+from vllm.v1.attention.backends.linear_attn import LinearAttentionMetadata
63
+
64
+from .interfaces import HasInnerState, IsHybrid, SupportsPP
65
+from .utils import (
66
+ AutoWeightsLoader,
67
+ PPMissingLayer,
68
+ is_pp_missing_parameter,
69
+ make_layers,
70
+ maybe_prefix,
71
+)
72
+
73
+logger = init_logger(__name__)
74
+
75
+
76
+def is_linear_layer(layer_idx, layer_group_size):
77
+ if layer_idx is None:
78
+ return False
79
+ if layer_group_size > 0:
80
+ return (layer_idx + 1) % layer_group_size != 0
81
+ else:
82
+ return False
83
+
84
+
85
+def _build_rope_parameters(config: PretrainedConfig) -> dict | None:
86
+ rope_parameters = copy.deepcopy(getattr(config, "rope_parameters", None)) or {}
87
+ if "rope_theta" not in rope_parameters and hasattr(config, "rope_theta"):
88
+ rope_parameters["rope_theta"] = config.rope_theta
89
+ if "partial_rotary_factor" not in rope_parameters and hasattr(
90
+ config, "partial_rotary_factor"
91
+ ):
92
+ rope_parameters["partial_rotary_factor"] = config.partial_rotary_factor
93
+
94
+ rope_scaling = getattr(config, "rope_scaling", None)
95
+ if isinstance(rope_scaling, dict):
96
+ rope_scaling = copy.deepcopy(rope_scaling)
97
+ if "type" in rope_scaling and "rope_type" not in rope_scaling:
98
+ rope_scaling["rope_type"] = rope_scaling.pop("type")
99
+ rope_parameters.update(rope_scaling)
100
+
101
+ return rope_parameters or None
102
+
103
+
104
+class BailingMoeV25MLAAttention(nn.Module):
105
+ """
106
+ MLA Attention for BailingMoeV2.5 full attention layers.
107
+ """
108
+
109
+ def __init__(
110
+ self,
111
+ config: PretrainedConfig,
112
+ quant_config: QuantizationConfig | None = None,
113
+ layer_id: int = 0,
114
+ prefix: str = "attention",
115
+ cache_config: CacheConfig | None = None,
116
+ ) -> None:
117
+ super().__init__()
118
+ self.hidden_size = config.hidden_size
119
+ self.num_heads = config.num_attention_heads
120
+ self.layer_id = layer_id
121
+ self.prefix = prefix
122
+
123
+ # MLA dimensions
124
+ self.qk_nope_head_dim = getattr(config, "qk_nope_head_dim", 128)
125
+ self.qk_rope_head_dim = getattr(config, "qk_rope_head_dim", 64)
126
+ self.qk_head_dim = self.qk_nope_head_dim + self.qk_rope_head_dim
127
+ self.v_head_dim = getattr(config, "v_head_dim", 128)
128
+
129
+ # LoRA ranks
130
+ self.q_lora_rank = getattr(config, "q_lora_rank", None)
131
+ self.kv_lora_rank = getattr(config, "kv_lora_rank", 512)
132
+
133
+ tp_size = get_tensor_model_parallel_world_size()
134
+ assert self.num_heads % tp_size == 0
135
+ self.num_local_heads = self.num_heads // tp_size
136
+
137
+ self.scaling = self.qk_head_dim**-0.5
138
+
139
+ # KV projections
140
+ self.kv_a_layernorm = RMSNorm(
141
+ self.kv_lora_rank,
142
+ eps=config.rms_norm_eps,
143
+ )
144
+ self.kv_b_proj = ColumnParallelLinear(
145
+ self.kv_lora_rank,
146
+ self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
147
+ bias=False,
148
+ quant_config=quant_config,
149
+ prefix=f"{prefix}.kv_b_proj",
150
+ )
151
+
152
+ # Output projection
153
+ self.o_proj = RowParallelLinear(
154
+ self.num_heads * self.v_head_dim,
155
+ self.hidden_size,
156
+ bias=False,
157
+ quant_config=quant_config,
158
+ prefix=f"{prefix}.o_proj",
159
+ )
160
+
161
+ if self.q_lora_rank is not None:
162
+ # Use fused_qkv_a_proj when q_lora_rank is set
163
+ self.fused_qkv_a_proj = MergedColumnParallelLinear(
164
+ self.hidden_size,
165
+ [self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
166
+ bias=False,
167
+ quant_config=quant_config,
168
+ prefix=f"{prefix}.fused_qkv_a_proj",
169
+ disable_tp=True,
170
+ )
171
+ self.q_a_layernorm = RMSNorm(
172
+ self.q_lora_rank,
173
+ eps=config.rms_norm_eps,
174
+ )
175
+ self.q_b_proj = ColumnParallelLinear(
176
+ self.q_lora_rank,
177
+ self.num_heads * self.qk_head_dim,
178
+ bias=False,
179
+ quant_config=quant_config,
180
+ prefix=f"{prefix}.q_b_proj",
181
+ )
182
+ self.q_proj = None
183
+ self.kv_a_proj_with_mqa = None
184
+ else:
185
+ # Direct projections when no q_lora_rank
186
+ self.q_proj = ColumnParallelLinear(
187
+ self.hidden_size,
188
+ self.num_heads * self.qk_head_dim,
189
+ bias=False,
190
+ quant_config=quant_config,
191
+ prefix=f"{prefix}.q_proj",
192
+ )
193
+ self.kv_a_proj_with_mqa = ReplicatedLinear(
194
+ self.hidden_size,
195
+ self.kv_lora_rank + self.qk_rope_head_dim,
196
+ bias=False,
197
+ quant_config=quant_config,
198
+ prefix=f"{prefix}.kv_a_proj_with_mqa",
199
+ )
200
+ self.fused_qkv_a_proj = None
201
+ self.q_a_layernorm = None
202
+ self.q_b_proj = None
203
+
204
+ rope_parameters = _build_rope_parameters(config)
205
+ max_position = getattr(config, "max_position_embeddings", 8192)
206
+ self.rotary_emb = get_rope(
207
+ head_size=self.qk_rope_head_dim,
208
+ max_position=max_position,
209
+ is_neox_style=False,
210
+ rope_parameters=rope_parameters or None,
211
+ dtype=torch.float32,
212
+ )
213
+
214
+ # Build MLAModules for MultiHeadLatentAttentionWrapper
215
+ mla_modules = MLAModules(
216
+ kv_a_layernorm=self.kv_a_layernorm,
217
+ kv_b_proj=self.kv_b_proj,
218
+ rotary_emb=self.rotary_emb,
219
+ o_proj=self.o_proj,
220
+ fused_qkv_a_proj=self.fused_qkv_a_proj,
221
+ kv_a_proj_with_mqa=self.kv_a_proj_with_mqa,
222
+ q_a_layernorm=self.q_a_layernorm,
223
+ q_b_proj=self.q_b_proj,
224
+ q_proj=self.q_proj,
225
+ indexer=None,
226
+ is_sparse=False,
227
+ topk_indices_buffer=None,
228
+ )
229
+
230
+ self.mla_attn = MultiHeadLatentAttentionWrapper(
231
+ self.hidden_size,
232
+ self.num_local_heads,
233
+ self.scaling,
234
+ self.qk_nope_head_dim,
235
+ self.qk_rope_head_dim,
236
+ self.v_head_dim,
237
+ self.q_lora_rank,
238
+ self.kv_lora_rank,
239
+ mla_modules,
240
+ cache_config,
241
+ quant_config,
242
+ prefix,
243
+ )
244
+
245
+ def forward(
246
+ self,
247
+ hidden_states: torch.Tensor,
248
+ positions: torch.Tensor,
249
+ ) -> torch.Tensor:
250
+ """Forward pass for MLA attention."""
251
+ return self.mla_attn(positions, hidden_states)
252
+
253
+
254
+class BailingMoEGate(nn.Module):
255
+ def __init__(
256
+ self,
257
+ config: PretrainedConfig,
258
+ params_dtype: torch.dtype | None = None,
259
+ prefix: str = "",
260
+ ):
261
+ super().__init__()
262
+ if params_dtype is None:
263
+ params_dtype = torch.get_default_dtype()
264
+ self.params_dtype = params_dtype
265
+ self.weight = nn.Parameter(
266
+ torch.empty(
267
+ (config.num_experts, config.hidden_size),
268
+ dtype=self.params_dtype,
269
+ ),
270
+ )
271
+ if getattr(config, "moe_router_enable_expert_bias", False):
272
+ self.expert_bias = nn.Parameter(
273
+ torch.empty((config.num_experts,), dtype=torch.float32),
274
+ )
275
+ else:
276
+ self.expert_bias = None
277
+
278
+ def forward(self, hidden_states):
279
+ logits = F.linear(hidden_states.to(self.weight.dtype), self.weight, None).to(
280
+ hidden_states.dtype
281
+ )
282
+ return logits
283
+
284
+
285
+class BailingMoeV25(nn.Module):
286
+ """Bailing MoE v2.5 - standalone implementation for linear attention model."""
287
+
288
+ def __init__(
289
+ self,
290
+ config: PretrainedConfig,
291
+ quant_config: QuantizationConfig | None = None,
292
+ layer_id: int = 0,
293
+ prefix: str = "",
294
+ ):
295
+ super().__init__()
296
+
297
+ self.layer_id = layer_id
298
+ self.tp_size = get_tensor_model_parallel_world_size()
299
+ self.tp_rank = get_tensor_model_parallel_rank()
300
+ self.num_experts = config.num_experts
301
+ self.top_k = config.num_experts_per_tok
302
+ norm_topk_prob = getattr(config, "norm_topk_prob", None)
303
+ # Ring-2.5 reference implementations normalize routing weights by default.
304
+ self.norm_expert_prob = True if norm_topk_prob is None else bool(norm_topk_prob)
305
+ self.hidden_size = config.hidden_size
306
+ self.quant_config = quant_config
307
+ self.num_shared_experts = config.num_shared_experts
308
+ self.score_function = getattr(config, "score_function", None)
309
+ self.n_group = getattr(config, "n_group", None)
310
+ self.topk_group = getattr(config, "topk_group", None)
311
+ self.use_grouped_topk = self.n_group is not None and self.topk_group is not None
312
+ self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
313
+
314
+ router_dtype = getattr(config, "router_dtype", None)
315
+ if router_dtype is None or router_dtype == "fp32":
316
+ self.router_dtype = torch.float32
317
+ else:
318
+ self.router_dtype = torch.bfloat16
319
+
320
+ # Gate for routing
321
+ self.gate = BailingMoEGate(
322
+ config=config,
323
+ params_dtype=self.router_dtype,
324
+ prefix=f"{prefix}.gate",
325
+ )
326
+ correction_bias = (
327
+ self.gate.expert_bias if self.gate.expert_bias is not None else None
328
+ )
329
+ if self.score_function is not None:
330
+ assert (self.score_function == "softmax" and correction_bias is None) or (
331
+ self.score_function == "sigmoid" and correction_bias is not None
332
+ ), (
333
+ "score_function and correction_bias should be "
334
+ "(softmax, None) or (sigmoid, not None)"
335
+ )
336
+
337
+ # Shared experts (using BailingMLP)
338
+ if self.num_shared_experts > 0:
339
+ if hasattr(config, "moe_shared_expert_intermediate_size"):
340
+ intermediate_size = config.moe_shared_expert_intermediate_size
341
+ else:
342
+ intermediate_size = config.moe_intermediate_size
343
+ intermediate_size *= config.num_shared_experts
344
+ self.shared_experts = BailingMLP(
345
+ intermediate_size=intermediate_size,
346
+ config=config,
347
+ quant_config=quant_config,
348
+ reduce_results=False,
349
+ prefix=f"{prefix}.shared_experts",
350
+ )
351
+ else:
352
+ self.shared_experts = None
353
+
354
+ # Routed experts using SharedFusedMoE
355
+ self.experts = SharedFusedMoE(
356
+ shared_experts=self.shared_experts,
357
+ num_experts=self.num_experts,
358
+ top_k=self.top_k,
359
+ hidden_size=self.hidden_size,
360
+ intermediate_size=config.moe_intermediate_size,
361
+ reduce_results=False,
362
+ renormalize=self.norm_expert_prob,
363
+ quant_config=quant_config,
364
+ prefix=f"{prefix}.experts",
365
+ scoring_func=self.score_function,
366
+ e_score_correction_bias=correction_bias,
367
+ num_expert_group=self.n_group,
368
+ topk_group=self.topk_group,
369
+ use_grouped_topk=self.use_grouped_topk,
370
+ router_logits_dtype=self.router_dtype,
371
+ )
372
+
373
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
374
+ num_tokens, hidden_size = hidden_states.shape
375
+ # Ensure contiguous token-major layout before router/projections.
376
+ hidden_states = hidden_states.contiguous().view(-1, hidden_size)
377
+
378
+ # router_logits: (num_tokens, n_experts)
379
+ router_logits = self.gate(hidden_states.to(self.router_dtype))
380
+ router_logits = router_logits.to(hidden_states.dtype)
381
+
382
+ final_hidden_states = self.experts(
383
+ hidden_states=hidden_states, router_logits=router_logits
384
+ )
385
+
386
+ # Handle tuple return from SharedFusedMoE
387
+ if self.shared_experts is not None:
388
+ shared_output, final_hidden_states = final_hidden_states
389
+ else:
390
+ shared_output = None
391
+
392
+ final_hidden_states *= self.routed_scaling_factor
393
+
394
+ if shared_output is not None:
395
+ final_hidden_states = final_hidden_states + shared_output
396
+
397
+ if self.tp_size > 1:
398
+ final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
399
+ final_hidden_states
400
+ )
401
+
402
+ return final_hidden_states.view(num_tokens, hidden_size)
403
+
404
+
405
+BailingRMSNormTP = MiniMaxText01RMSNormTP
406
+
407
+
408
+class BailingGroupRMSNormGate(RMSNormGated):
409
+ def __init__(
410
+ self,
411
+ hidden_size,
412
+ eps=1e-5,
413
+ group_size=None,
414
+ norm_before_gate=True,
415
+ device=None,
416
+ dtype=None,
417
+ ):
418
+ super().__init__(
419
+ hidden_size,
420
+ eps=eps,
421
+ group_size=group_size,
422
+ norm_before_gate=norm_before_gate,
423
+ device=device,
424
+ dtype=dtype,
425
+ activation="sigmoid",
426
+ )
427
+ # Add custom weight loader for TP sharding
428
+ self.weight.weight_loader = self._weight_loader
429
+
430
+ @staticmethod
431
+ def _weight_loader(param: torch.nn.Parameter, loaded_weight: torch.Tensor) -> None:
432
+ """Load weight with TP sharding."""
433
+ tp_size = get_tensor_model_parallel_world_size()
434
+ tp_rank = get_tensor_model_parallel_rank()
435
+ shard_size = loaded_weight.shape[0] // tp_size
436
+ shard = slice(tp_rank * shard_size, (tp_rank + 1) * shard_size)
437
+ param.data.copy_(loaded_weight[shard].contiguous())
438
+
439
+
440
+class BailingMoELinearAttention(nn.Module, MambaBase):
441
+ """
442
+ Bailing MoE Linear Attention implementation using minimax backend.
443
+
444
+ This implements the linear attention mechanism from sglang, adapted for vLLM's
445
+ v1 engine with MambaBase interface support.
446
+ """
447
+
448
+ @property
449
+ def mamba_type(self) -> str:
450
+ return "linear_attention"
451
+
452
+ def get_state_shape(self) -> tuple[tuple[int, ...], ...]:
453
+ """Return state shape for linear attention cache.
454
+
455
+ Must match the calculation in get_mamba_state_shape_from_config.
456
+ """
457
+ return MambaStateShapeCalculator.linear_attention_state_shape(
458
+ num_heads=self.total_num_heads,
459
+ tp_size=self.tp_size,
460
+ head_dim=self.head_dim,
461
+ )
462
+
463
+ def get_state_dtype(self) -> tuple[torch.dtype, ...]:
464
+ """Return state dtype for linear attention cache.
465
+
466
+ Must match the calculation in get_mamba_state_dtype_from_config.
467
+ """
468
+ return MambaStateDtypeCalculator.linear_attention_state_dtype(
469
+ self.model_config.dtype,
470
+ self.cache_config.mamba_cache_dtype,
471
+ )
472
+
473
+ def __init__(
474
+ self,
475
+ config: PretrainedConfig,
476
+ quant_config: QuantizationConfig | None = None,
477
+ layer_id: int = 0,
478
+ prefix: str = "linear_attn",
479
+ model_config: ModelConfig | None = None,
480
+ cache_config: CacheConfig | None = None,
481
+ ):
482
+ super().__init__()
483
+
484
+ self.layer_id = layer_id
485
+ self.hidden_size = config.hidden_size
486
+ self.total_num_heads = config.num_attention_heads
487
+ self.total_kv_heads = config.num_attention_heads # MHA
488
+ self.tp_size = get_tensor_model_parallel_world_size()
489
+ self.tp_rank = get_tensor_model_parallel_rank()
490
+ self.model_config = model_config
491
+ self.cache_config = cache_config
492
+ self.prefix = prefix
493
+
494
+ self.head_dim = (
495
+ config.head_dim
496
+ if hasattr(config, "head_dim")
497
+ else config.hidden_size // self.total_num_heads
498
+ )
499
+
500
+ self.hidden_inner_size = self.head_dim * self.total_num_heads
501
+ self.scaling = self.head_dim**-0.5
502
+
503
+ assert self.total_num_heads % self.tp_size == 0
504
+ self.tp_heads = self.total_num_heads // self.tp_size
505
+
506
+ self.max_position_embeddings = config.max_position_embeddings
507
+ self.rope_theta = getattr(config, "rope_theta", 600000)
508
+
509
+ self.tp_kv_heads = self.total_kv_heads // self.tp_size
510
+ self.q_size_per_rank = self.head_dim * self.tp_heads
511
+ self.kv_size_per_rank = self.head_dim * self.tp_kv_heads
512
+
513
+ self.use_qk_norm = getattr(config, "use_qk_norm", False)
514
+ self.linear_backend = "minimax"
515
+ self.linear_scale = self.linear_backend == "minimax"
516
+ self.linear_rope = getattr(config, "linear_rope", True)
517
+ if hasattr(config, "use_linear_silu"):
518
+ self.linear_silu = config.use_linear_silu
519
+ elif hasattr(config, "linear_silu"):
520
+ self.linear_silu = config.linear_silu
521
+ else:
522
+ self.linear_silu = False
523
+
524
+ # Block size for lightning attention
525
+ self.BLOCK = getattr(config, "block", 256)
526
+
527
+ self.query_key_value = QKVParallelLinear(
528
+ self.hidden_size,
529
+ self.head_dim,
530
+ self.total_num_heads,
531
+ self.total_num_heads, # MHA: kv_heads = num_heads
532
+ bias=(config.use_bias or config.use_qkv_bias),
533
+ quant_config=quant_config,
534
+ prefix=f"{prefix}.query_key_value",
535
+ )
536
+
537
+ if self.use_qk_norm:
538
+ self.query_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
539
+ self.key_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
540
+
541
+ self.g_proj = ColumnParallelLinear(
542
+ self.hidden_size,
543
+ self.hidden_inner_size,
544
+ bias=False,
545
+ quant_config=quant_config,
546
+ prefix=f"{prefix}.g_proj",
547
+ )
548
+ self.dense = RowParallelLinear(
549
+ self.hidden_inner_size,
550
+ self.hidden_size,
551
+ bias=config.use_bias,
552
+ quant_config=quant_config,
553
+ prefix=f"{prefix}.dense",
554
+ reduce_results=True,
555
+ )
556
+
557
+ self.group_norm_size = getattr(config, "group_norm_size", 1)
558
+ self.rms_norm_eps = float(getattr(config, "rms_norm_eps", 1e-5))
559
+ assert self.tp_size <= self.group_norm_size, (
560
+ "tp_size must be <= group_norm_size for local rms norm"
561
+ )
562
+ assert self.group_norm_size % self.tp_size == 0, (
563
+ "group_norm_size must be divisible by tp_size"
564
+ )
565
+
566
+ # When group_norm_size == 1, group_size equals hidden_size // tp_size
567
+ self.g_norm = BailingGroupRMSNormGate(
568
+ hidden_size=self.hidden_inner_size // self.tp_size,
569
+ eps=self.rms_norm_eps,
570
+ group_size=(
571
+ self.hidden_inner_size // self.group_norm_size
572
+ if self.group_norm_size > 1
573
+ else self.hidden_inner_size // self.tp_size
574
+ ),
575
+ )
576
+
577
+ # use fp32 rotary embedding
578
+ rope_parameters = _build_rope_parameters(config)
579
+
580
+ self.rotary_emb = get_rope(
581
+ self.head_dim,
582
+ max_position=self.max_position_embeddings,
583
+ is_neox_style=True,
584
+ dtype=torch.float32,
585
+ rope_parameters=rope_parameters or None,
586
+ )
587
+
588
+ # Build slope tensor for linear attention decay
589
+ num_hidden_layers = config.num_hidden_layers
590
+ slope_rate = MiniMaxText01LinearAttention._build_slope_tensor(
591
+ self.total_num_heads
592
+ )
593
+ if num_hidden_layers <= 1:
594
+ self.slope_rate = slope_rate * (1 + 1e-5)
595
+ else:
596
+ self.slope_rate = slope_rate * (
597
+ 1 - layer_id / (num_hidden_layers - 1) + 1e-5
598
+ )
599
+ self.tp_slope = self.slope_rate[
600
+ self.tp_rank * self.tp_heads : (self.tp_rank + 1) * self.tp_heads
601
+ ].contiguous()
602
+
603
+ # Register for compilation
604
+ compilation_config = get_current_vllm_config().compilation_config
605
+ if prefix in compilation_config.static_forward_context:
606
+ raise ValueError(f"Duplicate layer name: {prefix}")
607
+ compilation_config.static_forward_context[prefix] = self
608
+
609
+ @staticmethod
610
+ def weight_direct_load(param: torch.Tensor, loaded_weight: torch.Tensor) -> None:
611
+ """Load weight for linear attention layers.
612
+
613
+ For FP8 quantized parameters, we need to use the weight_loader if available,
614
+ as it handles special cases like tensor parallelism sharding.
615
+ """
616
+ # Check if param has a weight_loader (for vLLM ModelWeightParameter)
617
+ weight_loader = getattr(param, "weight_loader", None)
618
+ if weight_loader is not None:
619
+ # Use the weight_loader which handles TP sharding and quantization
620
+ weight_loader(param, loaded_weight)
621
+ else:
622
+ # Fall back to direct copy for standard tensors
623
+ assert param.size() == loaded_weight.size(), (
624
+ f"Shape mismatch: {param.shape} vs {loaded_weight.shape}"
625
+ )
626
+ param.data.copy_(loaded_weight)
627
+
628
+ def forward(
629
+ self,
630
+ hidden_states: torch.Tensor,
631
+ output: torch.Tensor,
632
+ positions: torch.Tensor,
633
+ ) -> None:
634
+ """Forward method called by torch.ops.vllm.linear_attention"""
635
+ torch.ops.vllm.linear_attention(
636
+ hidden_states,
637
+ output,
638
+ positions,
639
+ self.prefix,
640
+ )
641
+
642
+ def _forward(
643
+ self,
644
+ hidden_states: torch.Tensor,
645
+ output: torch.Tensor,
646
+ positions: torch.Tensor,
647
+ ) -> None:
648
+ """Actual forward implementation."""
649
+ forward_context = get_forward_context()
650
+ attn_metadata: AttentionMetadata = forward_context.attn_metadata
651
+ if attn_metadata is not None:
652
+ assert isinstance(attn_metadata, dict)
653
+ attn_metadata = attn_metadata[self.prefix]
654
+ assert isinstance(attn_metadata, LinearAttentionMetadata)
655
+ num_actual_tokens = (
656
+ attn_metadata.num_prefill_tokens + attn_metadata.num_decode_tokens
657
+ )
658
+ else:
659
+ num_actual_tokens = hidden_states.shape[0]
660
+
661
+ # QKV projection
662
+ qkv, _ = self.query_key_value(hidden_states[:num_actual_tokens])
663
+
664
+ # use rotary_emb support fp32
665
+ qkv = qkv.to(torch.float32)
666
+ if self.linear_silu:
667
+ qkv = F.silu(qkv)
668
+
669
+ # Split q, k, v
670
+ q, k, v = torch.split(
671
+ qkv,
672
+ [self.q_size_per_rank, self.kv_size_per_rank, self.kv_size_per_rank],
673
+ dim=-1,
674
+ )
675
+
676
+ # Apply QK norm if needed
677
+ if self.use_qk_norm:
678
+ q = q.reshape(-1, self.tp_heads, self.head_dim)
679
+ k = k.reshape(-1, self.tp_kv_heads, self.head_dim)
680
+ q = layernorm_fn(
681
+ q,
682
+ self.query_layernorm.weight.data,
683
+ bias=None,
684
+ eps=self.rms_norm_eps,
685
+ is_rms_norm=True,
686
+ )
687
+ k = layernorm_fn(
688
+ k,
689
+ self.key_layernorm.weight.data,
690
+ bias=None,
691
+ eps=self.rms_norm_eps,
692
+ is_rms_norm=True,
693
+ )
694
+ q = q.reshape(-1, self.q_size_per_rank)
695
+ k = k.reshape(-1, self.kv_size_per_rank)
696
+
697
+ # Apply rotary embeddings
698
+ if self.linear_rope:
699
+ q, k = self.rotary_emb(positions[:num_actual_tokens], q, k)
700
+
701
+ # Reshape to [batch, heads, seq_len, head_dim]
702
+ q = q.view((qkv.shape[0], self.tp_heads, self.head_dim))
703
+ k = k.view((qkv.shape[0], self.tp_kv_heads, self.head_dim))
704
+ v = v.view((qkv.shape[0], self.tp_kv_heads, self.head_dim))
705
+
706
+ # Apply scaling if using minimax backend
707
+ if self.linear_scale:
708
+ q = q * self.scaling
709
+
710
+ # Get KV cache and state indices
711
+ if attn_metadata is not None:
712
+ kv_cache = self.kv_cache[forward_context.virtual_engine][0]
713
+ state_indices_tensor = attn_metadata.state_indices_tensor
714
+ clear_linear_attention_cache_for_new_sequences(
715
+ kv_cache, state_indices_tensor, attn_metadata
716
+ )
717
+
718
+ # Compute attention
719
+ decode_only = getattr(attn_metadata, "num_prefills", 0) == 0
720
+ if attn_metadata is None:
721
+ hidden = torch.empty(
722
+ (q.shape[0], q.shape[1] * q.shape[2]), device=q.device, dtype=q.dtype
723
+ )
724
+ else:
725
+ if not decode_only:
726
+ hidden = self._prefill_and_mix_infer(
727
+ q, k, v, kv_cache, state_indices_tensor, attn_metadata
728
+ )
729
+ else:
730
+ hidden = self._decode_infer(
731
+ q, k, v, kv_cache, state_indices_tensor, attn_metadata
732
+ )
733
+
734
+ # Apply group norm and gate (matching SGLang behavior)
735
+ gate, _ = self.g_proj(hidden_states[:num_actual_tokens])
736
+
737
+ if self.group_norm_size > 1:
738
+ hidden = self.g_norm(hidden, gate)
739
+ else:
740
+ hidden = self.g_norm(hidden)
741
+ hidden = F.sigmoid(gate) * hidden
742
+
743
+ hidden = hidden.to(hidden_states.dtype)
744
+
745
+ # Output projection
746
+ dense_out, _ = self.dense(hidden)
747
+ output[:num_actual_tokens] = dense_out
748
+
749
+ def _prefill_and_mix_infer(
750
+ self, q, k, v, kv_cache, state_indices_tensor, attn_metadata
751
+ ):
752
+ """Handle prefill (mixed with decode if any)."""
753
+ return linear_attention_prefill_and_mix(
754
+ q=q,
755
+ k=k,
756
+ v=v,
757
+ kv_cache=kv_cache,
758
+ state_indices_tensor=state_indices_tensor,
759
+ attn_metadata=attn_metadata,
760
+ slope_rate=self.tp_slope,
761
+ block_size=self.BLOCK,
762
+ decode_fn=self._decode_infer,
763
+ prefix_fn=MiniMaxText01LinearKernel.jit_linear_forward_prefix,
764
+ layer_idx=self.layer_id,
765
+ )
766
+
767
+ def _decode_infer(self, q, k, v, kv_cache, state_indices_tensor, attn_metadata):
768
+ """Handle decode (single token per sequence)."""
769
+ num_prefill_tokens = attn_metadata.num_prefill_tokens
770
+ num_prefills = attn_metadata.num_prefills
771
+ hidden = linear_attention_decode(
772
+ q,
773
+ k,
774
+ v,
775
+ kv_cache,
776
+ self.tp_slope,
777
+ state_indices_tensor,
778
+ q_start=num_prefill_tokens,
779
+ q_end=None,
780
+ slot_start=num_prefills,
781
+ slot_end=None,
782
+ block_size=32,
783
+ )
784
+ return hidden
785
+
786
+
787
+class BailingMoeV25DecoderLayer(nn.Module):
788
+ """Decoder layer supporting both linear and full attention."""
789
+
790
+ def __init__(
791
+ self,
792
+ config: PretrainedConfig,
793
+ quant_config: QuantizationConfig | None = None,
794
+ layer_id: int = 0,
795
+ prefix: str = "layer",
796
+ model_config: ModelConfig | None = None,
797
+ cache_config: CacheConfig | None = None,
798
+ ) -> None:
799
+ super().__init__()
800
+ self.layer_id = layer_id
801
+ self.hidden_size = config.hidden_size
802
+
803
+ # Determine attention type (0 = linear, 1 = full)
804
+ self.attention_type = getattr(config, "attention_type", 1)
805
+
806
+ if self.attention_type == 0: # Linear attention
807
+ self.self_attn = BailingMoELinearAttention(
808
+ config,
809
+ quant_config=quant_config,
810
+ layer_id=layer_id,
811
+ prefix=f"{prefix}.self_attn",
812
+ model_config=model_config,
813
+ cache_config=cache_config,
814
+ )
815
+ else: # Full attention
816
+ self.self_attn = BailingMoeV25MLAAttention(
817
+ config,
818
+ quant_config=quant_config,
819
+ layer_id=layer_id,
820
+ prefix=f"{prefix}.self_attn",
821
+ cache_config=cache_config,
822
+ )
823
+
824
+ # MLP/MoE
825
+ is_moe_layer = config.num_experts > 1 and layer_id >= getattr(
826
+ config, "first_k_dense_replace", 0
827
+ )
828
+
829
+ if is_moe_layer:
830
+ self.mlp = BailingMoeV25(
831
+ config,
832
+ quant_config=quant_config,
833
+ layer_id=layer_id,
834
+ prefix=f"{prefix}.mlp",
835
+ )
836
+ else:
837
+ self.mlp = BailingMLP(
838
+ intermediate_size=config.intermediate_size,
839
+ config=config,
840
+ quant_config=quant_config,
841
+ reduce_results=True,
842
+ prefix=f"{prefix}.mlp",
843
+ )
844
+
845
+ # Layer norms
846
+ rms_norm_eps = float(getattr(config, "rms_norm_eps", 1e-5))
847
+ self.input_layernorm = RMSNorm(self.hidden_size, eps=rms_norm_eps)
848
+ self.post_attention_layernorm = RMSNorm(self.hidden_size, eps=rms_norm_eps)
849
+
850
+ def forward(
851
+ self,
852
+ hidden_states: torch.Tensor,
853
+ positions: torch.Tensor,
854
+ attn_metadata: AttentionMetadata | None = None,
855
+ residual: torch.Tensor | None = None,
856
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
857
+ # Input layernorm
858
+ if residual is None:
859
+ residual = hidden_states
860
+ hidden_states = self.input_layernorm(hidden_states)
861
+ else:
862
+ hidden_states, residual = self.input_layernorm(hidden_states, residual)
863
+
864
+ # Self attention
865
+ if self.attention_type == 0:
866
+ # Linear attention uses output tensor
867
+ self_attention_output = torch.zeros_like(hidden_states)
868
+ self.self_attn(
869
+ hidden_states=hidden_states,
870
+ output=self_attention_output,
871
+ positions=positions,
872
+ )
873
+ else:
874
+ # Full attention
875
+ self_attention_output = self.self_attn(hidden_states, positions)
876
+
877
+ hidden_states, residual = self.post_attention_layernorm(
878
+ self_attention_output, residual
879
+ )
880
+ hidden_states = self.mlp(hidden_states)
881
+ return hidden_states, residual
882
+
883
+
884
+@support_torch_compile(
885
+ dynamic_arg_dims={
886
+ "input_ids": 0,
887
+ "positions": -1,
888
+ "intermediate_tensors": 0,
889
+ "inputs_embeds": 0,
890
+ }
891
+)
892
+class BailingMoeV25Model(nn.Module):
893
+ """Bailing MoE v2.5 Model with hybrid attention support."""
894
+
895
+ def __init__(
896
+ self,
897
+ *,
898
+ vllm_config: VllmConfig,
899
+ prefix: str = "",
900
+ ):
901
+ super().__init__()
902
+ config = vllm_config.model_config.hf_config
903
+ model_config = vllm_config.model_config
904
+ quant_config = vllm_config.quant_config
905
+ cache_config = vllm_config.cache_config
906
+
907
+ self.config = config
908
+ self.vocab_size = config.vocab_size
909
+ self.embed_dim = config.hidden_size
910
+
911
+ # Determine layer types based on layer_group_size
912
+ self.layer_group_size = getattr(config, "layer_group_size", 1)
913
+ self.num_layers = config.num_hidden_layers
914
+
915
+ # decoder_attention_types: 0 = linear, 1 = full
916
+ self.decoder_attention_types = [
917
+ 0 if is_linear_layer(i, self.layer_group_size) else 1
918
+ for i in range(self.num_layers)
919
+ ]
920
+
921
+ # Embeddings
922
+ if get_pp_group().is_first_rank:
923
+ self.word_embeddings = VocabParallelEmbedding(
924
+ self.vocab_size,
925
+ self.embed_dim,
926
+ org_num_embeddings=self.vocab_size,
927
+ )
928
+ else:
929
+ from vllm.model_executor.models.utils import PPMissingLayer
930
+
931
+ self.word_embeddings = PPMissingLayer()
932
+
933
+ # Layers
934
+ def layer_fn(prefix):
935
+ layer_idx = int(prefix.split(".")[-1])
936
+ layer_config = copy.deepcopy(config)
937
+ layer_config.attention_type = self.decoder_attention_types[layer_idx]
938
+
939
+ return BailingMoeV25DecoderLayer(
940
+ config=layer_config,
941
+ quant_config=quant_config,
942
+ layer_id=layer_idx,
943
+ prefix=prefix,
944
+ model_config=model_config,
945
+ cache_config=cache_config,
946
+ )
947
+
948
+ self.start_layer, self.end_layer, self.layers = make_layers(
949
+ self.num_layers, layer_fn, prefix=f"{prefix}.layers"
950
+ )
951
+
952
+ # Final norm
953
+ norm_kwargs = {}
954
+ if hasattr(config, "rms_norm_eps"):
955
+ norm_kwargs["eps"] = config.rms_norm_eps
956
+ if get_pp_group().is_last_rank:
957
+ self.norm = RMSNorm(config.hidden_size, **norm_kwargs)
958
+ else:
959
+ from vllm.model_executor.models.utils import PPMissingLayer
960
+
961
+ self.norm = PPMissingLayer()
962
+
963
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
964
+ return self.word_embeddings(input_ids)
965
+
966
+ def forward(
967
+ self,
968
+ input_ids: torch.Tensor | None,
969
+ positions: torch.Tensor,
970
+ intermediate_tensors: IntermediateTensors | None = None,
971
+ inputs_embeds: torch.Tensor | None = None,
972
+ ) -> torch.Tensor:
973
+ forward_context = get_forward_context()
974
+ attn_metadata = forward_context.attn_metadata
975
+
976
+ if get_pp_group().is_first_rank:
977
+ if inputs_embeds is None:
978
+ hidden_states = self.word_embeddings(input_ids)
979
+ else:
980
+ hidden_states = inputs_embeds
981
+ residual = None
982
+ else:
983
+ assert intermediate_tensors is not None
984
+ hidden_states = intermediate_tensors["hidden_states"]
985
+ residual = intermediate_tensors["residual"]
986
+
987
+ for layer in self.layers[self.start_layer : self.end_layer]:
988
+ hidden_states, residual = layer(
989
+ hidden_states=hidden_states,
990
+ positions=positions,
991
+ attn_metadata=attn_metadata,
992
+ residual=residual,
993
+ )
994
+
995
+ if not get_pp_group().is_last_rank:
996
+ return IntermediateTensors(
997
+ {"hidden_states": hidden_states, "residual": residual}
998
+ )
999
+ else:
1000
+ if residual is not None:
1001
+ hidden_states, _ = self.norm(hidden_states, residual)
1002
+ else:
1003
+ hidden_states = self.norm(hidden_states)
1004
+ return hidden_states
1005
+
1006
+ def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
1007
+ """Get expert parameter mapping for MoE layers."""
1008
+ return FusedMoE.make_expert_params_mapping(
1009
+ self,
1010
+ ckpt_gate_proj_name="gate_proj",
1011
+ ckpt_down_proj_name="down_proj",
1012
+ ckpt_up_proj_name="up_proj",
1013
+ num_experts=self.config.num_experts,
1014
+ num_redundant_experts=0,
1015
+ )
1016
+
1017
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1018
+ """Load checkpoint weights with simplified mapping."""
1019
+
1020
+ params_dict = dict(self.named_parameters(remove_duplicate=False))
1021
+ loaded_params: set[str] = set()
1022
+
1023
+ # Stacked parameter mappings (fused projections)
1024
+ stacked_mappings = [
1025
+ (".fused_qkv_a_proj", ".q_a_proj", 0),
1026
+ (".fused_qkv_a_proj", ".kv_a_proj_with_mqa", 1),
1027
+ (".gate_up_proj", ".gate_proj", 0),
1028
+ (".gate_up_proj", ".up_proj", 1),
1029
+ ]
1030
+
1031
+ # Expert parameter mappings from FusedMoE
1032
+ expert_mappings = list(self.get_expert_mapping())
1033
+
1034
+ def load_param(name: str, tensor: torch.Tensor, shard_id=None) -> bool:
1035
+ """Load a single parameter."""
1036
+ if name not in params_dict or is_pp_missing_parameter(name, self):
1037
+ return False
1038
+ if name.endswith(".bias") and name not in params_dict:
1039
+ return False
1040
+
1041
+ param = params_dict[name]
1042
+ weight_loader = getattr(param, "weight_loader", default_weight_loader)
1043
+
1044
+ if shard_id is None:
1045
+ weight_loader(param, tensor)
1046
+ elif isinstance(shard_id, int):
1047
+ weight_loader(param, tensor, shard_id)
1048
+ else:
1049
+ # Expert param: (expert_id, shard_id)
1050
+ weight_loader(
1051
+ param, tensor, name, expert_id=shard_id[0], shard_id=shard_id[1]
1052
+ )
1053
+
1054
+ loaded_params.add(name)
1055
+ return True
1056
+
1057
+ def normalize_name(name: str) -> str | None:
1058
+ """Normalize checkpoint name to model parameter name."""
1059
+ # Skip special weights
1060
+ if name.startswith("model.mtp"):
1061
+ return None
1062
+ # Remove 'model.' prefix if present
1063
+ # (e.g., 'model.layers.0...' -> 'layers.0...')
1064
+ name = name.removeprefix("model.")
1065
+ # Map attention.dense based on layer type
1066
+ if "attention.dense" in name:
1067
+ layer_idx = (
1068
+ int(name.split("layers.")[1].split(".")[0])
1069
+ if "layers." in name
1070
+ else 0
1071
+ )
1072
+ attn_name = (
1073
+ "self_attn.dense"
1074
+ if is_linear_layer(layer_idx, self.config.layer_group_size)
1075
+ else "self_attn.o_proj"
1076
+ )
1077
+ name = name.replace("attention.dense", attn_name)
1078
+
1079
+ # Standard mappings
1080
+ name = name.replace("attention.", "self_attn.")
1081
+ name = name.replace(
1082
+ "mlp.gate.e_score_correction_bias", "mlp.gate.expert_bias"
1083
+ )
1084
+
1085
+ return maybe_remap_kv_scale_name(name, params_dict)
1086
+
1087
+ for orig_name, weight in weights:
1088
+ norm_name = normalize_name(orig_name)
1089
+ if norm_name is None:
1090
+ continue
1091
+
1092
+ # Try stacked mappings
1093
+ loaded = False
1094
+ for param_suf, weight_suf, shard_id in stacked_mappings:
1095
+ if weight_suf not in norm_name:
1096
+ continue
1097
+ mapped = norm_name.replace(weight_suf, param_suf).replace(
1098
+ "attention.", "self_attn."
1099
+ )
1100
+ if load_param(mapped, weight, shard_id):
1101
+ loaded = True
1102
+ break
1103
+ if loaded:
1104
+ continue
1105
+
1106
+ # Handle expert weights
1107
+ if "mlp.experts" in norm_name:
1108
+ # Expert bias
1109
+ if (
1110
+ "mlp.experts.e_score_correction_bias" in norm_name
1111
+ or "mlp.experts.expert_bias" in norm_name
1112
+ ):
1113
+ alt = norm_name.replace(
1114
+ "mlp.experts.e_score_correction_bias", "mlp.gate.expert_bias"
1115
+ ).replace("mlp.experts.expert_bias", "mlp.gate.expert_bias")
1116
+ if load_param(alt, weight) or load_param(norm_name, weight):
1117
+ continue
1118
+
1119
+ # Routed experts
1120
+ for param_name, weight_name, expert_id, shard_id in expert_mappings:
1121
+ if weight_name not in norm_name:
1122
+ continue
1123
+ mapped = norm_name.replace(weight_name, param_name)
1124
+ if load_param(mapped, weight, (expert_id, shard_id)):
1125
+ break
1126
+ continue
1127
+
1128
+ # General parameters
1129
+ load_param(norm_name, weight)
1130
+
1131
+ return loaded_params
1132
+
1133
+
1134
+class BailingMoeV25ForCausalLM(nn.Module, HasInnerState, IsHybrid, SupportsPP):
1135
+ """Bailing MoE v2.5 For CausalLM."""
1136
+
1137
+ packed_modules_mapping = {
1138
+ "gate_up_proj": ["gate_proj", "up_proj"],
1139
+ }
1140
+
1141
+ def __init__(
1142
+ self,
1143
+ *,
1144
+ vllm_config: VllmConfig,
1145
+ prefix: str = "",
1146
+ ) -> None:
1147
+ super().__init__()
1148
+ config = vllm_config.model_config.hf_config
1149
+ quant_config = vllm_config.quant_config
1150
+
1151
+ self.config = config
1152
+ self.quant_config = quant_config
1153
+
1154
+ self.model = BailingMoeV25Model(
1155
+ vllm_config=vllm_config,
1156
+ prefix=maybe_prefix(prefix, "model"),
1157
+ )
1158
+
1159
+ if get_pp_group().is_last_rank:
1160
+ self.lm_head = ParallelLMHead(
1161
+ config.vocab_size,
1162
+ config.hidden_size,
1163
+ quant_config=quant_config,
1164
+ )
1165
+ self.logits_processor = LogitsProcessor(config.vocab_size)
1166
+ else:
1167
+ self.lm_head = PPMissingLayer()
1168
+
1169
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1170
+ return self.model.embed_input_ids(input_ids)
1171
+
1172
+ def forward(
1173
+ self,
1174
+ input_ids: torch.Tensor | None,
1175
+ positions: torch.Tensor,
1176
+ intermediate_tensors: IntermediateTensors | None = None,
1177
+ inputs_embeds: torch.Tensor | None = None,
1178
+ ) -> torch.Tensor:
1179
+ hidden_states = self.model(
1180
+ input_ids=input_ids,
1181
+ positions=positions,
1182
+ intermediate_tensors=intermediate_tensors,
1183
+ inputs_embeds=inputs_embeds,
1184
+ )
1185
+ return hidden_states
1186
+
1187
+ def compute_logits(
1188
+ self,
1189
+ hidden_states: torch.Tensor,
1190
+ ) -> torch.Tensor:
1191
+ return self.logits_processor(self.lm_head, hidden_states)
1192
+
1193
+ def make_empty_intermediate_tensors(
1194
+ self, batch_size: int, dtype: torch.dtype, device: torch.device
1195
+ ) -> IntermediateTensors:
1196
+ return IntermediateTensors(
1197
+ {
1198
+ "hidden_states": torch.zeros(
1199
+ (batch_size, self.config.hidden_size), dtype=dtype, device=device
1200
+ ),
1201
+ "residual": torch.zeros(
1202
+ (batch_size, self.config.hidden_size), dtype=dtype, device=device
1203
+ ),
1204
+ }
1205
+ )
1206
+
1207
+ @classmethod
1208
+ def get_mamba_state_shape_from_config(
1209
+ cls,
1210
+ vllm_config: VllmConfig,
1211
+ ) -> tuple[tuple[int, ...], ...]:
1212
+ """Calculate shape for linear attention cache."""
1213
+ config = vllm_config.model_config.hf_config
1214
+ tp_size = vllm_config.parallel_config.tensor_parallel_size
1215
+
1216
+ head_dim = getattr(
1217
+ config, "head_dim", config.hidden_size // config.num_attention_heads
1218
+ )
1219
+
1220
+ # Return base state shape from linear attention (no padding)
1221
+ return MambaStateShapeCalculator.linear_attention_state_shape(
1222
+ num_heads=config.num_attention_heads,
1223
+ tp_size=tp_size,
1224
+ head_dim=head_dim,
1225
+ )
1226
+
1227
+ @classmethod
1228
+ def get_mamba_state_dtype_from_config(
1229
+ cls,
1230
+ vllm_config: VllmConfig,
1231
+ ) -> tuple[torch.dtype, ...]:
1232
+ return MambaStateDtypeCalculator.linear_attention_state_dtype(
1233
+ vllm_config.model_config.dtype,
1234
+ vllm_config.cache_config.mamba_cache_dtype,
1235
+ )
1236
+
1237
+ @classmethod
1238
+ def get_mamba_state_copy_func(cls) -> tuple:
1239
+ return MambaStateCopyFuncCalculator.linear_attention_state_copy_func()
1240
+
1241
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1242
+ loader = AutoWeightsLoader(self)
1243
+ return loader.load_weights(weights)
1244
+
1245
+ def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
1246
+ return self.model.get_expert_mapping()
vllm/model_executor/models/registry.py
+1
@@ -81,6 +81,7 @@ _TEXT_GENERATION_MODELS = {
81
"BaichuanForCausalLM": ("baichuan", "BaichuanForCausalLM"),
82
"BailingMoeForCausalLM": ("bailing_moe", "BailingMoeForCausalLM"),
83
"BailingMoeV2ForCausalLM": ("bailing_moe", "BailingMoeV2ForCausalLM"),
84
+ "BailingMoeV2_5ForCausalLM": ("bailing_moe_linear", "BailingMoeV25ForCausalLM"),
85
"BambaForCausalLM": ("bamba", "BambaForCausalLM"),
86
"BloomForCausalLM": ("bloom", "BloomForCausalLM"),
87
"ChatGLMModel": ("chatglm", "ChatGLMForCausalLM"),
vllm/transformers_utils/model_arch_config_convertor.py
+1
@@ -245,6 +245,7 @@ class ModelArchConfigConvertorBase:
245
"longcat_flash",
246
"pangu_ultra_moe",
247
"pangu_ultra_moe_mtp",
248
+ "bailing_hybrid",
249
):
250
return self.hf_text_config.kv_lora_rank is not None
251
elif self.hf_text_config.model_type == "eagle":