main
py 1,533 lines 56.2 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """vLLM implementation for BailingMoeV3ForCausalLM.
4
5 The HuggingFace reference model mixes MLA full-attention layers with Kimi
6 Delta Attention linear layers and Bailing MoE blocks. This file keeps the V3
7 module/weight names aligned with the reference implementation while reusing
8 vLLM's parallel linear layers, MLA kernel, KDA kernel and fused MoE loader.
9 """
10
11 import copy
12 from collections.abc import Callable, Iterable
13 from math import lcm
14 from typing import TypeGuard
15
16 import regex as re
17 import torch
18 import torch.nn as nn
19 from einops import rearrange
20 from transformers.configuration_utils import PreTrainedConfig
21
22 from vllm.compilation.decorators import support_torch_compile
23 from vllm.config import CacheConfig, ModelConfig, VllmConfig, get_current_vllm_config
24 from vllm.distributed import (
25 get_pp_group,
26 get_tensor_model_parallel_rank,
27 get_tensor_model_parallel_world_size,
28 )
29 from vllm.forward_context import ForwardContext, get_forward_context
30 from vllm.model_executor.custom_op import PluggableLayer
31 from vllm.model_executor.layers.activation import SiluAndMul, SwigluStepAndMul
32 from vllm.model_executor.layers.fused_moe import (
33 FusedMoEFactory,
34 GateLinear,
35 fused_moe_make_expert_params_mapping,
36 )
37 from vllm.model_executor.layers.layernorm import RMSNorm
38 from vllm.model_executor.layers.linear import (
39 ColumnParallelLinear,
40 MergedColumnParallelLinear,
41 ReplicatedLinear,
42 RowParallelLinear,
43 )
44 from vllm.model_executor.layers.logits_processor import LogitsProcessor
45 from vllm.model_executor.layers.mamba.abstract import MambaBase
46 from vllm.model_executor.layers.mamba.mamba_utils import (
47 MambaStateCopyFuncCalculator,
48 MambaStateDtypeCalculator,
49 MambaStateShapeCalculator,
50 is_conv_state_dim_first,
51 )
52 from vllm.model_executor.layers.mamba.ops.causal_conv1d import (
53 causal_conv1d_fn,
54 causal_conv1d_update,
55 )
56 from vllm.model_executor.layers.mla import (
57 MLAModules,
58 MultiHeadLatentAttentionWrapper,
59 )
60 from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
61 from vllm.model_executor.layers.quantization.fp8 import Fp8Config
62 from vllm.model_executor.layers.quantization.utils.quant_utils import is_layer_skipped
63 from vllm.model_executor.layers.rotary_embedding import get_rope
64 from vllm.model_executor.layers.vocab_parallel_embedding import (
65 ParallelLMHead,
66 VocabParallelEmbedding,
67 )
68 from vllm.model_executor.model_loader.weight_utils import (
69 default_weight_loader,
70 maybe_remap_kv_scale_name,
71 sharded_weight_loader,
72 )
73 from vllm.model_executor.utils import set_weight_attrs
74 from vllm.sequence import IntermediateTensors
75 from vllm.third_party.flash_linear_attention.ops.kda import (
76 FusedRMSNormGated,
77 chunk_kda,
78 fused_kda_gate,
79 fused_recurrent_kda,
80 fused_recurrent_kda_fwd,
81 )
82 from vllm.utils.torch_utils import direct_register_custom_op
83 from vllm.v1.attention.backends.gdn_attn import GDNAttentionMetadata
84 from vllm.v1.attention.backends.registry import MambaAttentionBackendEnum
85
86 from .interfaces import HasInnerState, IsHybrid, SupportsPP
87 from .utils import (
88 PPMissingLayer,
89 WeightsMapper,
90 is_pp_missing_parameter,
91 make_layers,
92 maybe_prefix,
93 )
94
95
96 def bailing_v3_kda_attention(
97 q_proj_states: torch.Tensor,
98 k_proj_states: torch.Tensor,
99 v_proj_states: torch.Tensor,
100 g1: torch.Tensor,
101 beta: torch.Tensor,
102 core_attn_out: torch.Tensor,
103 layer_name: str,
104 ) -> None:
105 """Run Bailing V3's KDA state update outside the compiled graph."""
106 forward_context: ForwardContext = get_forward_context()
107 layer = forward_context.no_compile_layers[layer_name]
108 layer._forward(
109 q_proj_states=q_proj_states,
110 k_proj_states=k_proj_states,
111 v_proj_states=v_proj_states,
112 g1=g1,
113 beta=beta,
114 core_attn_out=core_attn_out,
115 )
116
117
118 direct_register_custom_op(
119 op_name="bailing_v3_kda_attention",
120 op_func=bailing_v3_kda_attention,
121 mutates_args=["core_attn_out"],
122 )
123
124
125 def _is_kda_layer(
126 layer_idx: int, layer_group_size: int, num_hidden_layers: int
127 ) -> bool:
128 return not (
129 (layer_idx + 1) % layer_group_size == 0
130 or layer_idx >= num_hidden_layers // layer_group_size * layer_group_size
131 )
132
133
134 def _get_kda_state_shape_for_config(
135 vllm_config: VllmConfig,
136 ) -> tuple[tuple[int, int], tuple[int, int, int]]:
137 config = vllm_config.model_config.hf_config
138 num_spec = (
139 vllm_config.speculative_config.num_speculative_tokens
140 if vllm_config.speculative_config
141 else 0
142 )
143 shapes = MambaStateShapeCalculator.kda_state_shape(
144 vllm_config.parallel_config.tensor_parallel_size,
145 config.num_attention_heads,
146 config.head_dim,
147 conv_kernel_size=config.short_conv_kernel_size,
148 num_spec=num_spec,
149 )
150
151 return shapes
152
153
154 def _build_rope_parameters(config: PreTrainedConfig) -> dict | None:
155 rope_parameters = copy.deepcopy(getattr(config, "rope_parameters", None)) or {}
156 if "rope_theta" not in rope_parameters and hasattr(config, "rope_theta"):
157 rope_parameters["rope_theta"] = config.rope_theta
158 # BailingMoeV3 stores the MLA rotary width explicitly in qk_rope_head_dim.
159 # Some checkpoints also carry partial_rotary_factor=0.5, but applying it
160 # here would shrink the rotary cache to half of the qk_rope_head_dim.
161 rope_parameters.pop("partial_rotary_factor", None)
162
163 rope_scaling = getattr(config, "rope_scaling", None)
164 if isinstance(rope_scaling, dict):
165 rope_scaling = copy.deepcopy(rope_scaling)
166 if "type" in rope_scaling and "rope_type" not in rope_scaling:
167 rope_scaling["rope_type"] = rope_scaling.pop("type")
168 rope_scaling.pop("partial_rotary_factor", None)
169 rope_parameters.update(rope_scaling)
170
171 return rope_parameters or None
172
173
174 def _build_mla_rotary_embedding(
175 config: PreTrainedConfig,
176 head_size: int,
177 ) -> nn.Module:
178 rope_parameters = _build_rope_parameters(config)
179 if rope_parameters is not None and "mrope_section" in rope_parameters:
180 rope_type = rope_parameters.get("rope_type", "default")
181 if rope_type != "default":
182 raise ValueError(
183 f"Bailing M-RoPE only supports rope_type='default', got {rope_type!r}"
184 )
185 rope_parameters["rope_type"] = "bailing_mrope"
186
187 return get_rope(
188 head_size=head_size,
189 max_position=getattr(config, "max_position_embeddings", 8192),
190 is_neox_style=False,
191 rope_parameters=rope_parameters,
192 )
193
194
195 def _get_layer_swiglu_limit(limit_list: list | None, layer_idx: int) -> float | None:
196 if limit_list is None or layer_idx >= len(limit_list):
197 return None
198 limit = limit_list[layer_idx]
199 if limit in (None, 0):
200 return None
201 return float(limit)
202
203
204 def _load_a_log(param: torch.nn.Parameter, loaded_weight: torch.Tensor) -> None:
205 tp_rank = get_tensor_model_parallel_rank()
206 tp_size = get_tensor_model_parallel_world_size()
207 if loaded_weight.ndim == 1:
208 shard_size = loaded_weight.shape[0] // tp_size
209 shard = loaded_weight.narrow(0, tp_rank * shard_size, shard_size)
210 param.data.copy_(shard.view_as(param.data))
211 else:
212 sharded_weight_loader(2)(param, loaded_weight)
213
214
215 def _is_block_fp8_config(
216 quant_config: QuantizationConfig | None,
217 ) -> TypeGuard[Fp8Config]:
218 return (
219 isinstance(quant_config, Fp8Config)
220 and quant_config.weight_block_size is not None
221 )
222
223
224 def _configure_ling_fp8_quant_config(
225 quant_config: QuantizationConfig | None,
226 config: PreTrainedConfig,
227 ) -> None:
228 if not _is_block_fp8_config(quant_config):
229 return
230
231 quant_config.ignored_layers_match_mode = "suffix"
232 hf_quant_config = getattr(config, "quantization_config", None)
233 if not isinstance(hf_quant_config, dict):
234 return
235
236 quant_config.is_scale_e8m0 = ( # type: ignore[attr-defined]
237 hf_quant_config.get("scale_fmt") == "ue8m0"
238 )
239
240 routed_quant_method = hf_quant_config.get("routed_experts_quant_method")
241 if routed_quant_method is None:
242 return
243 if routed_quant_method != "mxfp4":
244 raise ValueError(
245 f"Unsupported routed experts quantization: {routed_quant_method!r}"
246 )
247
248 quant_config.store_dtype = "mxfp4"
249
250
251 _LING_MXFP4_WEIGHTS_MAPPER = WeightsMapper(
252 orig_to_new_regex={
253 re.compile(
254 r"(\.mlp\.experts\.\d+\."
255 r"(?:gate_proj|up_proj|down_proj)\.weight_scale)_inv$"
256 ): r"\1"
257 }
258 )
259
260
261 def _maybe_remap_ling_mxfp4_weight_names(
262 weights: Iterable[tuple[str, torch.Tensor]],
263 quant_config: QuantizationConfig | None,
264 ) -> Iterable[tuple[str, torch.Tensor]]:
265 """Map Ling's MXFP4 expert scales to Mxfp4MoEMethod parameters."""
266 if isinstance(quant_config, Fp8Config) and quant_config.store_dtype == "mxfp4":
267 return _LING_MXFP4_WEIGHTS_MAPPER.apply(weights)
268 return weights
269
270
271 def _is_fp8_module_excluded(
272 quant_config: QuantizationConfig | None,
273 prefix: str,
274 ) -> bool:
275 """Match Ling's abbreviated FP8 exclusions against mapped vLLM prefixes."""
276 if not _is_block_fp8_config(quant_config):
277 return False
278
279 return is_layer_skipped(
280 prefix=prefix,
281 ignored_layers=quant_config.ignored_layers,
282 fused_mapping=quant_config.packed_modules_mapping,
283 match_mode=quant_config.ignored_layers_match_mode,
284 )
285
286
287 def _get_block_fp8_mlp_padded_intermediate_size(
288 quant_config: QuantizationConfig | None,
289 intermediate_size: int,
290 prefix: str,
291 ) -> int:
292 """Pad a block-FP8 MLP so each TP shard contains whole quant blocks."""
293 if not _is_block_fp8_config(quant_config):
294 return intermediate_size
295 block_size = quant_config.weight_block_size
296 assert block_size is not None
297
298 alignments: list[int] = []
299 if not _is_fp8_module_excluded(quant_config, f"{prefix}.gate_up_proj"):
300 alignments.append(int(block_size[0]))
301 if not _is_fp8_module_excluded(quant_config, f"{prefix}.down_proj"):
302 alignments.append(int(block_size[1]))
303 if not alignments:
304 return intermediate_size
305
306 tp_size = get_tensor_model_parallel_world_size()
307 tp_alignment = tp_size * lcm(*alignments)
308 return (intermediate_size + tp_alignment - 1) // tp_alignment * tp_alignment
309
310
311 def _pad_block_fp8_mlp_checkpoint_tensor(
312 quant_config: QuantizationConfig,
313 name: str,
314 loaded_weight: torch.Tensor,
315 intermediate_size: int,
316 padded_intermediate_size: int,
317 ) -> torch.Tensor:
318 """Zero-pad an MLP checkpoint tensor on its intermediate dimension."""
319 if padded_intermediate_size == intermediate_size:
320 return loaded_weight
321
322 block_size = getattr(quant_config, "weight_block_size", None)
323 assert block_size is not None
324
325 if ".down_proj." in name:
326 if name.endswith(".bias"):
327 return loaded_weight
328 dim = 1
329 block = int(block_size[1])
330 logical_shards = 1
331 elif any(
332 projection in name
333 for projection in (".gate_proj.", ".up_proj.", ".gate_up_proj.")
334 ):
335 dim = 0
336 block = int(block_size[0])
337 logical_shards = 2 if ".gate_up_proj." in name else 1
338 else:
339 return loaded_weight
340
341 if name.endswith(".weight_scale_inv"):
342 expected_shard_size = (intermediate_size + block - 1) // block
343 target_shard_size = (padded_intermediate_size + block - 1) // block
344 elif name.endswith((".weight", ".bias")):
345 expected_shard_size = intermediate_size
346 target_shard_size = padded_intermediate_size
347 else:
348 return loaded_weight
349
350 current_size = loaded_weight.shape[dim]
351 if current_size % logical_shards != 0:
352 raise ValueError(
353 f"Cannot split {name} dimension {current_size} into "
354 f"{logical_shards} logical shards."
355 )
356 current_shard_size = current_size // logical_shards
357 if current_shard_size == target_shard_size:
358 return loaded_weight
359 if current_shard_size != expected_shard_size:
360 raise ValueError(
361 f"Cannot pad {name}: expected each logical intermediate dimension "
362 f"to be {expected_shard_size}, but got {current_shard_size}."
363 )
364
365 padded_shards: list[torch.Tensor] = []
366 for shard in loaded_weight.split(current_shard_size, dim=dim):
367 pad_shape = list(shard.shape)
368 pad_shape[dim] = target_shard_size - current_shard_size
369 padded_shards.extend([shard, shard.new_zeros(pad_shape)])
370 return torch.cat(padded_shards, dim=dim)
371
372
373 def _maybe_pad_block_fp8_shared_expert_checkpoint_tensor(
374 quant_config: QuantizationConfig | None,
375 config: PreTrainedConfig,
376 name: str,
377 loaded_weight: torch.Tensor,
378 ) -> torch.Tensor:
379 if ".mlp.shared_experts." not in name:
380 return loaded_weight
381
382 shared_prefix = name.split(".shared_experts.", 1)[0] + ".shared_experts"
383 shared_intermediate = (
384 config.moe_shared_expert_intermediate_size * config.num_shared_experts
385 )
386 padded_shared_intermediate = _get_block_fp8_mlp_padded_intermediate_size(
387 quant_config,
388 shared_intermediate,
389 shared_prefix,
390 )
391 if padded_shared_intermediate == shared_intermediate:
392 return loaded_weight
393
394 assert quant_config is not None
395 return _pad_block_fp8_mlp_checkpoint_tensor(
396 quant_config,
397 name,
398 loaded_weight,
399 shared_intermediate,
400 padded_shared_intermediate,
401 )
402
403
404 class _IdentityProjection(nn.Module):
405 def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, None]:
406 return x, None
407
408
409 class BailingMoeV3MLP(nn.Module):
410 def __init__(
411 self,
412 intermediate_size: int,
413 config: PreTrainedConfig,
414 quant_config: QuantizationConfig | None = None,
415 reduce_results: bool = True,
416 prefix: str = "",
417 swiglu_limit: float | None = None,
418 ) -> None:
419 super().__init__()
420 self.gate_up_proj = MergedColumnParallelLinear(
421 config.hidden_size,
422 [intermediate_size] * 2,
423 bias=config.use_bias,
424 quant_config=quant_config,
425 prefix=f"{prefix}.gate_up_proj",
426 )
427 self.down_proj = RowParallelLinear(
428 intermediate_size,
429 config.hidden_size,
430 bias=config.use_bias,
431 quant_config=quant_config,
432 reduce_results=reduce_results,
433 prefix=f"{prefix}.down_proj",
434 )
435 self.act_fn = (
436 SwigluStepAndMul(limit=swiglu_limit) if swiglu_limit else SiluAndMul()
437 )
438
439 def forward(self, x):
440 x, _ = self.gate_up_proj(x)
441 x = self.act_fn(x)
442 x, _ = self.down_proj(x)
443 return x
444
445
446 class BailingMoeV3MLAAttention(nn.Module):
447 def __init__(
448 self,
449 config: PreTrainedConfig,
450 quant_config: QuantizationConfig | None = None,
451 layer_id: int = 0,
452 prefix: str = "attention",
453 cache_config: CacheConfig | None = None,
454 ) -> None:
455 super().__init__()
456 self.hidden_size = config.hidden_size
457 self.num_heads = config.num_attention_heads
458 self.layer_id = layer_id
459
460 self.qk_nope_head_dim = getattr(config, "qk_nope_head_dim", 128)
461 self.qk_rope_head_dim = getattr(config, "qk_rope_head_dim", 64)
462 self.qk_head_dim = self.qk_nope_head_dim + self.qk_rope_head_dim
463 self.v_head_dim = getattr(config, "v_head_dim", 128)
464 self.q_lora_rank = getattr(config, "q_lora_rank", None)
465 self.kv_lora_rank = getattr(config, "kv_lora_rank", 512)
466
467 tp_size = get_tensor_model_parallel_world_size()
468 assert self.num_heads % tp_size == 0
469 self.num_local_heads = self.num_heads // tp_size
470 self.scaling = self.qk_head_dim**-0.5
471
472 self.q_proj: ColumnParallelLinear | None
473 self.kv_a_proj_with_mqa: ReplicatedLinear | None
474 if self.q_lora_rank is None:
475 self.q_proj = ColumnParallelLinear(
476 self.hidden_size,
477 self.num_heads * self.qk_head_dim,
478 bias=False,
479 quant_config=quant_config,
480 prefix=f"{prefix}.q_proj",
481 )
482 self.fused_qkv_a_proj = None
483 self.q_a_layernorm = None
484 self.q_b_proj = None
485 self.kv_a_proj_with_mqa = ReplicatedLinear(
486 self.hidden_size,
487 self.kv_lora_rank + self.qk_rope_head_dim,
488 bias=False,
489 quant_config=quant_config,
490 prefix=f"{prefix}.kv_a_proj_with_mqa",
491 )
492 else:
493 self.fused_qkv_a_proj = MergedColumnParallelLinear(
494 self.hidden_size,
495 [self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
496 bias=False,
497 quant_config=quant_config,
498 prefix=f"{prefix}.fused_qkv_a_proj",
499 disable_tp=True,
500 )
501 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
502 self.q_b_proj = ColumnParallelLinear(
503 self.q_lora_rank,
504 self.num_heads * self.qk_head_dim,
505 bias=False,
506 quant_config=quant_config,
507 prefix=f"{prefix}.q_b_proj",
508 )
509 self.q_proj = None
510 self.kv_a_proj_with_mqa = None
511
512 self.kv_a_layernorm = RMSNorm(self.kv_lora_rank, eps=config.rms_norm_eps)
513 self.kv_b_proj = ColumnParallelLinear(
514 self.kv_lora_rank,
515 self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
516 bias=False,
517 quant_config=quant_config,
518 prefix=f"{prefix}.kv_b_proj",
519 )
520 self.gated_attention_proj_granularity_type = getattr(
521 config, "gated_attention_proj_granularity_type", None
522 )
523 if self.gated_attention_proj_granularity_type == "head_wise":
524 g_out = self.num_heads
525 elif self.gated_attention_proj_granularity_type == "element_wise":
526 g_out = self.num_heads * self.v_head_dim
527 else:
528 g_out = 0
529 self.g_proj = (
530 ColumnParallelLinear(
531 self.hidden_size,
532 g_out,
533 bias=False,
534 quant_config=quant_config,
535 prefix=f"{prefix}.g_proj",
536 )
537 if g_out > 0
538 else None
539 )
540 self.dense = RowParallelLinear(
541 self.num_heads * self.v_head_dim,
542 self.hidden_size,
543 bias=False,
544 quant_config=quant_config,
545 prefix=f"{prefix}.dense",
546 )
547
548 self.rotary_emb = _build_mla_rotary_embedding(
549 config,
550 head_size=self.qk_rope_head_dim,
551 )
552 mla_modules = MLAModules(
553 kv_a_layernorm=self.kv_a_layernorm,
554 kv_b_proj=self.kv_b_proj,
555 rotary_emb=self.rotary_emb,
556 o_proj=_IdentityProjection(),
557 fused_qkv_a_proj=self.fused_qkv_a_proj,
558 kv_a_proj_with_mqa=self.kv_a_proj_with_mqa,
559 q_a_layernorm=self.q_a_layernorm,
560 q_b_proj=self.q_b_proj,
561 q_proj=self.q_proj,
562 indexer=None,
563 is_sparse=False,
564 topk_indices_buffer=None,
565 )
566 self.mla_attn = MultiHeadLatentAttentionWrapper(
567 self.hidden_size,
568 self.num_local_heads,
569 self.scaling,
570 self.qk_nope_head_dim,
571 self.qk_rope_head_dim,
572 self.v_head_dim,
573 self.q_lora_rank,
574 self.kv_lora_rank,
575 mla_modules,
576 cache_config,
577 quant_config,
578 prefix,
579 )
580
581 def forward(self, hidden_states: torch.Tensor, positions: torch.Tensor):
582 attn_out = self.mla_attn(positions, hidden_states)
583 if self.g_proj is not None:
584 gate = torch.sigmoid(self.g_proj(hidden_states)[0].float()).to(
585 hidden_states.dtype
586 )
587 if self.gated_attention_proj_granularity_type == "head_wise":
588 attn_out = attn_out.view(-1, self.num_local_heads, self.v_head_dim)
589 attn_out = attn_out * gate.unsqueeze(-1)
590 attn_out = attn_out.reshape(hidden_states.shape[0], -1)
591 else:
592 attn_out = attn_out * gate
593
594 return self.dense(attn_out)[0]
595
596
597 # --8<-- [start:bailing_moe_v3_kimi_delta_attention]
598 @PluggableLayer.register("bailing_moe_v3_kimi_delta_attention")
599 class BailingMoeV3KimiDeltaAttention(PluggableLayer, MambaBase):
600 # --8<-- [end:bailing_moe_v3_kimi_delta_attention]
601
602 @property
603 def mamba_type(self) -> MambaAttentionBackendEnum:
604 return MambaAttentionBackendEnum.GDN_ATTN
605
606 def get_state_dtype(
607 self,
608 ) -> tuple[torch.dtype, torch.dtype]:
609 if self.model_config is None or self.cache_config is None:
610 raise ValueError("model_config and cache_config must be set")
611 return MambaStateDtypeCalculator.kda_state_dtype(
612 self.model_config.dtype, self.cache_config.mamba_cache_dtype
613 )
614
615 def get_state_shape(
616 self,
617 ) -> tuple[tuple[int, int], tuple[int, int, int]]:
618 return MambaStateShapeCalculator.kda_state_shape(
619 self.tp_size,
620 self.num_heads,
621 self.head_dim,
622 conv_kernel_size=self.conv_size,
623 num_spec=self.num_speculative_tokens,
624 )
625
626 def __init__(
627 self,
628 config: PreTrainedConfig,
629 quant_config: QuantizationConfig | None = None,
630 layer_id: int = 0,
631 prefix: str = "attention",
632 model_config: ModelConfig | None = None,
633 cache_config: CacheConfig | None = None,
634 num_speculative_tokens: int = 0,
635 ) -> None:
636 super().__init__()
637 self.tp_size = get_tensor_model_parallel_world_size()
638 self.hidden_size = config.hidden_size
639 self.head_dim = config.head_dim
640 self.num_heads = config.num_attention_heads
641 self.local_num_heads = self.num_heads // self.tp_size
642 self.conv_size = config.short_conv_kernel_size
643 self.layer_id = layer_id
644 self.prefix = prefix
645 self.model_config = model_config
646 self.cache_config = cache_config
647 self.num_speculative_tokens = num_speculative_tokens
648 self.safe_gate = getattr(config, "kda_safe_gate", True)
649 self.lower_bound = getattr(config, "kda_lower_bound", -5.0)
650 if not getattr(config, "no_kda_lora", False):
651 raise ValueError("BailingMoeV3 KDA currently expects no_kda_lora=True")
652
653 projection_size = self.head_dim * self.num_heads
654 self.projection_size_per_partition = projection_size // self.tp_size
655 self.separate_b_proj = _is_fp8_module_excluded(quant_config, f"{prefix}.b_proj")
656 self.qkv_proj: MergedColumnParallelLinear | None
657 self.b_proj: ColumnParallelLinear | None
658 self.qkvb_proj: MergedColumnParallelLinear | None
659 if self.separate_b_proj:
660 self.qkv_proj = MergedColumnParallelLinear(
661 self.hidden_size,
662 [projection_size, projection_size, projection_size],
663 bias=False,
664 quant_config=quant_config,
665 prefix=f"{prefix}.qkv_proj",
666 )
667 self.b_proj = ColumnParallelLinear(
668 self.hidden_size,
669 self.num_heads,
670 bias=False,
671 quant_config=quant_config,
672 prefix=f"{prefix}.b_proj",
673 )
674 self.qkvb_proj = None
675 else:
676 self.qkvb_proj = MergedColumnParallelLinear(
677 self.hidden_size,
678 [projection_size, projection_size, projection_size, self.num_heads],
679 bias=False,
680 quant_config=quant_config,
681 prefix=f"{prefix}.qkvb_proj",
682 )
683 self.qkv_proj = None
684 self.b_proj = None
685 self.f_proj = ColumnParallelLinear(
686 self.hidden_size,
687 projection_size,
688 bias=False,
689 quant_config=quant_config,
690 prefix=f"{prefix}.f_proj",
691 )
692 self.dt_bias = nn.Parameter(
693 torch.empty(projection_size // self.tp_size, dtype=torch.float32)
694 )
695 set_weight_attrs(self.dt_bias, {"weight_loader": sharded_weight_loader(0)})
696
697 self.q_conv1d = ColumnParallelLinear(
698 input_size=self.conv_size,
699 output_size=projection_size,
700 bias=False,
701 params_dtype=torch.float32,
702 prefix=f"{prefix}.q_conv1d",
703 )
704 self.k_conv1d = ColumnParallelLinear(
705 input_size=self.conv_size,
706 output_size=projection_size,
707 bias=False,
708 params_dtype=torch.float32,
709 prefix=f"{prefix}.k_conv1d",
710 )
711 self.v_conv1d = ColumnParallelLinear(
712 input_size=self.conv_size,
713 output_size=projection_size,
714 bias=False,
715 params_dtype=torch.float32,
716 prefix=f"{prefix}.v_conv1d",
717 )
718 self.q_conv1d.weight.data = self.q_conv1d.weight.data.unsqueeze(1)
719 self.k_conv1d.weight.data = self.k_conv1d.weight.data.unsqueeze(1)
720 self.v_conv1d.weight.data = self.v_conv1d.weight.data.unsqueeze(1)
721
722 self.A_log = nn.Parameter(
723 torch.empty(1, 1, self.local_num_heads, 1, dtype=torch.float32)
724 )
725 set_weight_attrs(self.A_log, {"weight_loader": _load_a_log})
726
727 self.g_proj = ColumnParallelLinear(
728 self.hidden_size,
729 projection_size,
730 bias=False,
731 quant_config=quant_config,
732 prefix=f"{prefix}.g_proj",
733 )
734 self.o_norm = FusedRMSNormGated(
735 self.head_dim, eps=config.rms_norm_eps, activation="sigmoid"
736 )
737 self.o_proj = RowParallelLinear(
738 projection_size,
739 self.hidden_size,
740 bias=False,
741 quant_config=quant_config,
742 prefix=f"{prefix}.o_proj",
743 )
744
745 compilation_config = get_current_vllm_config().compilation_config
746 if prefix in compilation_config.static_forward_context:
747 raise ValueError(f"Duplicate layer name: {prefix}")
748 compilation_config.static_forward_context[prefix] = self
749 splitting_op = "vllm::bailing_v3_kda_attention"
750 if (
751 compilation_config.splitting_ops is not None
752 and splitting_op not in compilation_config.splitting_ops
753 ):
754 compilation_config.splitting_ops.append(splitting_op)
755
756 def forward(
757 self,
758 hidden_states: torch.Tensor,
759 positions: torch.Tensor,
760 output: torch.Tensor,
761 ) -> None:
762 del positions
763 num_tokens = hidden_states.size(0)
764 if self.separate_b_proj:
765 assert self.qkv_proj is not None and self.b_proj is not None
766 qkv = self.qkv_proj(hidden_states)[0]
767 q, k, v = qkv.split(self.projection_size_per_partition, dim=-1)
768 beta_logits = self.b_proj(hidden_states)[0]
769 else:
770 assert self.qkvb_proj is not None
771 qkvb = self.qkvb_proj(hidden_states)[0]
772 q, k, v, beta_logits = qkvb.split(
773 [
774 self.projection_size_per_partition,
775 self.projection_size_per_partition,
776 self.projection_size_per_partition,
777 self.local_num_heads,
778 ],
779 dim=-1,
780 )
781 beta = beta_logits.float().sigmoid().unsqueeze(0)
782 g1 = self.f_proj(hidden_states)[0]
783 g1 = fused_kda_gate(
784 g1,
785 self.A_log,
786 self.head_dim,
787 g_bias=self.dt_bias,
788 lower_bound=self.lower_bound if self.safe_gate else None,
789 )
790 g1 = g1.unsqueeze(0)
791 g2 = rearrange(
792 self.g_proj(hidden_states)[0], "... (h d) -> ... h d", d=self.head_dim
793 )
794
795 core_attn_out = torch.zeros(
796 (1, num_tokens, self.local_num_heads, self.head_dim),
797 dtype=hidden_states.dtype,
798 device=hidden_states.device,
799 )
800 torch.ops.vllm.bailing_v3_kda_attention(
801 q, k, v, g1, beta, core_attn_out, self.prefix
802 )
803 core_attn_out = self.o_norm(core_attn_out, g2)
804 core_attn_out = rearrange(core_attn_out, "1 n h d -> n (h d)")
805 output[:] = self.o_proj(core_attn_out)[0]
806
807 def _forward(
808 self,
809 q_proj_states: torch.Tensor,
810 k_proj_states: torch.Tensor,
811 v_proj_states: torch.Tensor,
812 g1: torch.Tensor,
813 beta: torch.Tensor,
814 core_attn_out: torch.Tensor,
815 ) -> None:
816 forward_context = get_forward_context()
817 attn_metadata_map = forward_context.attn_metadata
818 if attn_metadata_map is None:
819 return
820
821 assert isinstance(attn_metadata_map, dict)
822 attn_metadata = attn_metadata_map.get(self.prefix)
823 if attn_metadata is None:
824 # Profile/warmup dummy runs skip mamba-family metadata.
825 return
826 assert isinstance(attn_metadata, GDNAttentionMetadata)
827 has_initial_state = attn_metadata.has_initial_state
828 spec_query_start_loc = attn_metadata.spec_query_start_loc
829 query_start_loc = attn_metadata.non_spec_query_start_loc
830 spec_state_indices = attn_metadata.spec_state_indices_tensor
831 state_indices = attn_metadata.non_spec_state_indices_tensor
832 spec_sequence_masks = attn_metadata.spec_sequence_masks
833 spec_token_indx = attn_metadata.spec_token_indx
834 non_spec_token_indx = attn_metadata.non_spec_token_indx
835 num_accepted_tokens = attn_metadata.num_accepted_tokens
836 num_actual_tokens = attn_metadata.num_actual_tokens
837 conv_state, recurrent_state = self.kv_cache
838 recurrent_state_active = recurrent_state[..., : self.head_dim]
839 if not is_conv_state_dim_first():
840 conv_state = conv_state.transpose(-1, -2)
841 conv_state_q, conv_state_k, conv_state_v = conv_state.chunk(3, dim=-2)
842
843 q_proj_states = q_proj_states[:num_actual_tokens]
844 k_proj_states = k_proj_states[:num_actual_tokens]
845 v_proj_states = v_proj_states[:num_actual_tokens]
846 g1 = g1[:, :num_actual_tokens]
847 beta = beta[:, :num_actual_tokens]
848
849 spec_only = (
850 spec_sequence_masks is not None
851 and attn_metadata.num_prefills == 0
852 and attn_metadata.num_decodes == 0
853 )
854 if spec_sequence_masks is not None:
855 assert spec_token_indx is not None
856 assert non_spec_token_indx is not None
857 if spec_only:
858 q_proj_states_spec = q_proj_states
859 k_proj_states_spec = k_proj_states
860 v_proj_states_spec = v_proj_states
861 g1_spec = g1
862 beta_spec = beta
863 q_proj_states_non_spec = None
864 k_proj_states_non_spec = None
865 v_proj_states_non_spec = None
866 g1_non_spec = None
867 beta_non_spec = None
868 else:
869 q_proj_states_spec = q_proj_states.index_select(0, spec_token_indx)
870 k_proj_states_spec = k_proj_states.index_select(0, spec_token_indx)
871 v_proj_states_spec = v_proj_states.index_select(0, spec_token_indx)
872 g1_spec = g1.index_select(1, spec_token_indx)
873 beta_spec = beta.index_select(1, spec_token_indx)
874 q_proj_states_non_spec = q_proj_states.index_select(
875 0, non_spec_token_indx
876 )
877 k_proj_states_non_spec = k_proj_states.index_select(
878 0, non_spec_token_indx
879 )
880 v_proj_states_non_spec = v_proj_states.index_select(
881 0, non_spec_token_indx
882 )
883 g1_non_spec = g1.index_select(1, non_spec_token_indx)
884 beta_non_spec = beta.index_select(1, non_spec_token_indx)
885 else:
886 q_proj_states_spec = None
887 k_proj_states_spec = None
888 v_proj_states_spec = None
889 g1_spec = None
890 beta_spec = None
891 q_proj_states_non_spec = q_proj_states
892 k_proj_states_non_spec = k_proj_states
893 v_proj_states_non_spec = v_proj_states
894 g1_non_spec = g1
895 beta_non_spec = beta
896
897 q_conv_weights = self.q_conv1d.weight.view(
898 self.q_conv1d.weight.size(0), self.q_conv1d.weight.size(2)
899 )
900 k_conv_weights = self.k_conv1d.weight.view(
901 self.k_conv1d.weight.size(0), self.k_conv1d.weight.size(2)
902 )
903 v_conv_weights = self.v_conv1d.weight.view(
904 self.v_conv1d.weight.size(0), self.v_conv1d.weight.size(2)
905 )
906
907 if spec_sequence_masks is not None:
908 assert q_proj_states_spec is not None
909 assert k_proj_states_spec is not None
910 assert v_proj_states_spec is not None
911 assert spec_state_indices is not None
912 q_spec = causal_conv1d_update(
913 q_proj_states_spec,
914 conv_state_q,
915 q_conv_weights,
916 self.q_conv1d.bias,
917 activation="silu",
918 conv_state_indices=spec_state_indices[:, 0][
919 : attn_metadata.num_spec_decodes
920 ],
921 num_accepted_tokens=num_accepted_tokens,
922 query_start_loc=spec_query_start_loc,
923 max_query_len=spec_state_indices.size(-1),
924 validate_data=False,
925 )
926 k_spec = causal_conv1d_update(
927 k_proj_states_spec,
928 conv_state_k,
929 k_conv_weights,
930 self.k_conv1d.bias,
931 activation="silu",
932 conv_state_indices=spec_state_indices[:, 0][
933 : attn_metadata.num_spec_decodes
934 ],
935 num_accepted_tokens=num_accepted_tokens,
936 query_start_loc=spec_query_start_loc,
937 max_query_len=spec_state_indices.size(-1),
938 validate_data=False,
939 )
940 v_spec = causal_conv1d_update(
941 v_proj_states_spec,
942 conv_state_v,
943 v_conv_weights,
944 self.v_conv1d.bias,
945 activation="silu",
946 conv_state_indices=spec_state_indices[:, 0][
947 : attn_metadata.num_spec_decodes
948 ],
949 num_accepted_tokens=num_accepted_tokens,
950 query_start_loc=spec_query_start_loc,
951 max_query_len=spec_state_indices.size(-1),
952 validate_data=False,
953 )
954 else:
955 q_spec = k_spec = v_spec = None
956
957 if attn_metadata.num_prefills > 0:
958 assert q_proj_states_non_spec is not None
959 assert k_proj_states_non_spec is not None
960 assert v_proj_states_non_spec is not None
961 q = causal_conv1d_fn(
962 q_proj_states_non_spec.transpose(0, 1),
963 q_conv_weights,
964 self.q_conv1d.bias,
965 activation="silu",
966 conv_states=conv_state_q,
967 has_initial_state=has_initial_state,
968 cache_indices=state_indices,
969 query_start_loc=query_start_loc,
970 metadata=attn_metadata,
971 ).transpose(0, 1)
972 k = causal_conv1d_fn(
973 k_proj_states_non_spec.transpose(0, 1),
974 k_conv_weights,
975 self.k_conv1d.bias,
976 activation="silu",
977 conv_states=conv_state_k,
978 has_initial_state=has_initial_state,
979 cache_indices=state_indices,
980 query_start_loc=query_start_loc,
981 metadata=attn_metadata,
982 ).transpose(0, 1)
983 v = causal_conv1d_fn(
984 v_proj_states_non_spec.transpose(0, 1),
985 v_conv_weights,
986 self.v_conv1d.bias,
987 activation="silu",
988 conv_states=conv_state_v,
989 has_initial_state=has_initial_state,
990 cache_indices=state_indices,
991 query_start_loc=query_start_loc,
992 metadata=attn_metadata,
993 ).transpose(0, 1)
994 elif attn_metadata.num_decodes > 0:
995 assert q_proj_states_non_spec is not None
996 assert k_proj_states_non_spec is not None
997 assert v_proj_states_non_spec is not None
998 assert state_indices is not None
999 decode_indices = state_indices[:num_actual_tokens]
1000 q = causal_conv1d_update(
1001 q_proj_states_non_spec,
1002 conv_state_q,
1003 q_conv_weights,
1004 self.q_conv1d.bias,
1005 activation="silu",
1006 conv_state_indices=decode_indices,
1007 validate_data=True,
1008 )
1009 k = causal_conv1d_update(
1010 k_proj_states_non_spec,
1011 conv_state_k,
1012 k_conv_weights,
1013 self.k_conv1d.bias,
1014 activation="silu",
1015 conv_state_indices=decode_indices,
1016 validate_data=True,
1017 )
1018 v = causal_conv1d_update(
1019 v_proj_states_non_spec,
1020 conv_state_v,
1021 v_conv_weights,
1022 self.v_conv1d.bias,
1023 activation="silu",
1024 conv_state_indices=decode_indices,
1025 validate_data=True,
1026 )
1027 else:
1028 q = k = v = None
1029
1030 if q_spec is not None:
1031 q_spec, k_spec, v_spec = map(
1032 lambda x: rearrange(x, "n (h d) -> 1 n h d", d=self.head_dim),
1033 (q_spec, k_spec, v_spec),
1034 )
1035 if q is not None:
1036 q, k, v = map(
1037 lambda x: rearrange(x, "n (h d) -> 1 n h d", d=self.head_dim),
1038 (q, k, v),
1039 )
1040 if spec_sequence_masks is not None:
1041 assert g1_spec is not None
1042 assert beta_spec is not None
1043 assert spec_query_start_loc is not None
1044 assert spec_state_indices is not None
1045 out_spec, _ = fused_recurrent_kda_fwd(
1046 q=q_spec.contiguous(),
1047 k=k_spec.contiguous(),
1048 v=v_spec.contiguous(),
1049 g=g1_spec.contiguous(),
1050 beta=beta_spec.contiguous(),
1051 scale=self.head_dim**-0.5,
1052 initial_state=recurrent_state_active,
1053 inplace_final_state=True,
1054 cu_seqlens=spec_query_start_loc[: attn_metadata.num_spec_decodes + 1],
1055 ssm_state_indices=spec_state_indices,
1056 num_accepted_tokens=num_accepted_tokens,
1057 use_qk_l2norm_in_kernel=True,
1058 )
1059 else:
1060 out_spec = None
1061
1062 if attn_metadata.num_prefills > 0:
1063 assert q is not None and k is not None and v is not None
1064 assert g1_non_spec is not None and beta_non_spec is not None
1065 assert state_indices is not None
1066 assert has_initial_state is not None
1067 zero_idx = state_indices[~has_initial_state]
1068 recurrent_state[zero_idx] = 0
1069 initial_state = recurrent_state_active[state_indices].contiguous()
1070 out, last_state = chunk_kda(
1071 q=q,
1072 k=k,
1073 v=v,
1074 g=g1_non_spec,
1075 beta=beta_non_spec,
1076 initial_state=initial_state,
1077 output_final_state=True,
1078 use_qk_l2norm_in_kernel=True,
1079 safe_gate=self.safe_gate,
1080 lower_bound=self.lower_bound,
1081 cu_seqlens=query_start_loc,
1082 )
1083 recurrent_state_active[state_indices] = last_state
1084 elif attn_metadata.num_decodes > 0:
1085 assert q is not None and k is not None and v is not None
1086 assert g1_non_spec is not None and beta_non_spec is not None
1087 assert state_indices is not None
1088 assert query_start_loc is not None
1089 out, _ = fused_recurrent_kda(
1090 q=q,
1091 k=k,
1092 v=v,
1093 g=g1_non_spec,
1094 beta=beta_non_spec,
1095 initial_state=recurrent_state_active,
1096 use_qk_l2norm_in_kernel=True,
1097 safe_gate=self.safe_gate,
1098 lower_bound=self.lower_bound,
1099 cu_seqlens=query_start_loc[: attn_metadata.num_decodes + 1],
1100 ssm_state_indices=state_indices,
1101 )
1102 else:
1103 out = None
1104
1105 if spec_sequence_masks is not None and out is not None:
1106 assert spec_token_indx is not None and non_spec_token_indx is not None
1107 merged_out = torch.empty(
1108 (1, num_actual_tokens, *out_spec.shape[2:]),
1109 dtype=out_spec.dtype,
1110 device=out_spec.device,
1111 )
1112 merged_out.index_copy_(1, spec_token_indx, out_spec)
1113 merged_out.index_copy_(1, non_spec_token_indx, out)
1114 core_attn_out[0, :num_actual_tokens] = merged_out[0, :num_actual_tokens]
1115 elif spec_sequence_masks is not None:
1116 core_attn_out[0, :num_actual_tokens] = out_spec[0, :num_actual_tokens]
1117 else:
1118 core_attn_out[0, :num_actual_tokens] = out[0, :num_actual_tokens]
1119
1120
1121 class BailingMoeV3MoE(nn.Module):
1122 def __init__(
1123 self,
1124 config: PreTrainedConfig,
1125 quant_config: QuantizationConfig | None = None,
1126 layer_id: int = 0,
1127 prefix: str = "",
1128 ) -> None:
1129 super().__init__()
1130 self.num_experts = config.num_experts
1131 self.top_k = config.num_experts_per_tok
1132 self.hidden_size = config.hidden_size
1133 self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
1134 self.gate = GateLinear(
1135 config.hidden_size,
1136 config.num_experts,
1137 out_dtype=torch.float32,
1138 params_dtype=torch.float32,
1139 prefix=f"{prefix}.gate",
1140 )
1141 self.gate.expert_bias = nn.Parameter(
1142 torch.empty((config.num_experts,), dtype=torch.float32)
1143 )
1144 self.expert_swiglu_limit = _get_layer_swiglu_limit(
1145 getattr(config, "expert_swiglu_limit_list", None), layer_id
1146 )
1147 self.share_expert_swiglu_limit = _get_layer_swiglu_limit(
1148 getattr(config, "share_expert_swiglu_limit_list", None), layer_id
1149 )
1150 shared_intermediate = (
1151 config.moe_shared_expert_intermediate_size * config.num_shared_experts
1152 )
1153 self.shared_experts = BailingMoeV3MLP(
1154 intermediate_size=_get_block_fp8_mlp_padded_intermediate_size(
1155 quant_config, shared_intermediate, f"{prefix}.shared_experts"
1156 ),
1157 config=config,
1158 quant_config=quant_config,
1159 reduce_results=False,
1160 prefix=f"{prefix}.shared_experts",
1161 swiglu_limit=self.share_expert_swiglu_limit,
1162 )
1163 self.experts = FusedMoEFactory(
1164 shared_experts=self.shared_experts,
1165 num_experts=self.num_experts,
1166 top_k=self.top_k,
1167 hidden_size=self.hidden_size,
1168 intermediate_size=config.moe_intermediate_size,
1169 renormalize=True,
1170 quant_config=quant_config,
1171 activation="silu",
1172 swiglu_limit=self.expert_swiglu_limit,
1173 prefix=f"{prefix}.experts",
1174 scoring_func="sigmoid",
1175 routed_scaling_factor=self.routed_scaling_factor,
1176 e_score_correction_bias=self.gate.expert_bias,
1177 num_expert_group=config.n_group,
1178 topk_group=config.topk_group,
1179 use_grouped_topk=True,
1180 router_logits_dtype=torch.float32,
1181 )
1182
1183 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
1184 num_tokens, hidden_size = hidden_states.shape
1185 hidden_states = hidden_states.contiguous().view(-1, hidden_size)
1186 router_logits, _ = self.gate(hidden_states)
1187 hidden_states = self.experts(
1188 hidden_states=hidden_states, router_logits=router_logits
1189 )
1190 return hidden_states.view(num_tokens, hidden_size)
1191
1192
1193 class BailingMoeV3DecoderLayer(nn.Module):
1194 def __init__(
1195 self,
1196 config: PreTrainedConfig,
1197 quant_config: QuantizationConfig | None = None,
1198 layer_id: int = 0,
1199 prefix: str = "layer",
1200 model_config: ModelConfig | None = None,
1201 cache_config: CacheConfig | None = None,
1202 num_speculative_tokens: int = 0,
1203 ) -> None:
1204 super().__init__()
1205 self.layer_id = layer_id
1206 self.hidden_size = config.hidden_size
1207 self.is_kda_layer = _is_kda_layer(
1208 layer_id, config.layer_group_size, config.num_hidden_layers
1209 )
1210 if self.is_kda_layer:
1211 self.self_attn = BailingMoeV3KimiDeltaAttention(
1212 config,
1213 quant_config=quant_config,
1214 layer_id=layer_id,
1215 prefix=f"{prefix}.self_attn",
1216 model_config=model_config,
1217 cache_config=cache_config,
1218 num_speculative_tokens=num_speculative_tokens,
1219 )
1220 else:
1221 self.self_attn = BailingMoeV3MLAAttention(
1222 config,
1223 quant_config=quant_config,
1224 layer_id=layer_id,
1225 prefix=f"{prefix}.self_attn",
1226 cache_config=cache_config,
1227 )
1228
1229 if config.num_experts is not None and layer_id >= config.first_k_dense_replace:
1230 self.mlp = BailingMoeV3MoE(
1231 config,
1232 quant_config=quant_config,
1233 layer_id=layer_id,
1234 prefix=f"{prefix}.mlp",
1235 )
1236 else:
1237 self.mlp = BailingMoeV3MLP(
1238 intermediate_size=config.intermediate_size,
1239 config=config,
1240 quant_config=quant_config,
1241 reduce_results=True,
1242 prefix=f"{prefix}.mlp",
1243 )
1244
1245 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
1246 self.post_attention_layernorm = RMSNorm(
1247 config.hidden_size, eps=config.rms_norm_eps
1248 )
1249
1250 def forward(
1251 self,
1252 hidden_states: torch.Tensor,
1253 positions: torch.Tensor,
1254 residual: torch.Tensor | None = None,
1255 ) -> tuple[torch.Tensor, torch.Tensor | None]:
1256 if residual is None:
1257 residual = hidden_states
1258 hidden_states = self.input_layernorm(hidden_states)
1259 else:
1260 hidden_states, residual = self.input_layernorm(hidden_states, residual)
1261
1262 if self.is_kda_layer:
1263 attn_output = torch.zeros_like(hidden_states)
1264 self.self_attn(hidden_states, positions, attn_output)
1265 else:
1266 attn_output = self.self_attn(hidden_states, positions)
1267
1268 hidden_states, residual = self.post_attention_layernorm(attn_output, residual)
1269 hidden_states = self.mlp(hidden_states)
1270 return hidden_states, residual
1271
1272
1273 @support_torch_compile(
1274 dynamic_arg_dims={
1275 "input_ids": 0,
1276 "positions": -1,
1277 "intermediate_tensors": 0,
1278 "inputs_embeds": 0,
1279 }
1280 )
1281 class BailingMoeV3Model(nn.Module):
1282 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
1283 super().__init__()
1284 config = vllm_config.model_config.hf_config
1285 model_config = vllm_config.model_config
1286 quant_config = vllm_config.quant_config
1287 cache_config = vllm_config.cache_config
1288 num_speculative_tokens = (
1289 vllm_config.speculative_config.num_speculative_tokens
1290 if vllm_config.speculative_config
1291 else 0
1292 )
1293
1294 self.config = config
1295 self.vocab_size = config.vocab_size
1296 self.embed_dim = config.hidden_size
1297 self.num_layers = config.num_hidden_layers
1298
1299 if get_pp_group().is_first_rank:
1300 self.word_embeddings = VocabParallelEmbedding(
1301 self.vocab_size,
1302 self.embed_dim,
1303 org_num_embeddings=self.vocab_size,
1304 )
1305 else:
1306 self.word_embeddings = PPMissingLayer()
1307
1308 def layer_fn(prefix: str):
1309 layer_idx = int(prefix.split(".")[-1])
1310 return BailingMoeV3DecoderLayer(
1311 config=config,
1312 quant_config=quant_config,
1313 layer_id=layer_idx,
1314 prefix=prefix,
1315 model_config=model_config,
1316 cache_config=cache_config,
1317 num_speculative_tokens=num_speculative_tokens,
1318 )
1319
1320 self.start_layer, self.end_layer, self.layers = make_layers(
1321 self.num_layers, layer_fn, prefix=f"{prefix}.layers"
1322 )
1323
1324 if get_pp_group().is_last_rank:
1325 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
1326 else:
1327 self.norm = PPMissingLayer()
1328
1329 @property
1330 def embed_tokens(self) -> nn.Module:
1331 return self.word_embeddings
1332
1333 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1334 return self.word_embeddings(input_ids)
1335
1336 def forward(
1337 self,
1338 input_ids: torch.Tensor | None,
1339 positions: torch.Tensor,
1340 intermediate_tensors: IntermediateTensors | None = None,
1341 inputs_embeds: torch.Tensor | None = None,
1342 ) -> torch.Tensor | IntermediateTensors:
1343 if get_pp_group().is_first_rank:
1344 hidden_states = (
1345 self.word_embeddings(input_ids)
1346 if inputs_embeds is None
1347 else inputs_embeds
1348 )
1349 residual = None
1350 else:
1351 assert intermediate_tensors is not None
1352 hidden_states = intermediate_tensors["hidden_states"]
1353 residual = intermediate_tensors["residual"]
1354
1355 for layer in self.layers[self.start_layer : self.end_layer]:
1356 hidden_states, residual = layer(hidden_states, positions, residual)
1357
1358 if not get_pp_group().is_last_rank:
1359 return IntermediateTensors(
1360 {"hidden_states": hidden_states, "residual": residual}
1361 )
1362 if residual is not None:
1363 hidden_states, _ = self.norm(hidden_states, residual)
1364 else:
1365 hidden_states = self.norm(hidden_states)
1366 return hidden_states
1367
1368 def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
1369 return fused_moe_make_expert_params_mapping(
1370 self,
1371 ckpt_gate_proj_name="gate_proj",
1372 ckpt_down_proj_name="down_proj",
1373 ckpt_up_proj_name="up_proj",
1374 num_experts=self.config.num_experts,
1375 )
1376
1377
1378 class BailingMoeV3ForCausalLM(nn.Module, HasInnerState, IsHybrid, SupportsPP):
1379 hf_to_vllm_mapper = WeightsMapper(orig_to_new_substr={".attention.": ".self_attn."})
1380
1381 packed_modules_mapping = {
1382 "gate_up_proj": ["gate_proj", "up_proj"],
1383 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
1384 "qkvb_proj": ["q_proj", "k_proj", "v_proj", "b_proj"],
1385 }
1386
1387 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
1388 super().__init__()
1389 config = vllm_config.model_config.hf_config
1390 quant_config = vllm_config.quant_config
1391 _configure_ling_fp8_quant_config(quant_config, config)
1392 self.config = config
1393 self.quant_config = quant_config
1394 self.model = BailingMoeV3Model(
1395 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
1396 )
1397 if get_pp_group().is_last_rank:
1398 self.lm_head = ParallelLMHead(
1399 config.vocab_size,
1400 config.hidden_size,
1401 quant_config=quant_config,
1402 prefix=maybe_prefix(prefix, "lm_head"),
1403 )
1404 self.logits_processor = LogitsProcessor(config.vocab_size)
1405 else:
1406 self.lm_head = PPMissingLayer()
1407
1408 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1409 return self.model.embed_input_ids(input_ids)
1410
1411 def forward(
1412 self,
1413 input_ids: torch.Tensor | None,
1414 positions: torch.Tensor,
1415 intermediate_tensors: IntermediateTensors | None = None,
1416 inputs_embeds: torch.Tensor | None = None,
1417 ) -> torch.Tensor | IntermediateTensors:
1418 return self.model(input_ids, positions, intermediate_tensors, inputs_embeds)
1419
1420 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
1421 return self.logits_processor(self.lm_head, hidden_states)
1422
1423 def make_empty_intermediate_tensors(
1424 self, batch_size: int, dtype: torch.dtype, device: torch.device
1425 ) -> IntermediateTensors:
1426 return IntermediateTensors(
1427 {
1428 "hidden_states": torch.zeros(
1429 (batch_size, self.config.hidden_size), dtype=dtype, device=device
1430 ),
1431 "residual": torch.zeros(
1432 (batch_size, self.config.hidden_size), dtype=dtype, device=device
1433 ),
1434 }
1435 )
1436
1437 @classmethod
1438 def get_mamba_state_shape_from_config(
1439 cls, vllm_config: VllmConfig
1440 ) -> tuple[tuple[int, int], tuple[int, int, int]]:
1441 return _get_kda_state_shape_for_config(vllm_config)
1442
1443 @classmethod
1444 def get_mamba_state_dtype_from_config(
1445 cls, vllm_config: VllmConfig
1446 ) -> tuple[torch.dtype, torch.dtype]:
1447 return MambaStateDtypeCalculator.kda_state_dtype(
1448 vllm_config.model_config.dtype,
1449 vllm_config.cache_config.mamba_cache_dtype,
1450 )
1451
1452 @classmethod
1453 def get_mamba_state_copy_func(cls) -> tuple:
1454 return MambaStateCopyFuncCalculator.kda_state_copy_func()
1455
1456 def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
1457 return self.model.get_expert_mapping()
1458
1459 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1460 weights = self.hf_to_vllm_mapper.apply(weights)
1461 weights = _maybe_remap_ling_mxfp4_weight_names(weights, self.quant_config)
1462 params_dict = dict(self.named_parameters(remove_duplicate=False))
1463 loaded_params: set[str] = set()
1464 stacked_mappings = [
1465 (".gate_up_proj", ".gate_proj", 0),
1466 (".gate_up_proj", ".up_proj", 1),
1467 (".qkv_proj", ".q_proj", 0),
1468 (".qkv_proj", ".k_proj", 1),
1469 (".qkv_proj", ".v_proj", 2),
1470 (".qkvb_proj", ".q_proj", 0),
1471 (".qkvb_proj", ".k_proj", 1),
1472 (".qkvb_proj", ".v_proj", 2),
1473 (".qkvb_proj", ".b_proj", 3),
1474 (".fused_qkv_a_proj", ".q_a_proj", 0),
1475 (".fused_qkv_a_proj", ".kv_a_proj_with_mqa", 1),
1476 ]
1477 expert_mappings = list(self.get_expert_mapping())
1478
1479 def load_param(name: str, tensor: torch.Tensor, shard_id=None) -> bool:
1480 if name not in params_dict or is_pp_missing_parameter(name, self):
1481 return False
1482 param = params_dict[name]
1483 weight_loader: Callable[..., None] = getattr(
1484 param, "weight_loader", default_weight_loader
1485 )
1486 if shard_id is None:
1487 weight_loader(param, tensor)
1488 elif isinstance(shard_id, int):
1489 weight_loader(param, tensor, shard_id)
1490 else:
1491 weight_loader(
1492 param, tensor, name, expert_id=shard_id[0], shard_id=shard_id[1]
1493 )
1494 loaded_params.add(name)
1495 return True
1496
1497 def normalize_name(name: str) -> str | None:
1498 if name.startswith("model.layers."):
1499 layer_idx = int(name.split("model.layers.")[1].split(".")[0])
1500 if layer_idx >= self.config.num_hidden_layers:
1501 return None
1502 return maybe_remap_kv_scale_name(name, params_dict)
1503
1504 for orig_name, weight in weights:
1505 name = normalize_name(orig_name)
1506 if name is None:
1507 continue
1508 weight = _maybe_pad_block_fp8_shared_expert_checkpoint_tensor(
1509 self.quant_config,
1510 self.config,
1511 name,
1512 weight,
1513 )
1514 loaded = False
1515 for param_suf, weight_suf, stacked_shard_id in stacked_mappings:
1516 if weight_suf in name:
1517 mapped = name.replace(weight_suf, param_suf)
1518 if load_param(mapped, weight, stacked_shard_id):
1519 loaded = True
1520 break
1521 if loaded:
1522 continue
1523
1524 if ".mlp.experts." in name:
1525 for param_name, weight_name, expert_id, shard_id in expert_mappings:
1526 if weight_name in name:
1527 mapped = name.replace(weight_name, param_name)
1528 load_param(mapped, weight, (expert_id, shard_id))
1529 break
1530 continue
1531
1532 load_param(name, weight)
1533 return loaded_params