NullSense commited on
Commit
6a4f3cb
·
verified ·
1 Parent(s): b518539

Update bundled plugin: looped-target Eagle3 draft fixes (layer-name offset + target_layer_count stamp)

Browse files
vllm_plugin/nanbeige_vllm_plugin/__init__.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Out-of-tree vLLM registration for Nanbeige4.2 (NanbeigeForCausalLM).
2
+ # Source: vllm-project/vllm PR #49433 (Nanbeige/vllm branch nanbeige42),
3
+ # vendored until the PR merges + ships in a release — then DELETE this plugin
4
+ # and drop the pip-install line from the vllm-nanbeige wrapper.
5
+ def _patch_eagle3_draft_layer_naming():
6
+ # The looped target registers num_loops * num_hidden_layers virtual
7
+ # attention layers (0..43), but vLLM's Eagle3 draft names its layer at
8
+ # start_layer_id = physical num_hidden_layers (22), colliding with the
9
+ # target's loop-2 layer 22 ("Duplicate layer name: model.layers.22...").
10
+ # Bump the draft's naming offset to the virtual depth. start_layer_id is
11
+ # ONLY a KV-registry prefix (layer_types indexing uses layer_idx), so this
12
+ # is a pure rename.
13
+ try:
14
+ from vllm.model_executor.models import llama_eagle3
15
+ except ImportError:
16
+ return
17
+ if getattr(llama_eagle3.LlamaModel, "_nanbeige_layer_naming_patch", False):
18
+ return
19
+ _orig_init = llama_eagle3.LlamaModel.__init__
20
+
21
+ def _patched_init(self, *, vllm_config, start_layer_id=0, prefix=""):
22
+ tgt_hf = vllm_config.model_config.hf_config
23
+ num_loops = getattr(tgt_hf, "num_loops", 1) or 1
24
+ if (
25
+ getattr(tgt_hf, "model_type", "") == "nanbeige"
26
+ and num_loops > 1
27
+ and start_layer_id == tgt_hf.num_hidden_layers
28
+ ):
29
+ start_layer_id = tgt_hf.num_hidden_layers * num_loops
30
+ # vLLM stamped target_layer_count = physical layers (22) on the
31
+ # draft config; the draft decoder layer indexes its own
32
+ # layer_types as (prefix_index - target_layer_count), so with the
33
+ # virtual naming offset the stamp must be virtual too, making the
34
+ # effective index 0 for the single draft layer.
35
+ draft_cfg = vllm_config.speculative_config.draft_model_config.hf_config
36
+ draft_cfg.target_layer_count = start_layer_id
37
+ _orig_init(
38
+ self, vllm_config=vllm_config, start_layer_id=start_layer_id, prefix=prefix
39
+ )
40
+
41
+ llama_eagle3.LlamaModel.__init__ = _patched_init
42
+ llama_eagle3.LlamaModel._nanbeige_layer_naming_patch = True
43
+
44
+
45
+ def register():
46
+ from vllm import ModelRegistry
47
+
48
+ _patch_eagle3_draft_layer_naming()
49
+ if "NanbeigeForCausalLM" in ModelRegistry.get_supported_archs():
50
+ return # native support arrived — plugin is a no-op
51
+ ModelRegistry.register_model(
52
+ "NanbeigeForCausalLM",
53
+ "nanbeige_vllm_plugin.nanbeige:NanbeigeForCausalLM",
54
+ )
vllm_plugin/nanbeige_vllm_plugin/nanbeige.py ADDED
@@ -0,0 +1,591 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+
4
+ from collections.abc import Iterable
5
+ from dataclasses import replace
6
+ from itertools import islice
7
+ from typing import Any
8
+
9
+ import torch
10
+ from torch import nn
11
+ from nanbeige_vllm_plugin.nanbeige_config import NanbeigeConfig
12
+
13
+ from vllm.compilation.decorators import support_torch_compile
14
+ from vllm.config import CacheConfig, VllmConfig
15
+ from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
16
+ from vllm.model_executor.layers.activation import SiluAndMul
17
+ from vllm.model_executor.layers.attention import (
18
+ Attention,
19
+ EncoderOnlyAttention,
20
+ )
21
+ from vllm.model_executor.layers.layernorm import RMSNorm
22
+ from vllm.model_executor.layers.linear import (
23
+ MergedColumnParallelLinear,
24
+ QKVParallelLinear,
25
+ RowParallelLinear,
26
+ )
27
+ from vllm.model_executor.layers.logits_processor import LogitsProcessor
28
+ from vllm.model_executor.layers.quantization import QuantizationConfig
29
+ from vllm.model_executor.layers.rotary_embedding import get_rope
30
+ from vllm.model_executor.layers.vocab_parallel_embedding import (
31
+ ParallelLMHead,
32
+ VocabParallelEmbedding,
33
+ )
34
+ from vllm.model_executor.model_loader.weight_utils import (
35
+ default_weight_loader,
36
+ maybe_remap_kv_scale_name,
37
+ )
38
+ from vllm.sequence import IntermediateTensors
39
+ from vllm.transformers_utils.config import is_interleaved, set_default_rope_theta
40
+ from vllm.v1.attention.backend import AttentionType
41
+
42
+ from vllm.model_executor.models.interfaces import (
43
+ EagleModelMixin,
44
+ SupportsEagle,
45
+ SupportsEagle3,
46
+ SupportsLoRA,
47
+ SupportsPP,
48
+ )
49
+ from vllm.model_executor.models.utils import (
50
+ AutoWeightsLoader,
51
+ PPMissingLayer,
52
+ extract_layer_index,
53
+ is_pp_missing_parameter,
54
+ make_empty_intermediate_tensors_factory,
55
+ make_layers,
56
+ maybe_prefix,
57
+ )
58
+
59
+
60
+ class NanbeigeMLP(nn.Module):
61
+ def __init__(
62
+ self,
63
+ hidden_size: int,
64
+ intermediate_size: int,
65
+ hidden_act: str,
66
+ quant_config: QuantizationConfig | None = None,
67
+ prefix: str = "",
68
+ ) -> None:
69
+ super().__init__()
70
+ self.gate_up_proj = MergedColumnParallelLinear(
71
+ hidden_size,
72
+ [intermediate_size] * 2,
73
+ bias=False,
74
+ quant_config=quant_config,
75
+ prefix=f"{prefix}.gate_up_proj",
76
+ )
77
+ self.down_proj = RowParallelLinear(
78
+ intermediate_size,
79
+ hidden_size,
80
+ bias=False,
81
+ quant_config=quant_config,
82
+ prefix=f"{prefix}.down_proj",
83
+ )
84
+ if hidden_act != "silu":
85
+ raise ValueError(
86
+ f"Unsupported activation: {hidden_act}. Only silu is supported for now."
87
+ )
88
+ self.act_fn = SiluAndMul()
89
+
90
+ def forward(self, x):
91
+ gate_up, _ = self.gate_up_proj(x)
92
+ x = self.act_fn(gate_up)
93
+ x, _ = self.down_proj(x)
94
+ return x
95
+
96
+
97
+ class NanbeigeAttention(nn.Module):
98
+ def __init__(
99
+ self,
100
+ config: NanbeigeConfig,
101
+ hidden_size: int,
102
+ num_heads: int,
103
+ num_kv_heads: int,
104
+ rope_parameters: dict[str, Any],
105
+ max_position: int = 4096 * 32,
106
+ cache_config: CacheConfig | None = None,
107
+ quant_config: QuantizationConfig | None = None,
108
+ prefix: str = "",
109
+ attn_type: str = AttentionType.DECODER,
110
+ dual_chunk_attention_config: dict[str, Any] | None = None,
111
+ qk_norm: bool = False,
112
+ rms_norm_eps: float = 1e-6,
113
+ ) -> None:
114
+ super().__init__()
115
+ self.hidden_size = hidden_size
116
+ tp_size = get_tensor_model_parallel_world_size()
117
+ self.total_num_heads = num_heads
118
+ assert self.total_num_heads % tp_size == 0
119
+ self.num_heads = self.total_num_heads // tp_size
120
+ self.total_num_kv_heads = num_kv_heads
121
+ if self.total_num_kv_heads >= tp_size:
122
+ # Number of KV heads is greater than TP size, so we partition
123
+ # the KV heads across multiple tensor parallel GPUs.
124
+ assert self.total_num_kv_heads % tp_size == 0
125
+ else:
126
+ # Number of KV heads is less than TP size, so we replicate
127
+ # the KV heads across multiple tensor parallel GPUs.
128
+ assert tp_size % self.total_num_kv_heads == 0
129
+ self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
130
+ self.head_dim = hidden_size // self.total_num_heads
131
+ self.head_dim = getattr(config, "head_dim", hidden_size // self.total_num_heads)
132
+ self.q_size = self.num_heads * self.head_dim
133
+ self.kv_size = self.num_kv_heads * self.head_dim
134
+ self.scaling = self.head_dim**-0.5
135
+ self.dual_chunk_attention_config = dual_chunk_attention_config
136
+ self.qk_norm = qk_norm
137
+
138
+ self.qkv_proj = QKVParallelLinear(
139
+ hidden_size,
140
+ self.head_dim,
141
+ self.total_num_heads,
142
+ self.total_num_kv_heads,
143
+ bias=False,
144
+ quant_config=quant_config,
145
+ prefix=f"{prefix}.qkv_proj",
146
+ )
147
+ self.o_proj = RowParallelLinear(
148
+ self.total_num_heads * self.head_dim,
149
+ hidden_size,
150
+ bias=False,
151
+ quant_config=quant_config,
152
+ prefix=f"{prefix}.o_proj",
153
+ )
154
+
155
+ # QK Normalization support (used in BAGEL and some other models)
156
+ if self.qk_norm:
157
+ self.q_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
158
+ self.k_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
159
+
160
+ self.rotary_emb = get_rope(
161
+ self.head_dim,
162
+ max_position=max_position,
163
+ rope_parameters=rope_parameters,
164
+ dual_chunk_attention_config=dual_chunk_attention_config,
165
+ )
166
+
167
+ self.loops_num = getattr(config, "num_loops", 1)
168
+ total_layers = config.num_hidden_layers
169
+ self.attn = nn.ModuleList()
170
+
171
+ for loop_idx in range(self.loops_num):
172
+ base_layer_idx = extract_layer_index(prefix)
173
+ unique_layer_idx = loop_idx * total_layers + base_layer_idx
174
+ unique_prefix = prefix.replace(
175
+ f"layers.{base_layer_idx}", f"layers.{unique_layer_idx}"
176
+ )
177
+ self.attn.append(
178
+ Attention(
179
+ self.num_heads,
180
+ self.head_dim,
181
+ self.scaling,
182
+ num_kv_heads=self.num_kv_heads,
183
+ cache_config=cache_config,
184
+ quant_config=quant_config,
185
+ attn_type=attn_type,
186
+ prefix=f"{unique_prefix}.attn",
187
+ **{
188
+ "layer_idx": unique_layer_idx,
189
+ "dual_chunk_attention_config": dual_chunk_attention_config,
190
+ }
191
+ if dual_chunk_attention_config and loop_idx == 0
192
+ else {},
193
+ )
194
+ )
195
+
196
+ def forward(
197
+ self,
198
+ positions: torch.Tensor,
199
+ hidden_states: torch.Tensor,
200
+ loop_idx: int,
201
+ ) -> torch.Tensor:
202
+ qkv, _ = self.qkv_proj(hidden_states)
203
+ q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
204
+
205
+ # Apply QK normalization if enabled (before RoPE)
206
+ if self.qk_norm:
207
+ # Reshape to apply per-head normalization
208
+ # q shape: (total_tokens, q_size) -> (total_tokens, num_heads, head_dim)
209
+ total_tokens = q.shape[0]
210
+ q = q.view(total_tokens, self.num_heads, self.head_dim)
211
+ k = k.view(total_tokens, self.num_kv_heads, self.head_dim)
212
+
213
+ # Apply normalization
214
+ q = self.q_norm(q)
215
+ k = self.k_norm(k)
216
+
217
+ # Reshape back
218
+ q = q.view(total_tokens, self.q_size)
219
+ k = k.view(total_tokens, self.kv_size)
220
+
221
+ q, k = self.rotary_emb(positions, q, k)
222
+ attn_output = self.attn[loop_idx](q, k, v)
223
+ output, _ = self.o_proj(attn_output)
224
+ return output
225
+
226
+
227
+ class NanbeigeDecoderLayer(nn.Module):
228
+ def __init__(
229
+ self,
230
+ config: NanbeigeConfig,
231
+ cache_config: CacheConfig | None = None,
232
+ quant_config: QuantizationConfig | None = None,
233
+ prefix: str = "",
234
+ ) -> None:
235
+ super().__init__()
236
+ self.hidden_size = config.hidden_size
237
+ set_default_rope_theta(config, default_theta=1000000)
238
+ dual_chunk_attention_config = getattr(
239
+ config, "dual_chunk_attention_config", None
240
+ )
241
+
242
+ if getattr(config, "is_causal", True):
243
+ attn_type = AttentionType.DECODER
244
+ else:
245
+ attn_type = AttentionType.ENCODER_ONLY
246
+
247
+ # Check if QK normalization is enabled (used in BAGEL and some other models)
248
+ qk_norm = getattr(config, "qk_norm", False)
249
+
250
+ self.self_attn = NanbeigeAttention(
251
+ config=config,
252
+ hidden_size=self.hidden_size,
253
+ num_heads=config.num_attention_heads,
254
+ max_position=config.max_position_embeddings,
255
+ num_kv_heads=config.num_key_value_heads,
256
+ cache_config=cache_config,
257
+ quant_config=quant_config,
258
+ rope_parameters=config.rope_parameters,
259
+ prefix=f"{prefix}.self_attn",
260
+ attn_type=attn_type,
261
+ dual_chunk_attention_config=dual_chunk_attention_config,
262
+ qk_norm=qk_norm,
263
+ rms_norm_eps=config.rms_norm_eps,
264
+ )
265
+ self.mlp = NanbeigeMLP(
266
+ hidden_size=self.hidden_size,
267
+ intermediate_size=config.intermediate_size,
268
+ hidden_act=config.hidden_act,
269
+ quant_config=quant_config,
270
+ prefix=f"{prefix}.mlp",
271
+ )
272
+ self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
273
+ self.post_attention_layernorm = RMSNorm(
274
+ config.hidden_size, eps=config.rms_norm_eps
275
+ )
276
+
277
+ def forward(
278
+ self,
279
+ positions: torch.Tensor,
280
+ hidden_states: torch.Tensor,
281
+ residual: torch.Tensor | None,
282
+ loop_idx: int = 0,
283
+ ) -> tuple[torch.Tensor, torch.Tensor]:
284
+ # Self Attention
285
+ if residual is None:
286
+ residual = hidden_states
287
+ hidden_states = self.input_layernorm(hidden_states)
288
+ else:
289
+ hidden_states, residual = self.input_layernorm(hidden_states, residual)
290
+ hidden_states = self.self_attn(
291
+ positions=positions,
292
+ hidden_states=hidden_states,
293
+ loop_idx=loop_idx,
294
+ )
295
+
296
+ # Fully Connected
297
+ hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
298
+ hidden_states = self.mlp(hidden_states)
299
+ return hidden_states, residual
300
+
301
+
302
+ @support_torch_compile(
303
+ dynamic_arg_dims={
304
+ "input_ids": {0: "b"},
305
+ "positions": {-1: "b"},
306
+ "intermediate_tensors": {0: "b"},
307
+ "inputs_embeds": {0: "b"},
308
+ }
309
+ )
310
+ class NanbeigeModel(nn.Module, EagleModelMixin):
311
+ def __init__(
312
+ self,
313
+ *,
314
+ vllm_config: VllmConfig,
315
+ prefix: str = "",
316
+ decoder_layer_type: type[nn.Module] = NanbeigeDecoderLayer,
317
+ ):
318
+ super().__init__()
319
+
320
+ config = vllm_config.model_config.hf_config.get_text_config()
321
+ cache_config = vllm_config.cache_config
322
+ quant_config = vllm_config.quant_config
323
+
324
+ # TODO (@robertgshaw2): see if this can be moved out
325
+ if is_interleaved(vllm_config.model_config.hf_text_config):
326
+ assert config.max_window_layers == config.num_hidden_layers, (
327
+ "Sliding window for some but all layers is not supported. "
328
+ "This model uses sliding window but `max_window_layers` = {} "
329
+ "is less than `num_hidden_layers` = {}. Please open an issue "
330
+ "to discuss this feature.".format(
331
+ config.max_window_layers,
332
+ config.num_hidden_layers,
333
+ )
334
+ )
335
+
336
+ self.config = config
337
+ self.quant_config = quant_config
338
+ self.vocab_size = config.vocab_size
339
+ self.loops_num = getattr(config, "num_loops", 1)
340
+ self.skip_loop_final_norm = getattr(config, "skip_loop_final_norm", False)
341
+
342
+ if get_pp_group().is_first_rank or (
343
+ config.tie_word_embeddings and get_pp_group().is_last_rank
344
+ ):
345
+ self.embed_tokens = VocabParallelEmbedding(
346
+ config.vocab_size,
347
+ config.hidden_size,
348
+ quant_config=quant_config,
349
+ prefix=f"{prefix}.embed_tokens",
350
+ )
351
+ else:
352
+ self.embed_tokens = PPMissingLayer()
353
+
354
+ self.start_layer, self.end_layer, self.layers = make_layers(
355
+ config.num_hidden_layers,
356
+ lambda prefix: decoder_layer_type(
357
+ config=config,
358
+ cache_config=cache_config,
359
+ quant_config=quant_config,
360
+ prefix=prefix,
361
+ ),
362
+ prefix=f"{prefix}.layers",
363
+ )
364
+
365
+ self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
366
+ ["hidden_states", "residual"], config.hidden_size
367
+ )
368
+ if get_pp_group().is_last_rank:
369
+ self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
370
+ else:
371
+ self.norm = PPMissingLayer()
372
+
373
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
374
+ return self.embed_tokens(input_ids)
375
+
376
+ def forward(
377
+ self,
378
+ input_ids: torch.Tensor | None,
379
+ positions: torch.Tensor,
380
+ intermediate_tensors: IntermediateTensors | None = None,
381
+ inputs_embeds: torch.Tensor | None = None,
382
+ ) -> torch.Tensor | IntermediateTensors:
383
+ if get_pp_group().is_first_rank:
384
+ if inputs_embeds is not None:
385
+ hidden_states = inputs_embeds
386
+ else:
387
+ hidden_states = self.embed_input_ids(input_ids)
388
+ residual = None
389
+ else:
390
+ assert intermediate_tensors is not None
391
+ hidden_states = intermediate_tensors["hidden_states"]
392
+ residual = intermediate_tensors["residual"]
393
+
394
+ # EAGLE-3 aux-state capture uses VIRTUAL (unrolled) depth, not physical
395
+ # layer index: 0 = embeddings, 1..22 = loop pass 1, 23..44 = loop pass 2.
396
+ # The PR's original `idx + 1` keying fired once PER LOOP for each selected
397
+ # physical layer (EagleModelMixin membership-tests a flat index), so a
398
+ # 3-layer tap emitted 6 tensors — a silent shape mismatch against any
399
+ # standard EAGLE-3 head (fc expects 3*hidden). Virtual indexing restores
400
+ # exactly-once capture AND makes loop-2 features addressable.
401
+ aux_hidden_states = self._maybe_add_hidden_state([], 0, hidden_states, residual)
402
+ virtual_idx = 0
403
+ for loop_idx in range(self.loops_num):
404
+ for idx, layer in enumerate(
405
+ islice(self.layers, self.start_layer, self.end_layer)
406
+ ):
407
+ hidden_states, residual = layer(positions, hidden_states, residual, loop_idx=loop_idx)
408
+ virtual_idx += 1
409
+ self._maybe_add_hidden_state(
410
+ aux_hidden_states, virtual_idx, hidden_states, residual
411
+ )
412
+
413
+ if loop_idx < self.loops_num - 1:
414
+ if residual is not None:
415
+ hidden_states = hidden_states + residual
416
+ residual = None
417
+ if not self.skip_loop_final_norm:
418
+ hidden_states = self.norm(hidden_states)
419
+
420
+ if not get_pp_group().is_last_rank:
421
+ return IntermediateTensors(
422
+ {"hidden_states": hidden_states, "residual": residual}
423
+ )
424
+
425
+ hidden_states, _ = self.norm(hidden_states, residual)
426
+
427
+ if len(aux_hidden_states) > 0:
428
+ return hidden_states, aux_hidden_states
429
+
430
+ return hidden_states
431
+
432
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
433
+ stacked_params_mapping = [
434
+ # (param_name, shard_name, shard_id)
435
+ ("qkv_proj", "q_proj", "q"),
436
+ ("qkv_proj", "k_proj", "k"),
437
+ ("qkv_proj", "v_proj", "v"),
438
+ ("gate_up_proj", "gate_proj", 0),
439
+ ("gate_up_proj", "up_proj", 1),
440
+ ]
441
+ params_dict = dict(self.named_parameters(remove_duplicate=False))
442
+ loaded_params: set[str] = set()
443
+ for name, loaded_weight in weights:
444
+ if "rotary_emb.inv_freq" in name:
445
+ continue
446
+ # LOCAL PATCH: get_cache_scale doesn't exist on CompressedTensorsConfig in
447
+ # v0.25.1 (fork targets newer main). Our FP8-dynamic checkpoint ships no KV
448
+ # scales, so skipping the branch when the method is absent is lossless.
449
+ if self.quant_config is not None and (
450
+ scale_name := (
451
+ self.quant_config.get_cache_scale(name)
452
+ if hasattr(self.quant_config, "get_cache_scale")
453
+ else None
454
+ )
455
+ ):
456
+ # Loading kv cache quantization scales
457
+ param = params_dict[scale_name]
458
+ weight_loader = getattr(param, "weight_loader", default_weight_loader)
459
+ loaded_weight = (
460
+ loaded_weight if loaded_weight.dim() == 0 else loaded_weight[0]
461
+ )
462
+ weight_loader(param, loaded_weight)
463
+ loaded_params.add(scale_name)
464
+ continue
465
+ for param_name, weight_name, shard_id in stacked_params_mapping:
466
+ if weight_name not in name:
467
+ continue
468
+ name = name.replace(weight_name, param_name)
469
+ # Skip loading extra bias for GPTQ models.
470
+ if name.endswith(".bias") and name not in params_dict:
471
+ continue
472
+ if is_pp_missing_parameter(name, self):
473
+ continue
474
+ if name.endswith("scale"):
475
+ # Remapping the name of FP8 kv-scale.
476
+ name = maybe_remap_kv_scale_name(name, params_dict)
477
+ if name is None:
478
+ continue
479
+ param = params_dict[name]
480
+ weight_loader = getattr(param, "weight_loader", default_weight_loader)
481
+ if weight_loader == default_weight_loader:
482
+ weight_loader(param, loaded_weight)
483
+ else:
484
+ weight_loader(param, loaded_weight, shard_id)
485
+ break
486
+ else:
487
+ # Skip loading extra bias for GPTQ models.
488
+ if name.endswith(".bias") and name not in params_dict:
489
+ continue
490
+ # Remapping the name of FP8 kv-scale.
491
+ name = maybe_remap_kv_scale_name(name, params_dict)
492
+ if name is None:
493
+ continue
494
+ if is_pp_missing_parameter(name, self):
495
+ continue
496
+ if name not in params_dict:
497
+ continue
498
+ param = params_dict[name]
499
+ weight_loader = getattr(param, "weight_loader", default_weight_loader)
500
+ weight_loader(param, loaded_weight)
501
+ loaded_params.add(name)
502
+ return loaded_params
503
+
504
+
505
+ class NanbeigeForCausalLM(
506
+ nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3
507
+ ):
508
+ packed_modules_mapping = {
509
+ "qkv_proj": [
510
+ "q_proj",
511
+ "k_proj",
512
+ "v_proj",
513
+ ],
514
+ "gate_up_proj": [
515
+ "gate_proj",
516
+ "up_proj",
517
+ ],
518
+ }
519
+
520
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
521
+ super().__init__()
522
+ config = vllm_config.model_config.hf_config.get_text_config()
523
+ quant_config = vllm_config.quant_config
524
+
525
+ self.config = config
526
+
527
+ self.quant_config = quant_config
528
+ self.model = NanbeigeModel(
529
+ vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
530
+ )
531
+
532
+ if get_pp_group().is_last_rank:
533
+ if config.tie_word_embeddings:
534
+ self.lm_head = self.model.embed_tokens
535
+ else:
536
+ self.lm_head = ParallelLMHead(
537
+ config.vocab_size,
538
+ config.hidden_size,
539
+ quant_config=quant_config,
540
+ prefix=maybe_prefix(prefix, "lm_head"),
541
+ )
542
+ else:
543
+ self.lm_head = PPMissingLayer()
544
+
545
+ self.logits_processor = LogitsProcessor(config.vocab_size)
546
+
547
+ self.make_empty_intermediate_tensors = (
548
+ self.model.make_empty_intermediate_tensors
549
+ )
550
+
551
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
552
+ return self.model.embed_input_ids(input_ids)
553
+
554
+ def get_eagle3_default_aux_hidden_state_layers(self) -> tuple[int, ...]:
555
+ # Indices are VIRTUAL (unrolled) depth — see NanbeigeModel.forward:
556
+ # 0 = embeddings, 1..N = loop pass 1, N+1..2N = loop pass 2 (N = 22).
557
+ # The stock heuristic (2, L//2, L-3) on physical L=22 would pick
558
+ # loop-1-only features; low/mid/high on the 44-step virtual axis taps
559
+ # early loop-1, the loop boundary, and late loop-2 — the closest match
560
+ # to EAGLE-3's feature-diversity intent for a looped model. No prior
561
+ # art exists for looped-arch taps; triple subject to ablation.
562
+ n = len(self.model.layers) # physical layers (22)
563
+ total = n * self.model.loops_num # virtual depth (44)
564
+ return (3, n + 1, total - 3)
565
+
566
+ def forward(
567
+ self,
568
+ input_ids: torch.Tensor | None,
569
+ positions: torch.Tensor,
570
+ intermediate_tensors: IntermediateTensors | None = None,
571
+ inputs_embeds: torch.Tensor | None = None,
572
+ ) -> torch.Tensor | IntermediateTensors:
573
+ hidden_states = self.model(
574
+ input_ids, positions, intermediate_tensors, inputs_embeds
575
+ )
576
+ return hidden_states
577
+
578
+ def compute_logits(
579
+ self,
580
+ hidden_states: torch.Tensor,
581
+ ) -> torch.Tensor | None:
582
+ logits = self.logits_processor(self.lm_head, hidden_states)
583
+ return logits
584
+
585
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
586
+ loader = AutoWeightsLoader(
587
+ self,
588
+ skip_prefixes=(["lm_head."] if self.config.tie_word_embeddings else None),
589
+ )
590
+ return loader.load_weights(weights)
591
+
vllm_plugin/nanbeige_vllm_plugin/nanbeige_config.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ """ Nanbeige model configuration"""
14
+
15
+ from transformers.configuration_utils import PretrainedConfig
16
+
17
+
18
+ class NanbeigeConfig(PretrainedConfig):
19
+ r"""
20
+ This is the configuration class to store the configuration of a [`NanbeigeModel`]. It is used to instantiate an Nanbeige
21
+ model according to the specified arguments, defining the model architecture. Instantiating a configuration with the
22
+ defaults will yield a similar configuration to that of the LLaMA-7B.
23
+
24
+ Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
25
+ documentation from [`PretrainedConfig`] for more information.
26
+
27
+
28
+ Args:
29
+ vocab_size (`int`, *optional*, defaults to 32000):
30
+ Vocabulary size of the Nanbeige model. Defines the number of different tokens that can be represented by the
31
+ `inputs_ids` passed when calling [`NanbeigeModel`]
32
+ hidden_size (`int`, *optional*, defaults to 4096):
33
+ Dimension of the hidden representations.
34
+ intermediate_size (`int`, *optional*, defaults to 11008):
35
+ Dimension of the MLP representations.
36
+ num_hidden_layers (`int`, *optional*, defaults to 32):
37
+ Number of hidden layers in the Transformer decoder.
38
+ num_attention_heads (`int`, *optional*, defaults to 32):
39
+ Number of attention heads for each attention layer in the Transformer decoder.
40
+ num_key_value_heads (`int`, *optional*):
41
+ This is the number of key_value heads that should be used to implement Grouped Query Attention. If
42
+ `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
43
+ `num_key_value_heads=1 the model will use Multi Query Attention (MQA) otherwise GQA is used. When
44
+ converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
45
+ by meanpooling all the original heads within that group. For more details checkout [this
46
+ paper](https://arxiv.org/pdf/2305.13245.pdf). If it is not specified, will default to
47
+ `num_attention_heads`.
48
+ hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
49
+ The non-linear activation function (function or string) in the decoder.
50
+ max_position_embeddings (`int`, *optional*, defaults to 2048):
51
+ The maximum sequence length that this model might ever be used with. Llama 1 supports up to 2048 tokens,
52
+ Llama 2 up to 4096, CodeLlama up to 16384.
53
+ initializer_range (`float`, *optional*, defaults to 0.02):
54
+ The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
55
+ rms_norm_eps (`float`, *optional*, defaults to 1e-06):
56
+ The epsilon used by the rms normalization layers.
57
+ use_cache (`bool`, *optional*, defaults to `True`):
58
+ Whether or not the model should return the last key/values attentions (not used by all models). Only
59
+ relevant if `config.is_decoder=True`.
60
+ pad_token_id (`int`, *optional*):
61
+ Padding token id.
62
+ bos_token_id (`int`, *optional*, defaults to 1):
63
+ Beginning of stream token id.
64
+ eos_token_id (`int`, *optional*, defaults to 2):
65
+ End of stream token id.
66
+ pretraining_tp (`int`, *optional*, defaults to 1):
67
+ Experimental feature. Tensor parallelism rank used during pretraining. Please refer to [this
68
+ document](https://huggingface.co/docs/transformers/main/perf_train_gpu_many#tensor-parallelism) to understand more about it. This value is
69
+ necessary to ensure exact reproducibility of the pretraining results. Please refer to [this
70
+ issue](https://github.com/pytorch/pytorch/issues/76232).
71
+ tie_word_embeddings (`bool`, *optional*, defaults to `False`):
72
+ Whether to tie weight embeddings
73
+ rope_theta (`float`, *optional*, defaults to 10000.0):
74
+ The base period of the RoPE embeddings.
75
+ rope_scaling (`Dict`, *optional*):
76
+ Dictionary containing the scaling configuration for the RoPE embeddings. Currently supports two scaling
77
+ strategies: linear and dynamic. Their scaling factor must be a float greater than 1. The expected format is
78
+ `{"type": strategy name, "factor": scaling factor}`. When using this flag, don't update
79
+ `max_position_embeddings` to the expected new maximum. See the following thread for more information on how
80
+ these scaling strategies behave:
81
+ https://www.reddit.com/r/LocalLLaMA/comments/14mrgpr/dynamically_scaled_rope_further_increases/. This is an
82
+ experimental feature, subject to breaking API changes in future versions.
83
+ attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
84
+ Whether to use a bias in the query, key, value and output projection layers during self-attention.
85
+ attention_dropout (`float`, *optional*, defaults to 0.0):
86
+ The dropout ratio for the attention probabilities.
87
+ num_loops (`int`, *optional*, defaults to 1):
88
+ The number of loops for the loop model.
89
+ loop_loss_weights (`List[float]`, *optional*, defaults to `[]`):
90
+ The weights for the loop loss.
91
+ skip_loop_final_norm (`bool`, *optional*, defaults to `False`):
92
+ Whether to skip final norm after each loop (except the last one).
93
+
94
+ ```python
95
+ >>> from configuration_nanbeige import NanbeigeConfig
96
+ >>> from modeling_nanbeige import NanbeigeModel
97
+
98
+ >>> # Initializing a Nanbeige nanbeige-7b style configuration
99
+ >>> configuration = NanbeigeConfig()
100
+
101
+ >>> # Initializing a model from the nanbeige-7b style configuration
102
+ >>> model = NanbeigeModel(configuration)
103
+
104
+ >>> # Accessing the model configuration
105
+ >>> configuration = model.config
106
+ ```"""
107
+
108
+ model_type = "nanbeige"
109
+ keys_to_ignore_at_inference = ["past_key_values"]
110
+
111
+ def __init__(
112
+ self,
113
+ vocab_size=166144,
114
+ hidden_size=3072,
115
+ intermediate_size=10752,
116
+ num_hidden_layers=22,
117
+ num_attention_heads=48,
118
+ num_key_value_heads=None,
119
+ head_dim=None,
120
+ hidden_act="silu",
121
+ max_position_embeddings=4096,
122
+ initializer_range=0.02,
123
+ rms_norm_eps=1e-5,
124
+ use_cache=True,
125
+ pad_token_id=None,
126
+ bos_token_id=1,
127
+ eos_token_id=2,
128
+ pretraining_tp=1,
129
+ tie_word_embeddings=False,
130
+ rope_theta=50000.0,
131
+ rope_scaling=None,
132
+ attention_bias=False,
133
+ attention_dropout=0.0,
134
+ num_loops=2,
135
+ loop_loss_weights=None,
136
+ skip_loop_final_norm=False,
137
+ **kwargs,
138
+ ):
139
+ self.vocab_size = vocab_size
140
+ self.max_position_embeddings = max_position_embeddings
141
+ self.hidden_size = hidden_size
142
+ self.intermediate_size = intermediate_size
143
+ self.num_hidden_layers = num_hidden_layers
144
+ self.num_attention_heads = num_attention_heads
145
+
146
+ # Add head_dim logic
147
+ if head_dim is not None:
148
+ self.head_dim = head_dim
149
+ else:
150
+ self.head_dim = hidden_size // num_attention_heads
151
+
152
+ # for backward compatibility
153
+ if num_key_value_heads is None:
154
+ num_key_value_heads = num_attention_heads
155
+
156
+ self.num_key_value_heads = num_key_value_heads
157
+ self.hidden_act = hidden_act
158
+ self.initializer_range = initializer_range
159
+ self.rms_norm_eps = rms_norm_eps
160
+ self.pretraining_tp = pretraining_tp
161
+ self.use_cache = use_cache
162
+ self.rope_theta = rope_theta
163
+ self.rope_scaling = rope_scaling
164
+ self._rope_scaling_validation()
165
+ self.attention_bias = attention_bias
166
+ self.attention_dropout = attention_dropout
167
+
168
+ self.num_loops = num_loops
169
+ self.loop_loss_weights = loop_loss_weights if loop_loss_weights is not None else []
170
+ self.skip_loop_final_norm = skip_loop_final_norm
171
+
172
+ super().__init__(
173
+ pad_token_id=pad_token_id,
174
+ bos_token_id=bos_token_id,
175
+ eos_token_id=eos_token_id,
176
+ tie_word_embeddings=tie_word_embeddings,
177
+ **kwargs,
178
+ )
179
+
180
+ def _rope_scaling_validation(self):
181
+ """
182
+ Validate the `rope_scaling` configuration.
183
+ """
184
+ if self.rope_scaling is None:
185
+ return
186
+
187
+ if not isinstance(self.rope_scaling, dict) or len(self.rope_scaling) != 2:
188
+ raise ValueError(
189
+ "`rope_scaling` must be a dictionary with two fields, `type` and `factor`, " f"got {self.rope_scaling}"
190
+ )
191
+ rope_scaling_type = self.rope_scaling.get("type", None)
192
+ rope_scaling_factor = self.rope_scaling.get("factor", None)
193
+ if rope_scaling_type is None or rope_scaling_type not in ["linear", "dynamic"]:
194
+ raise ValueError(
195
+ f"`rope_scaling`'s type field must be one of ['linear', 'dynamic'], got {rope_scaling_type}"
196
+ )
197
+ if rope_scaling_factor is None or not isinstance(rope_scaling_factor, float) or rope_scaling_factor <= 1.0:
198
+ raise ValueError(f"`rope_scaling`'s factor field must be a float > 1, got {rope_scaling_factor}")
199
+