33
from transformers import DeepseekV2Config, DeepseekV3Config
34
35
from vllm.attention import Attention
36
+from vllm.attention.backends.abstract import AttentionBackend
37
+from vllm.attention.ops.common import pack_seq_triton, unpack_seq_triton
38
from vllm.compilation.decorators import support_torch_compile
37
-from vllm.config import CacheConfig, ParallelConfig, VllmConfig
39
+from vllm.config import (CacheConfig, ParallelConfig, VllmConfig,
40
+ get_current_vllm_config)
41
from vllm.distributed import (get_ep_group, get_pp_group,
42
get_tensor_model_parallel_rank,
43
get_tensor_model_parallel_world_size,
44
tensor_model_parallel_all_gather)
45
+from vllm.forward_context import get_forward_context
46
+from vllm.logger import init_logger
47
from vllm.model_executor.layers.activation import SiluAndMul
48
+from vllm.model_executor.layers.attention_layer_base import AttentionLayerBase
49
from vllm.model_executor.layers.fused_moe import FusedMoE
44
-from vllm.model_executor.layers.layernorm import RMSNorm
50
+from vllm.model_executor.layers.layernorm import LayerNorm, RMSNorm
51
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
52
MergedColumnParallelLinear,
53
ReplicatedLinear,
55
from vllm.model_executor.layers.logits_processor import LogitsProcessor
56
from vllm.model_executor.layers.mla import MLAModules, MultiHeadLatentAttention
57
from vllm.model_executor.layers.quantization import QuantizationConfig
58
+from vllm.model_executor.layers.quantization.utils.fp8_utils import (
59
+ per_token_group_quant_fp8)
60
from vllm.model_executor.layers.rotary_embedding import get_rope
61
from vllm.model_executor.layers.shared_fused_moe import SharedFusedMoE
62
from vllm.model_executor.layers.vocab_parallel_embedding import (
64
from vllm.model_executor.model_loader.weight_utils import (
65
default_weight_loader, maybe_remap_kv_scale_name)
66
from vllm.model_executor.models.utils import sequence_parallel_chunk
67
+from vllm.platforms import current_platform
68
from vllm.sequence import IntermediateTensors
69
+from vllm.utils import cdiv, direct_register_custom_op
70
+from vllm.utils.deep_gemm import fp8_mqa_logits, fp8_paged_mqa_logits
71
+from vllm.v1.attention.backends.mla.indexer import (DeepseekV32IndexerBackend,
72
+ DeepseekV32IndexerMetadata)
73
+from vllm.v1.kv_cache_interface import KVCacheSpec, MLAAttentionSpec
74
75
from .interfaces import MixtureOfExperts, SupportsLoRA, SupportsPP
76
from .utils import (PPMissingLayer, is_pp_missing_parameter,
77
make_empty_intermediate_tensors_factory, make_layers,
78
maybe_prefix)
79
80
+if current_platform.is_cuda_alike():
81
+ from vllm import _custom_ops as ops
82
+elif current_platform.is_xpu():
83
+ from vllm._ipex_ops import ipex_ops as ops
84
+
85
+logger = init_logger(__name__)
86
+
87
88
class DeepseekV2MLP(nn.Module):
89
297
298
def __init__(
299
self,
300
+ vllm_config: VllmConfig,
301
config: Union[DeepseekV2Config, DeepseekV3Config],
302
hidden_size: int,
303
num_heads: int,
311
max_position_embeddings: int = 8192,
312
cache_config: Optional[CacheConfig] = None,
313
quant_config: Optional[QuantizationConfig] = None,
314
+ topk_indices_buffer: Optional[torch.Tensor] = None,
315
prefix: str = "",
316
) -> None:
317
super().__init__()
329
self.scaling = self.qk_head_dim**-0.5
330
self.rope_theta = rope_theta
331
self.max_position_embeddings = max_position_embeddings
332
+ assert topk_indices_buffer is None, "topk_indices_buffer is not \
333
+ supported for DeepseekV2Attention"
334
335
if self.q_lora_rank is not None:
336
self.q_a_proj = ReplicatedLinear(self.hidden_size,
443
return output
444
445
446
+class DeepseekV32IndexerCache(torch.nn.Module, AttentionLayerBase):
447
+
448
+ def __init__(self, head_dim: int, dtype: torch.dtype, prefix: str,
449
+ cache_config: CacheConfig):
450
+ super().__init__()
451
+ self.kv_cache = [torch.tensor([])]
452
+ self.head_dim = head_dim
453
+ self.prefix = prefix
454
+ self.cache_config = cache_config
455
+ self.dtype = dtype
456
+ compilation_config = get_current_vllm_config().compilation_config
457
+ if prefix in compilation_config.static_forward_context:
458
+ raise ValueError(f"Duplicate layer name: {prefix}")
459
+ compilation_config.static_forward_context[prefix] = self
460
+
461
+ def get_kv_cache_spec(self) -> KVCacheSpec:
462
+ return MLAAttentionSpec( # Only has one vector instead of K + V
463
+ block_size=self.cache_config.block_size,
464
+ num_kv_heads=1,
465
+ head_size=self.head_dim,
466
+ dtype=self.dtype,
467
+ )
468
+
469
+ def forward(self):
470
+ ...
471
+
472
+ def get_attn_backend(self) -> AttentionBackend:
473
+ return DeepseekV32IndexerBackend
474
+
475
+
476
+@torch.inference_mode()
477
+def cp_gather_indexer_k_quant_cache(
478
+ kv_cache, # [num_blocks, block_size, head_dim + 1]
479
+ dst_value, # [cu_seq_lens[-1], head_dim]
480
+ dst_scale, # [cu_seq_lens[-1], 4]
481
+ block_table, # [batch_size, num_blocks]
482
+ cu_seq_lens, # [batch_size + 1, ]
483
+ batch_size,
484
+):
485
+ num_blocks, block_size, _ = kv_cache.shape
486
+ head_dim = dst_value.shape[-1]
487
+ kv_cache = kv_cache.view(num_blocks, -1)
488
+
489
+ expected_value = []
490
+ expected_scale = []
491
+ for b in range(batch_size):
492
+ s = cu_seq_lens[b + 1] - cu_seq_lens[b]
493
+ if s == 0:
494
+ continue
495
+ tot = cdiv(s, block_size)
496
+ blocks = block_table[b, :tot]
497
+
498
+ value = []
499
+ scale = []
500
+ full_block = torch.arange(tot - 1,
501
+ device=kv_cache.device,
502
+ dtype=torch.int32)
503
+ non_remaining_value = kv_cache[blocks[full_block], :block_size *
504
+ head_dim].view(-1, head_dim)
505
+ non_remaining_scale = kv_cache[blocks[full_block],
506
+ block_size * head_dim:].view(-1, 4)
507
+
508
+ remaining = s - (tot - 1) * block_size
509
+
510
+ value = torch.cat([
511
+ non_remaining_value,
512
+ kv_cache[blocks[-1], :remaining * head_dim].view(-1, head_dim)
513
+ ],
514
+ dim=0)
515
+ scale = torch.cat([
516
+ non_remaining_scale,
517
+ kv_cache[blocks[-1], block_size * head_dim:block_size * head_dim +
518
+ remaining * 4].view(-1, 4)
519
+ ],
520
+ dim=0)
521
+
522
+ expected_value.append(value)
523
+ expected_scale.append(scale)
524
+
525
+ gather_value = torch.cat(expected_value, dim=0).view(-1, head_dim)
526
+ gather_scale = torch.cat(expected_scale, dim=0).view(-1, 4)
527
+ gather_value = gather_value.view(torch.float8_e4m3fn)
528
+ gather_scale = gather_scale.view(torch.float32)
529
+ dst_value.copy_(gather_value)
530
+ dst_scale.copy_(gather_scale)
531
+
532
+
533
+def sparse_attn_indexer(
534
+ hidden_states: torch.Tensor,
535
+ k_cache_prefix: str,
536
+ kv_cache: torch.Tensor,
537
+ q_fp8: torch.Tensor,
538
+ k: torch.Tensor,
539
+ weights: torch.Tensor,
540
+ quant_block_size: int,
541
+ scale_fmt: Optional[str],
542
+ topk_tokens: int,
543
+ head_dim: int,
544
+ max_model_len: int,
545
+ total_seq_lens: int,
546
+ topk_indices_buffer: Optional[torch.Tensor],
547
+) -> torch.Tensor:
548
+
549
+ # careful! this will be None in dummy run
550
+ attn_metadata = get_forward_context().attn_metadata
551
+ # assert isinstance(attn_metadata, dict)
552
+ if not isinstance(attn_metadata, dict):
553
+ return sparse_attn_indexer_fake(
554
+ hidden_states,
555
+ k_cache_prefix,
556
+ kv_cache,
557
+ q_fp8,
558
+ k,
559
+ weights,
560
+ quant_block_size,
561
+ scale_fmt,
562
+ topk_tokens,
563
+ head_dim,
564
+ max_model_len,
565
+ total_seq_lens,
566
+ topk_indices_buffer,
567
+ )
568
+ attn_metadata = attn_metadata[k_cache_prefix]
569
+ assert isinstance(attn_metadata, DeepseekV32IndexerMetadata)
570
+ slot_mapping = attn_metadata.slot_mapping
571
+ has_decode = attn_metadata.num_decodes > 0
572
+ has_prefill = attn_metadata.num_prefills > 0
573
+ num_decode_tokens = attn_metadata.num_decode_tokens
574
+
575
+ ops.indexer_k_quant_and_cache(
576
+ k,
577
+ kv_cache,
578
+ slot_mapping,
579
+ quant_block_size,
580
+ scale_fmt,
581
+ )
582
+
583
+ topk_indices_buffer[:hidden_states.shape[0]] = -1
584
+ if has_prefill:
585
+ prefill_metadata = attn_metadata.prefill
586
+ num_prefills = attn_metadata.num_prefills
587
+ k_fp8 = torch.empty([prefill_metadata.total_seq_lens, head_dim],
588
+ device=k.device,
589
+ dtype=torch.float8_e4m3fn)
590
+ k_scale = torch.empty([prefill_metadata.total_seq_lens, 1],
591
+ device=k.device,
592
+ dtype=torch.float32)
593
+ cp_gather_indexer_k_quant_cache(
594
+ kv_cache,
595
+ k_fp8,
596
+ k_scale,
597
+ prefill_metadata.block_table,
598
+ prefill_metadata.cu_seq_lens,
599
+ num_prefills,
600
+ )
601
+ cu_seqlen_ks = prefill_metadata.cu_seqlen_ks
602
+ cu_seqlen_ke = prefill_metadata.cu_seqlen_ke
603
+ num_tokens = attn_metadata.num_actual_tokens
604
+ logits = fp8_mqa_logits(
605
+ q_fp8[num_decode_tokens:num_tokens],
606
+ (k_fp8, k_scale),
607
+ weights[num_decode_tokens:num_tokens],
608
+ cu_seqlen_ks,
609
+ cu_seqlen_ke,
610
+ )
611
+ topk_indices = logits.topk(min(topk_tokens, logits.shape[-1]),
612
+ dim=-1)[1]
613
+ topk_indices -= cu_seqlen_ks[:, None]
614
+ mask_lo = topk_indices >= 0
615
+ mask_hi = topk_indices - (cu_seqlen_ke - cu_seqlen_ks)[:, None] < 0
616
+ mask = torch.full_like(topk_indices,
617
+ False,
618
+ dtype=torch.bool,
619
+ device=topk_indices.device)
620
+ mask = mask_lo & mask_hi
621
+ topk_indices = topk_indices.masked_fill(~mask, -1)
622
+ topk_indices_buffer[num_decode_tokens:num_tokens, :topk_indices.
623
+ shape[-1]] = topk_indices.to(dtype=torch.int32)
624
+
625
+ if has_decode:
626
+ decode_metadata = attn_metadata.decode
627
+ # kv_cache size requirement [num_block, block_size, n_head, head_dim],
628
+ # we only have [num_block, block_size, head_dim],
629
+ kv_cache = kv_cache.unsqueeze(-2)
630
+ decode_lens = decode_metadata.decode_lens
631
+ if decode_metadata.requires_padding:
632
+ # pad in edge case where we have short chunked prefill length <
633
+ # decode_threshold since we unstrictly split
634
+ # prefill and decode by decode_threshold
635
+ # (currently set to 1 + speculative tokens)
636
+ padded_q_fp8_decode_tokens = pack_seq_triton(
637
+ q_fp8[:num_decode_tokens], decode_lens)
638
+ else:
639
+ padded_q_fp8_decode_tokens = q_fp8[:num_decode_tokens].reshape(
640
+ decode_lens.shape[0], -1, *q_fp8.shape[1:])
641
+ # TODO: move and optimize below logic with triton kernels
642
+ batch_size = padded_q_fp8_decode_tokens.shape[0]
643
+ next_n = padded_q_fp8_decode_tokens.shape[1]
644
+ assert batch_size == decode_metadata.seq_lens.shape[0]
645
+ num_padded_tokens = batch_size * next_n
646
+ logits = fp8_paged_mqa_logits(
647
+ padded_q_fp8_decode_tokens,
648
+ kv_cache,
649
+ weights[:num_padded_tokens],
650
+ decode_metadata.seq_lens,
651
+ decode_metadata.block_table,
652
+ decode_metadata.schedule_metadata,
653
+ max_model_len=max_model_len,
654
+ )
655
+ # padded query len
656
+ current_device = padded_q_fp8_decode_tokens.device
657
+ padded_num_tokens = batch_size * next_n
658
+ positions = torch.arange(max_model_len,
659
+ device=current_device).unsqueeze(0).expand(
660
+ batch_size * next_n, -1)
661
+ row_indices = torch.arange(padded_num_tokens,
662
+ device=current_device) // next_n
663
+ next_n_offset = torch.arange(
664
+ padded_num_tokens,
665
+ device=padded_q_fp8_decode_tokens.device) % next_n
666
+ index_end_pos = (decode_metadata.seq_lens[row_indices] - next_n +
667
+ next_n_offset).unsqueeze(1)
668
+ # index_end_pos: [B * N, 1]
669
+ mask = positions <= index_end_pos
670
+ # mask: [B * N, L]
671
+ logits = logits.masked_fill(~mask, float('-inf'))
672
+ topk_indices = logits.topk(topk_tokens,
673
+ dim=-1)[1].to(torch.int32) # [B * N, K]
674
+ # ensure we don't set indices for the top k
675
+ # that is out of range(masked already)
676
+ # this will happen if context length is shorter than K
677
+ topk_indices[topk_indices > index_end_pos] = -1
678
+ if decode_metadata.requires_padding:
679
+ # if padded, we need to unpack
680
+ # the topk indices removing padded tokens
681
+ topk_indices = unpack_seq_triton(
682
+ topk_indices.reshape(batch_size, -1, topk_indices.shape[-1]),
683
+ decode_lens)
684
+ topk_indices_buffer[:num_decode_tokens, :topk_indices.
685
+ shape[-1]] = topk_indices.to(dtype=torch.int32)
686
+
687
+ return topk_indices_buffer
688
+
689
+
690
+def sparse_attn_indexer_fake(
691
+ hidden_states: torch.Tensor,
692
+ k_cache_prefix: str,
693
+ kv_cache: torch.Tensor,
694
+ q_fp8: torch.Tensor,
695
+ k: torch.Tensor,
696
+ weights: torch.Tensor,
697
+ quant_block_size: int,
698
+ scale_fmt: Optional[str],
699
+ topk_tokens: int,
700
+ head_dim: int,
701
+ max_model_len: int,
702
+ total_seq_lens: int,
703
+ topk_indices_buffer: Optional[torch.Tensor],
704
+) -> torch.Tensor:
705
+ # profile run
706
+ # NOTE(Chen): create the max possible flattened_kv. So that
707
+ # profile_run can get correct memory usage.
708
+ _flattened_kv = torch.empty([total_seq_lens, head_dim + 4],
709
+ device=k.device,
710
+ dtype=torch.uint8)
711
+ _k_fp8 = _flattened_kv[..., :head_dim].view(
712
+ torch.float8_e4m3fn).contiguous()
713
+ _k_scale = _flattened_kv[..., head_dim:].view(torch.float32).contiguous()
714
+ return topk_indices_buffer
715
+
716
+
717
+direct_register_custom_op(
718
+ op_name="sparse_attn_indexer",
719
+ op_func=sparse_attn_indexer,
720
+ mutates_args=["topk_indices_buffer"],
721
+ fake_impl=sparse_attn_indexer_fake,
722
+ dispatch_key=current_platform.dispatch_key,
723
+)
724
+
725
+
726
+class Indexer(nn.Module):
727
+
728
+ def __init__(self,
729
+ vllm_config: VllmConfig,
730
+ config: Union[DeepseekV2Config, DeepseekV3Config],
731
+ hidden_size: int,
732
+ q_lora_rank: int,
733
+ quant_config: Optional[QuantizationConfig],
734
+ cache_config: Optional[CacheConfig],
735
+ topk_indices_buffer: Optional[torch.Tensor],
736
+ prefix: str = ""):
737
+ super().__init__()
738
+ self.vllm_config = vllm_config
739
+ self.config = config
740
+ # self.indexer_cfg = config.attn_module_list_cfg[0]["attn_index"]
741
+ self.topk_tokens = config.index_topk
742
+ self.n_head = config.index_n_heads # 64
743
+ self.head_dim = config.index_head_dim # 128
744
+ self.rope_dim = config.qk_rope_head_dim # 64
745
+ self.q_lora_rank = q_lora_rank # 1536
746
+ # no tensor parallel, just replicated
747
+ self.wq_b = ReplicatedLinear(self.q_lora_rank,
748
+ self.head_dim * self.n_head,
749
+ bias=False,
750
+ quant_config=quant_config,
751
+ prefix=f"{prefix}.wq_b")
752
+ self.wk = ReplicatedLinear(hidden_size,
753
+ self.head_dim,
754
+ bias=False,
755
+ quant_config=quant_config,
756
+ prefix=f"{prefix}.wk")
757
+ self.k_norm = LayerNorm(self.head_dim, eps=1e-6)
758
+ self.weights_proj = ReplicatedLinear(hidden_size,
759
+ self.n_head,
760
+ quant_config=None,
761
+ prefix=f"{prefix}.weights_proj")
762
+ self.softmax_scale = self.head_dim**-0.5
763
+
764
+ self.scale_fmt = "ue8m0"
765
+ self.quant_block_size = 128 # TODO: get from config
766
+ self.topk_indices_buffer = topk_indices_buffer
767
+
768
+ # NOTE: (zyongye) we use fp8 naive cache,
769
+ # where we store value in fp8 and scale in fp32
770
+ # per self.quant_block_size element
771
+ self.k_cache = DeepseekV32IndexerCache(
772
+ head_dim=self.head_dim +
773
+ self.head_dim // self.quant_block_size * 4,
774
+ dtype=torch.uint8,
775
+ prefix=f"{prefix}.k_cache",
776
+ cache_config=cache_config)
777
+ self.max_model_len = vllm_config.model_config.max_model_len
778
+ self.prefix = prefix
779
+ from vllm.v1.attention.backends.mla.indexer import (
780
+ get_max_prefill_buffer_size)
781
+ self.max_total_seq_len = get_max_prefill_buffer_size(vllm_config)
782
+
783
+ def forward(self, hidden_states: torch.Tensor, qr: torch.Tensor, positions,
784
+ rotary_emb) -> torch.Tensor:
785
+ q, _ = self.wq_b(qr)
786
+ q = q.view(-1, self.n_head, self.head_dim)
787
+ q_pe, q_nope = torch.split(
788
+ q, [self.rope_dim, self.head_dim - self.rope_dim], dim=-1)
789
+
790
+ k, _ = self.wk(hidden_states)
791
+ k = self.k_norm(k)
792
+ k_pe, k_nope = torch.split(
793
+ k, [self.rope_dim, self.head_dim - self.rope_dim], dim=-1)
794
+
795
+ q_pe, k_pe = rotary_emb(positions, q_pe, k_pe.unsqueeze(1))
796
+ q = torch.cat([q_pe, q_nope], dim=-1)
797
+ k = torch.cat([k_pe.squeeze(1), k_nope], dim=-1)
798
+
799
+ # we only quant q here since k quant is fused with cache insertion
800
+ q = q.view(-1, self.head_dim)
801
+ q_fp8, q_scale = per_token_group_quant_fp8(q,
802
+ self.quant_block_size,
803
+ column_major_scales=False,
804
+ use_ue8m0=self.scale_fmt
805
+ is not None)
806
+ q_fp8 = q_fp8.view(-1, self.n_head, self.head_dim)
807
+ q_scale = q_scale.view(-1, self.n_head, 1)
808
+
809
+ weights, _ = self.weights_proj(hidden_states)
810
+ weights = weights.unsqueeze(
811
+ -1) * q_scale * self.softmax_scale * self.n_head**-0.5
812
+ weights = weights.squeeze(-1)
813
+
814
+ return torch.ops.vllm.sparse_attn_indexer(
815
+ hidden_states,
816
+ self.k_cache.prefix,
817
+ self.k_cache.kv_cache[0],
818
+ q_fp8,
819
+ k,
820
+ weights,
821
+ self.quant_block_size,
822
+ self.scale_fmt,
823
+ self.topk_tokens,
824
+ self.head_dim,
825
+ self.max_model_len,
826
+ self.max_total_seq_len,
827
+ self.topk_indices_buffer,
828
+ )
829
+
830
+
831
class DeepseekV2MLAAttention(nn.Module):
832
"""
833
Main reference: DeepseekV2 paper, and FlashInfer Implementation
839
840
def __init__(
841
self,
842
+ vllm_config: VllmConfig,
843
config: Union[DeepseekV2Config, DeepseekV3Config],
844
hidden_size: int,
845
num_heads: int,
854
cache_config: Optional[CacheConfig] = None,
855
quant_config: Optional[QuantizationConfig] = None,
856
prefix: str = "",
857
+ topk_indices_buffer: Optional[torch.Tensor] = None,
858
) -> None:
859
super().__init__()
860
self.hidden_size = hidden_size
935
mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
936
self.scaling = self.scaling * mscale * mscale
937
938
+ self.is_v32 = hasattr(config, "index_topk")
939
+
940
+ if self.is_v32:
941
+ self.indexer = Indexer(vllm_config, config, hidden_size,
942
+ q_lora_rank, quant_config, cache_config,
943
+ topk_indices_buffer, f"{prefix}.indexer")
944
+ else:
945
+ self.indexer = None
946
+
947
mla_modules = MLAModules(
948
kv_a_layernorm=self.kv_a_layernorm,
949
kv_b_proj=self.kv_b_proj,
957
if self.q_lora_rank is not None else None,
958
q_b_proj=self.q_b_proj if self.q_lora_rank is not None else None,
959
q_proj=self.q_proj if self.q_lora_rank is None else None,
960
+ indexer=self.indexer,
961
+ is_sparse=self.is_v32,
962
+ topk_indices_buffer=topk_indices_buffer,
963
)
964
+
965
self.mla_attn = MultiHeadLatentAttention(
966
self.hidden_size,
967
self.num_local_heads,
987
988
class DeepseekV2DecoderLayer(nn.Module):
989
565
- def __init__(self, vllm_config: VllmConfig, prefix: str) -> None:
990
+ def __init__(self,
991
+ vllm_config: VllmConfig,
992
+ prefix: str,
993
+ topk_indices_buffer: Optional[torch.Tensor] = None) -> None:
994
super().__init__()
995
996
config = vllm_config.model_config.hf_config
1013
else:
1014
attn_cls = DeepseekV2Attention
1015
self.self_attn = attn_cls(
1016
+ vllm_config=vllm_config,
1017
config=config,
1018
hidden_size=self.hidden_size,
1019
num_heads=config.num_attention_heads,
1029
cache_config=cache_config,
1030
quant_config=quant_config,
1031
prefix=f"{prefix}.self_attn",
1032
+ topk_indices_buffer=topk_indices_buffer,
1033
)
1034
1035
if (config.n_routed_experts is not None
1113
self.config = config
1114
1115
self.vocab_size = config.vocab_size
1116
+ self.is_v32 = hasattr(config, "index_topk")
1117
+ if self.is_v32:
1118
+ topk_tokens = config.index_topk
1119
+ topk_indices_buffer = torch.empty(
1120
+ vllm_config.scheduler_config.max_num_batched_tokens,
1121
+ topk_tokens,
1122
+ dtype=torch.int32,
1123
+ device="cuda")
1124
+ else:
1125
+ topk_indices_buffer = None
1126
1127
if get_pp_group().is_first_rank:
1128
self.embed_tokens = VocabParallelEmbedding(
1135
1136
self.start_layer, self.end_layer, self.layers = make_layers(
1137
config.num_hidden_layers,
698
- lambda prefix: DeepseekV2DecoderLayer(vllm_config, prefix),
1138
+ lambda prefix: DeepseekV2DecoderLayer(vllm_config, prefix,
1139
+ topk_indices_buffer),
1140
prefix=f"{prefix}.layers")
1141
1142
if get_pp_group().is_last_rank: