main
py 755 lines 26.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 #
4 # Copyright 2026 Sarvam AI team. All rights reserved.
5 #
6 # This code is based on Llama, Deepseek, and Bailing MoE implementations
7 # in this library. It has been modified from its original forms to
8 # accommodate Sarvam's MoE architectures.
9 #
10 # Licensed under the Apache License, Version 2.0 (the "License");
11 # you may not use this file except in compliance with the License.
12 # You may obtain a copy of the License at
13 #
14 # http://www.apache.org/licenses/LICENSE-2.0
15 #
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS,
18 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 # See the License for the specific language governing permissions and
20 # limitations under the License.
21
22 from __future__ import annotations
23
24 import math
25 from collections.abc import Iterable, Iterator
26 from itertools import islice
27
28 import torch
29 from torch import nn
30
31 from vllm.compilation.decorators import support_torch_compile
32 from vllm.config import CacheConfig, ParallelConfig, VllmConfig
33 from vllm.distributed import (
34 get_pp_group,
35 get_tensor_model_parallel_rank,
36 get_tensor_model_parallel_world_size,
37 )
38 from vllm.model_executor.layers.activation import SiluAndMul
39 from vllm.model_executor.layers.fused_moe import (
40 FusedMoEFactory,
41 GateLinear,
42 MoERunner,
43 )
44 from vllm.model_executor.layers.layernorm import RMSNorm
45 from vllm.model_executor.layers.linear import (
46 ColumnParallelLinear,
47 MergedColumnParallelLinear,
48 ReplicatedLinear,
49 RowParallelLinear,
50 )
51 from vllm.model_executor.layers.logits_processor import LogitsProcessor
52 from vllm.model_executor.layers.mla import MLAModules, MultiHeadLatentAttentionWrapper
53 from vllm.model_executor.layers.quantization import QuantizationConfig
54 from vllm.model_executor.layers.rotary_embedding import get_rope
55 from vllm.model_executor.layers.vocab_parallel_embedding import (
56 ParallelLMHead,
57 VocabParallelEmbedding,
58 )
59 from vllm.sequence import IntermediateTensors
60
61 from .bailing_moe import BailingMoeForCausalLM
62 from .interfaces import (
63 EagleModelMixin,
64 MixtureOfExperts,
65 SupportsEagle3,
66 SupportsLoRA,
67 SupportsPP,
68 )
69 from .utils import (
70 AutoWeightsLoader,
71 PPMissingLayer,
72 WeightsMapper,
73 make_empty_intermediate_tensors_factory,
74 make_layers,
75 maybe_prefix,
76 spec_decode_needs_target_embed,
77 )
78
79
80 def yarn_get_mscale(scale: float = 1, mscale: float = 1) -> float:
81 if scale <= 1:
82 return 1.0
83 return 0.1 * mscale * math.log(scale) + 1.0
84
85
86 def _is_gate_expert_bias_name(name: str) -> bool:
87 return name.endswith(".mlp.gate.e_score_correction_bias") or name.endswith(
88 ".gate.e_score_correction_bias"
89 )
90
91
92 def _zero_mean_tensor(t: torch.Tensor) -> torch.Tensor:
93 if t.numel() == 0:
94 return t
95 return t - t.mean()
96
97
98 def _normalized_weights(
99 weights: Iterable[tuple[str, torch.Tensor]],
100 ) -> Iterator[tuple[str, torch.Tensor]]:
101 for name, w in weights:
102 if _is_gate_expert_bias_name(name):
103 yield name, _zero_mean_tensor(w)
104 else:
105 yield name, w
106
107
108 class SarvamMLAAttention(nn.Module):
109 def __init__(
110 self,
111 vllm_config: VllmConfig,
112 config,
113 cache_config: CacheConfig | None = None,
114 quant_config: QuantizationConfig | None = None,
115 prefix: str = "",
116 ) -> None:
117 super().__init__()
118
119 self.config = config
120 self.hidden_size = config.hidden_size
121 self.qk_nope_head_dim = config.qk_nope_head_dim
122 self.qk_rope_head_dim = config.qk_rope_head_dim
123 self.qk_head_dim = self.qk_nope_head_dim + self.qk_rope_head_dim
124 self.v_head_dim = config.v_head_dim
125
126 self.q_lora_rank = getattr(config, "q_lora_rank", None)
127 self.kv_lora_rank = config.kv_lora_rank
128
129 self.total_num_heads = config.num_attention_heads
130 tp_size = get_tensor_model_parallel_world_size()
131 assert self.total_num_heads % tp_size == 0
132 self.num_local_heads = self.total_num_heads // tp_size
133
134 self.scaling = self.qk_head_dim**-0.5
135 self.max_position_embeddings = config.max_position_embeddings
136
137 if self.q_lora_rank is not None:
138 self.q_a_proj = ReplicatedLinear(
139 self.hidden_size,
140 self.q_lora_rank,
141 bias=False,
142 quant_config=quant_config,
143 prefix=f"{prefix}.q_a_proj",
144 )
145 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
146 self.q_b_proj = ColumnParallelLinear(
147 self.q_lora_rank,
148 self.total_num_heads * self.qk_head_dim,
149 bias=False,
150 quant_config=quant_config,
151 prefix=f"{prefix}.q_b_proj",
152 )
153 self.q_proj = None # type: ignore
154 else:
155 self.q_proj = ColumnParallelLinear(
156 self.hidden_size,
157 self.total_num_heads * self.qk_head_dim,
158 bias=False,
159 quant_config=quant_config,
160 prefix=f"{prefix}.q_proj",
161 )
162 self.q_a_proj = None # type: ignore
163 self.q_a_layernorm = None # type: ignore
164 self.q_b_proj = None # type: ignore
165
166 # KV latent (MQA-style) A-proj
167 self.kv_a_proj_with_mqa = ReplicatedLinear(
168 self.hidden_size,
169 self.kv_lora_rank + self.qk_rope_head_dim,
170 bias=False,
171 quant_config=quant_config,
172 prefix=f"{prefix}.kv_a_proj_with_mqa",
173 )
174 self.kv_a_layernorm = RMSNorm(self.kv_lora_rank, eps=config.rms_norm_eps)
175
176 # KV B-proj produces per-head K_nope and V
177 self.kv_b_proj = ColumnParallelLinear(
178 self.kv_lora_rank,
179 self.total_num_heads * (self.qk_nope_head_dim + self.v_head_dim),
180 bias=False,
181 quant_config=quant_config,
182 prefix=f"{prefix}.kv_b_proj",
183 )
184
185 self.o_proj = RowParallelLinear(
186 self.total_num_heads * self.v_head_dim,
187 self.hidden_size,
188 bias=False,
189 quant_config=quant_config,
190 prefix=f"{prefix}.o_proj",
191 )
192
193 self.rotary_emb = get_rope(
194 self.qk_rope_head_dim,
195 # rotary_dim=self.qk_rope_head_dim,
196 max_position=config.max_position_embeddings,
197 rope_parameters=config.rope_parameters,
198 is_neox_style=False,
199 )
200
201 if config.rope_parameters.get("rope_type", None) == "deepseek_yarn":
202 mscale_all_dim = config.rope_parameters.get("mscale_all_dim", False)
203 scaling_factor = config.rope_parameters["factor"]
204 mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
205 self.scaling = self.scaling * mscale * mscale
206
207 mla_modules = MLAModules(
208 kv_a_layernorm=self.kv_a_layernorm,
209 kv_b_proj=self.kv_b_proj,
210 rotary_emb=self.rotary_emb,
211 o_proj=self.o_proj,
212 fused_qkv_a_proj=None,
213 kv_a_proj_with_mqa=self.kv_a_proj_with_mqa,
214 q_a_layernorm=self.q_a_layernorm if self.q_lora_rank is not None else None,
215 q_b_proj=self.q_b_proj if self.q_lora_rank is not None else None,
216 q_proj=self.q_proj if self.q_lora_rank is None else None,
217 indexer=None,
218 indexer_rotary_emb=None,
219 is_sparse=False,
220 topk_indices_buffer=None,
221 )
222
223 self.mla_attn = MultiHeadLatentAttentionWrapper(
224 self.hidden_size,
225 self.num_local_heads,
226 self.scaling,
227 self.qk_nope_head_dim,
228 self.qk_rope_head_dim,
229 self.v_head_dim,
230 self.q_lora_rank,
231 self.kv_lora_rank,
232 mla_modules,
233 cache_config=cache_config,
234 quant_config=quant_config,
235 prefix=prefix,
236 )
237
238 def forward(
239 self,
240 positions: torch.Tensor,
241 hidden_states: torch.Tensor,
242 ) -> torch.Tensor:
243 return self.mla_attn(positions, hidden_states, llama_4_scaling=None)
244
245
246 class SarvamMLAMLP(nn.Module):
247 def __init__(
248 self,
249 intermediate_size: int,
250 config,
251 quant_config: QuantizationConfig | None = None,
252 reduce_results: bool = True,
253 prefix: str = "",
254 ) -> None:
255 super().__init__()
256
257 self.gate_up_proj = MergedColumnParallelLinear(
258 config.hidden_size,
259 [intermediate_size] * 2,
260 bias=False,
261 quant_config=quant_config,
262 prefix=f"{prefix}.gate_up_proj",
263 )
264 self.down_proj = RowParallelLinear(
265 intermediate_size,
266 config.hidden_size,
267 bias=False,
268 quant_config=quant_config,
269 reduce_results=reduce_results,
270 prefix=f"{prefix}.down_proj",
271 )
272 self.act_fn = SiluAndMul()
273
274 def forward(self, x: torch.Tensor) -> torch.Tensor:
275 gate_up, _ = self.gate_up_proj(x)
276 x = self.act_fn(gate_up)
277 x, _ = self.down_proj(x)
278 return x
279
280
281 class SarvamMLAMoE(nn.Module):
282 def __init__(
283 self,
284 config,
285 parallel_config: ParallelConfig,
286 quant_config: QuantizationConfig | None = None,
287 prefix: str = "",
288 ) -> None:
289 super().__init__()
290
291 self.config = config
292 self.tp_size = get_tensor_model_parallel_world_size()
293 self.tp_rank = get_tensor_model_parallel_rank()
294 self.hidden_size = config.hidden_size
295
296 self.num_experts = config.num_experts
297 self.top_k = config.num_experts_per_tok
298 self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 2.5)
299
300 self.n_group = getattr(config, "n_group", None)
301 self.topk_group = getattr(config, "topk_group", None)
302 self.use_grouped_topk = self.n_group is not None and self.topk_group is not None
303
304 self.norm_expert_prob = getattr(config, "norm_topk_prob", True)
305
306 router_dtype_cfg = getattr(config, "router_dtype", "fp32")
307 if router_dtype_cfg is None:
308 self.router_dtype = None
309 elif router_dtype_cfg == "fp32":
310 self.router_dtype = torch.float32
311 else:
312 self.router_dtype = torch.bfloat16
313
314 self.gate = GateLinear(
315 self.hidden_size,
316 self.num_experts,
317 out_dtype=self.router_dtype,
318 prefix=f"{prefix}.gate",
319 )
320
321 if getattr(config, "moe_router_enable_expert_bias", True):
322 self.gate.e_score_correction_bias = nn.Parameter(
323 torch.empty(
324 (self.num_experts,),
325 dtype=torch.float32,
326 )
327 )
328 else:
329 self.gate.e_score_correction_bias = None
330
331 self.score_function = getattr(config, "score_function", "sigmoid")
332 self.num_shared_experts = getattr(config, "num_shared_experts", 1)
333 self.shared_experts: SarvamMLAMLP | None
334 if self.num_shared_experts > 0:
335 if hasattr(config, "moe_shared_expert_intermediate_size"):
336 shared_int = config.moe_shared_expert_intermediate_size
337 else:
338 shared_int = config.moe_intermediate_size
339 shared_int *= self.num_shared_experts
340 self.shared_experts = SarvamMLAMLP(
341 intermediate_size=shared_int,
342 config=config,
343 quant_config=quant_config,
344 reduce_results=False,
345 prefix=f"{prefix}.shared_experts",
346 )
347 else:
348 self.shared_experts = None
349
350 self.experts = FusedMoEFactory(
351 shared_experts=self.shared_experts,
352 num_experts=self.num_experts,
353 top_k=self.top_k,
354 hidden_size=self.hidden_size,
355 intermediate_size=config.moe_intermediate_size,
356 renormalize=self.norm_expert_prob,
357 quant_config=quant_config,
358 prefix=f"{prefix}.experts",
359 scoring_func=self.score_function,
360 e_score_correction_bias=self.gate.e_score_correction_bias,
361 num_expert_group=self.n_group,
362 topk_group=self.topk_group,
363 use_grouped_topk=self.use_grouped_topk,
364 routed_scaling_factor=self.routed_scaling_factor,
365 )
366
367 def maybe_get_fused_moe(self) -> MoERunner:
368 return self.experts
369
370 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
371 num_tokens, hidden_dim = hidden_states.shape
372 hidden_states = hidden_states.view(-1, hidden_dim)
373 router_logits, _ = self.gate(hidden_states)
374 final_hidden = self.experts(
375 hidden_states=hidden_states,
376 router_logits=router_logits,
377 )
378
379 return final_hidden.view(num_tokens, hidden_dim)
380
381
382 class SarvamMLABlock(nn.Module):
383 def __init__(
384 self,
385 vllm_config: VllmConfig,
386 prefix: str = "",
387 ) -> None:
388 super().__init__()
389 config = vllm_config.model_config.hf_config
390 cache_config = vllm_config.cache_config
391 quant_config = vllm_config.quant_config
392 parallel_config = vllm_config.parallel_config
393 layer_idx = int(prefix.split(".")[-1])
394 hidden_size = config.hidden_size
395 dense_intermediate = getattr(config, "intermediate_size", 16384)
396
397 self.input_layernorm = RMSNorm(hidden_size, eps=config.rms_norm_eps)
398 self.self_attn = SarvamMLAAttention(
399 vllm_config=vllm_config,
400 config=config,
401 cache_config=cache_config,
402 quant_config=quant_config,
403 prefix=f"{prefix}.self_attn",
404 )
405 self.post_attention_layernorm = RMSNorm(hidden_size, eps=config.rms_norm_eps)
406 use_moe = hasattr(config, "num_experts") and config.num_experts is not None
407 first_k_dense = getattr(config, "first_k_dense_replace", 1)
408 moe_layer_freq = getattr(config, "moe_layer_freq", 1)
409 if use_moe:
410 is_moe_layer = layer_idx >= first_k_dense and (
411 (layer_idx - first_k_dense) % moe_layer_freq == 0
412 )
413 else:
414 is_moe_layer = False
415
416 if is_moe_layer:
417 self.mlp = SarvamMLAMoE(
418 config=config,
419 parallel_config=parallel_config,
420 quant_config=quant_config,
421 prefix=f"{prefix}.mlp",
422 )
423 else:
424 self.mlp = SarvamMLAMLP(
425 intermediate_size=dense_intermediate,
426 config=config,
427 quant_config=quant_config,
428 reduce_results=True,
429 prefix=f"{prefix}.mlp",
430 )
431
432 def forward(
433 self,
434 hidden_states: torch.Tensor,
435 positions: torch.Tensor,
436 residual: torch.Tensor | None,
437 ) -> tuple[torch.Tensor, torch.Tensor]:
438 if residual is None:
439 residual = hidden_states
440 hidden_states = self.input_layernorm(hidden_states)
441 else:
442 hidden_states, residual = self.input_layernorm(hidden_states, residual)
443
444 hidden_states = self.self_attn(
445 positions=positions,
446 hidden_states=hidden_states,
447 )
448 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
449 hidden_states = self.mlp(hidden_states)
450 return hidden_states, residual
451
452
453 # PEP 563 stringifies annotations in this module, so the decorator cannot infer
454 # these; they are the set DeepSeek-V2 infers for the same forward signature.
455 @support_torch_compile(
456 dynamic_arg_dims={
457 "input_ids": 0,
458 "positions": 0,
459 "intermediate_tensors": 0,
460 "inputs_embeds": 0,
461 }
462 )
463 class SarvamMLAModel(nn.Module, EagleModelMixin):
464 """Sarvam MLA backbone with EAGLE3 auxiliary capture across pipeline stages."""
465
466 supports_aux_hidden_states_over_pp = True
467
468 hf_to_vllm_mapper = WeightsMapper(
469 orig_to_new_stacked={
470 # .experts.gate_up_proj must be handled by MoERunner.load_weights for EP
471 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
472 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
473 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
474 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
475 }
476 )
477
478 def __init__(
479 self,
480 *,
481 vllm_config: VllmConfig,
482 prefix: str = "",
483 ) -> None:
484 super().__init__()
485
486 config = vllm_config.model_config.hf_config
487 quant_config = vllm_config.quant_config
488
489 self.config = config
490 self.vocab_size = config.vocab_size
491 self.embed_dim = config.hidden_size
492 self.tie_word_embeddings = getattr(config, "tie_word_embeddings", False)
493 if (
494 get_pp_group().is_first_rank
495 or (self.tie_word_embeddings and get_pp_group().is_last_rank)
496 or spec_decode_needs_target_embed(vllm_config)
497 ):
498 self.embed_tokens = VocabParallelEmbedding(
499 self.vocab_size,
500 self.embed_dim,
501 quant_config=quant_config,
502 prefix=f"{prefix}.embed_tokens",
503 )
504 else:
505 self.embed_tokens = PPMissingLayer()
506
507 self.embedding_dropout = torch.nn.Dropout(
508 getattr(config, "embedding_dropout", 0.0)
509 )
510 self.start_layer, self.end_layer, self.layers = make_layers(
511 config.num_hidden_layers,
512 lambda prefix: SarvamMLABlock(
513 vllm_config=vllm_config,
514 prefix=prefix,
515 ),
516 prefix=f"{prefix}.layers",
517 )
518 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
519 ["hidden_states", "residual"], config.hidden_size
520 )
521 if get_pp_group().is_last_rank:
522 self.norm = RMSNorm(self.embed_dim, eps=config.rms_norm_eps)
523 else:
524 self.norm = PPMissingLayer()
525
526 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
527 return self.embed_tokens(input_ids)
528
529 def forward(
530 self,
531 input_ids: torch.Tensor,
532 positions: torch.Tensor,
533 intermediate_tensors: IntermediateTensors | None,
534 inputs_embeds: torch.Tensor | None = None,
535 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
536 """Run this stage and optionally return its auxiliary hidden states.
537
538 Returns:
539 Intermediate tensors with local auxiliary states on non-final stages.
540 On the final stage, normalized hidden states paired with the ordered
541 auxiliary states from all stages if captured.
542
543 """
544 if get_pp_group().is_first_rank:
545 if inputs_embeds is not None:
546 hidden_states = inputs_embeds
547 else:
548 hidden_states = self.embed_input_ids(input_ids)
549 hidden_states = self.embedding_dropout(hidden_states)
550 residual = None
551 else:
552 assert intermediate_tensors is not None
553 hidden_states = intermediate_tensors["hidden_states"]
554 residual = intermediate_tensors["residual"]
555
556 remote_aux = self.collect_remote_aux_hidden_states(intermediate_tensors)
557 aux_hidden_states: list[torch.Tensor] = []
558 if get_pp_group().is_first_rank:
559 self._maybe_add_hidden_state(
560 aux_hidden_states, self.start_layer, hidden_states, residual
561 )
562 for layer_idx, layer in enumerate(
563 islice(self.layers, self.start_layer, self.end_layer),
564 start=self.start_layer,
565 ):
566 hidden_states, residual = layer(
567 hidden_states,
568 positions,
569 residual,
570 )
571 self._maybe_add_hidden_state(
572 aux_hidden_states, layer_idx + 1, hidden_states, residual
573 )
574
575 if not get_pp_group().is_last_rank:
576 return IntermediateTensors(
577 {
578 "hidden_states": hidden_states,
579 "residual": residual,
580 **self.pack_local_aux_hidden_states(aux_hidden_states),
581 }
582 )
583 if residual is None:
584 hidden_states = self.norm(hidden_states)
585 else:
586 hidden_states, _ = self.norm(hidden_states, residual)
587
588 aux_hidden_states = remote_aux + aux_hidden_states
589 if len(aux_hidden_states) > 0:
590 return hidden_states, aux_hidden_states
591 return hidden_states
592
593 def load_weights(
594 self,
595 weights: Iterable[tuple[str, torch.Tensor]],
596 ) -> set[str]:
597 loader = AutoWeightsLoader(self)
598 return loader.load_weights(
599 _normalized_weights(weights), mapper=self.hf_to_vllm_mapper
600 )
601
602
603 class SarvamMixtureOfExperts(MixtureOfExperts):
604 moe_layers: list[MoERunner]
605 moe_mlp_layers: list[SarvamMLAMoE]
606
607 def extract_moe_parameters(self, example_moe: SarvamMLAMoE | None) -> None:
608 if example_moe is None:
609 raise RuntimeError("No SarvamMLAMoE layer found in model.layers.")
610
611 self.num_logical_experts = example_moe.num_experts
612 self.num_routed_experts = example_moe.num_experts # routed pool size
613 self.num_shared_experts = getattr(example_moe.config, "num_shared_experts", 1)
614
615 self.num_physical_experts = self.num_logical_experts
616 self.num_local_physical_experts = self.num_logical_experts
617 self.num_redundant_experts = 0
618
619 def update_physical_experts_metadata(
620 self,
621 num_physical_experts: int,
622 num_local_physical_experts: int,
623 ) -> None:
624 self.num_physical_experts = num_physical_experts
625 self.num_local_physical_experts = num_local_physical_experts
626 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
627
628 for moe in self.moe_mlp_layers:
629 moe.n_physical_experts = num_physical_experts
630 moe.n_local_physical_experts = num_local_physical_experts
631 moe.n_redundant_experts = self.num_redundant_experts
632
633 fused = moe.experts
634 if hasattr(fused, "n_local_physical_experts"):
635 fused.n_local_physical_experts = num_local_physical_experts
636 if hasattr(fused, "n_physical_experts"):
637 fused.n_physical_experts = num_physical_experts
638 if hasattr(fused, "n_redundant_experts"):
639 fused.n_redundant_experts = self.num_redundant_experts
640 if hasattr(fused, "update_expert_map"):
641 fused.update_expert_map()
642
643
644 class SarvamMLAForCausalLM(
645 nn.Module, SupportsPP, SupportsLoRA, SupportsEagle3, SarvamMixtureOfExperts
646 ):
647 packed_modules_mapping = {
648 "q_proj": ["q_proj"],
649 "q_a_proj": ["q_a_proj"],
650 "q_b_proj": ["q_b_proj"],
651 "kv_a_proj_with_mqa": ["kv_a_proj_with_mqa"],
652 "kv_b_proj": ["kv_b_proj"],
653 "gate_up_proj": ["gate_proj", "up_proj"],
654 }
655
656 @staticmethod
657 def _remap_config(config) -> None:
658 """Default the routing keys the released checkpoints omit."""
659 defaults = {
660 "n_group": 1,
661 "topk_group": 1,
662 }
663 for attr, default in defaults.items():
664 if getattr(config, attr, None) is None:
665 setattr(config, attr, default)
666
667 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
668 super().__init__()
669 config = vllm_config.model_config.hf_config
670 self._remap_config(config)
671 quant_config = vllm_config.quant_config
672 self.config = config
673 self.quant_config = quant_config
674
675 self.model = SarvamMLAModel(
676 vllm_config=vllm_config,
677 prefix=maybe_prefix(prefix, "model"),
678 )
679
680 self.tie_word_embeddings = getattr(config, "tie_word_embeddings", False)
681 if get_pp_group().is_last_rank:
682 self.lm_head = ParallelLMHead(
683 config.vocab_size,
684 config.hidden_size,
685 quant_config=quant_config,
686 prefix=maybe_prefix(prefix, "lm_head"),
687 )
688 if self.tie_word_embeddings:
689 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
690 self.logits_processor = LogitsProcessor(config.vocab_size)
691 else:
692 self.lm_head = PPMissingLayer()
693 self.logits_processor = None # type: ignore
694
695 self.make_empty_intermediate_tensors = (
696 self.model.make_empty_intermediate_tensors
697 )
698
699 self.num_moe_layers = 0
700
701 self.moe_layers = []
702 self.moe_mlp_layers = []
703
704 example_moe = None
705 for layer in self.model.layers:
706 if isinstance(layer, PPMissingLayer):
707 continue
708 if isinstance(layer.mlp, SarvamMLAMoE):
709 example_moe = layer.mlp
710 self.moe_mlp_layers.append(layer.mlp)
711 self.moe_layers.append(layer.mlp.experts)
712 self.num_moe_layers += 1
713
714 self.extract_moe_parameters(example_moe)
715
716 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
717 return self.model.embed_input_ids(input_ids)
718
719 def forward(
720 self,
721 input_ids: torch.Tensor,
722 positions: torch.Tensor,
723 intermediate_tensors: IntermediateTensors | None = None,
724 inputs_embeds: torch.Tensor | None = None,
725 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
726 """Return backbone outputs, including auxiliary states when captured."""
727 return self.model(
728 input_ids=input_ids,
729 positions=positions,
730 intermediate_tensors=intermediate_tensors,
731 inputs_embeds=inputs_embeds,
732 )
733
734 def compute_logits(
735 self,
736 hidden_states: torch.Tensor,
737 ) -> torch.Tensor | None:
738 if not get_pp_group().is_last_rank:
739 return None
740 logits = self.logits_processor(self.lm_head, hidden_states)
741 return logits
742
743 def load_weights(
744 self,
745 weights: Iterable[tuple[str, torch.Tensor]],
746 ) -> set[str]:
747 loader = AutoWeightsLoader(self)
748 return loader.load_weights(weights)
749
750
751 class SarvamMoEForCausalLM(BailingMoeForCausalLM):
752 """Same as BailingMoeForCausalLM, but normalizes gate expert_bias pre-load."""
753
754 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
755 return super().load_weights(_normalized_weights(weights))