1use anyhow::{Result, anyhow, bail};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum TensorNameMap {
7 Raw,
8 HfToGguf,
9 HfToGgufWithMtp { layer_start: u32 },
10}
11
12impl TensorNameMap {
13 pub fn map_tensor_name(self, name: &str) -> Result<String> {
14 match self {
15 Self::Raw => Ok(name.to_string()),
16 Self::HfToGguf => map_hf_to_gguf(name, None),
17 Self::HfToGgufWithMtp { layer_start } => map_hf_to_gguf(name, Some(layer_start)),
18 }
19 }
20}
21
22fn map_hf_to_gguf(name: &str, mtp_layer_start: Option<u32>) -> Result<String> {
23 if let Some(layer_start) = mtp_layer_start
24 && let Some(normalized) = normalize_inkling_mtp_source_name(name, layer_start)?
25 {
26 return map_hf_to_gguf(&normalized, mtp_layer_start);
27 }
28 if let Some(layer_start) = mtp_layer_start
29 && let Some(normalized) = normalize_qwen_mtp_source_name(name, layer_start)?
30 {
31 return map_hf_to_gguf(&normalized, mtp_layer_start);
32 }
33 if matches!(name, "model.embed_tokens.weight" | "model.llm.embed.weight") {
34 return Ok("token_embd.weight".to_string());
35 }
36 if name == "embed_tokens.weight" {
37 return Ok("token_embd.weight".to_string());
38 }
39 if matches!(name, "lm_head.weight" | "model.llm.unembed.weight") {
40 return Ok("output.weight".to_string());
41 }
42 if matches!(name, "model.norm.weight" | "model.llm.norm.weight") {
43 return Ok("output_norm.weight".to_string());
44 }
45 if name == "model.llm.embed_norm.weight" {
46 return Ok("token_embd_norm.weight".to_string());
47 }
48 if name == "norm.weight" {
49 return Ok("output_norm.weight".to_string());
50 }
51 if let Some(name) = map_mtp_source_tensor(name)? {
52 return Ok(name);
53 }
54 let Some(layer) = HfLayerTensor::parse(name)? else {
55 bail!("no HF->GGUF tensor mapping for {name}");
56 };
57 layer.map()
58}
59
60pub(crate) fn is_mtp_source_tensor(name: &str) -> bool {
61 is_inkling_mtp_source_tensor(name)
62 || is_qwen_mtp_source_tensor(name)
63 || map_mtp_source_tensor(name).is_ok_and(|mapped| mapped.is_some())
64}
65
66pub(crate) fn hf_layer_id(name: &str) -> Result<Option<u32>> {
67 let Some(rest) = name.strip_prefix("model.layers.") else {
68 return Ok(None);
69 };
70 let Some((layer, _suffix)) = rest.split_once('.') else {
71 bail!("malformed layer tensor name {name}");
72 };
73 layer
74 .parse::<u32>()
75 .map(Some)
76 .map_err(|err| anyhow!("malformed layer id in {name}: {err}"))
77}
78
79pub(crate) fn is_shared_mtp_context_tensor(name: &str) -> bool {
80 matches!(
81 name,
82 "model.embed_tokens.weight"
83 | "embed_tokens.weight"
84 | "model.norm.weight"
85 | "norm.weight"
86 | "lm_head.weight"
87 | "model.llm.embed.weight"
88 | "model.llm.embed_norm.weight"
89 | "model.llm.norm.weight"
90 | "model.llm.unembed.weight"
91 )
92}
93
94pub(crate) fn is_inkling_fused_w13(name: &str) -> bool {
95 (name.starts_with("model.layers.") || name.starts_with("model.mtp.layers."))
96 && name.ends_with(".mlp.w13_dn.weight")
97}
98
99pub(crate) fn inkling_mtp_depth(name: &str) -> Result<Option<u32>> {
100 let Some(rest) = name.strip_prefix("model.mtp.layers.") else {
101 return Ok(None);
102 };
103 let Some((depth, _)) = rest.split_once('.') else {
104 bail!("malformed Inkling MTP tensor name {name}");
105 };
106 depth
107 .parse::<u32>()
108 .map(Some)
109 .map_err(|err| anyhow!("malformed Inkling MTP depth in {name}: {err}"))
110}
111
112fn is_inkling_mtp_source_tensor(name: &str) -> bool {
113 name.starts_with("model.mtp.layers.")
114}
115
116fn normalize_inkling_mtp_source_name(name: &str, layer_start: u32) -> Result<Option<String>> {
117 let Some(rest) = name.strip_prefix("model.mtp.layers.") else {
118 return Ok(None);
119 };
120 let Some((depth, suffix)) = rest.split_once('.') else {
121 bail!("malformed Inkling MTP tensor name {name}");
122 };
123 let depth = depth
124 .parse::<u32>()
125 .map_err(|err| anyhow!("malformed Inkling MTP depth in {name}: {err}"))?;
126 let layer = layer_start
127 .checked_add(depth)
128 .ok_or_else(|| anyhow!("Inkling MTP layer id overflow for {name}"))?;
129 let suffix = match suffix {
130 "embed_norm.weight" => "enorm.weight",
131 "hidden_norm.weight" => "hnorm.weight",
132 "input_proj.weight" => "eh_proj.weight",
133 value => value.strip_prefix("transformer_block.").unwrap_or(value),
134 };
135 Ok(Some(format!("model.layers.{layer}.{suffix}")))
136}
137
138fn map_mtp_source_tensor(name: &str) -> Result<Option<String>> {
139 let mapped = match name {
140 "pre_projection" | "pre_projection.weight" => "nextn.pre_projection.weight".to_string(),
141 "post_projection" | "post_projection.weight" => "nextn.post_projection.weight".to_string(),
142 "d2t" | "d2t.weight" => "d2t.weight".to_string(),
143 _ => {
144 let Some(layer) = HfLayerTensor::parse(name)? else {
145 return Ok(None);
146 };
147 let bid = layer.layer;
148 match layer.suffix {
149 "eh_proj" | "eh_proj.weight" => format!("blk.{bid}.nextn.eh_proj.weight"),
150 "embed_tokens" | "embed_tokens.weight" => {
151 format!("blk.{bid}.nextn.embed_tokens.weight")
152 }
153 "enorm" | "enorm.weight" => format!("blk.{bid}.nextn.enorm.weight"),
154 "hnorm" | "hnorm.weight" => format!("blk.{bid}.nextn.hnorm.weight"),
155 "shared_head.head" | "shared_head.head.weight" | "shared_head.output.weight" => {
156 format!("blk.{bid}.nextn.shared_head_head.weight")
157 }
158 "shared_head.norm" | "shared_head.norm.weight" => {
159 format!("blk.{bid}.nextn.shared_head_norm.weight")
160 }
161 _ => return Ok(None),
162 }
163 }
164 };
165 Ok(Some(mapped))
166}
167
168fn is_qwen_mtp_source_tensor(name: &str) -> bool {
169 let name = name.strip_prefix("model.").unwrap_or(name);
170 name.starts_with("mtp.")
171}
172
173fn normalize_qwen_mtp_source_name(name: &str, layer_start: u32) -> Result<Option<String>> {
174 let name = name.strip_prefix("model.").unwrap_or(name);
175 if !name.starts_with("mtp.") {
176 return Ok(None);
177 }
178 let parts = name.splitn(4, '.').collect::<Vec<_>>();
179 if parts.len() == 4 && parts[1] == "layers" {
180 let mtp_idx = parts[2]
181 .parse::<u32>()
182 .map_err(|err| anyhow!("malformed MTP layer id in {name}: {err}"))?;
183 return Ok(Some(format!(
184 "model.layers.{}.{}",
185 layer_start + mtp_idx,
186 parts[3]
187 )));
188 }
189 if parts.len() == 3 {
190 let suffix = match parts[1] {
191 "fc" => "eh_proj",
192 "pre_fc_norm_embedding" => "enorm",
193 "pre_fc_norm_hidden" => "hnorm",
194 "norm" => "shared_head.norm",
195 _ => return Ok(None),
196 };
197 return Ok(Some(format!(
198 "model.layers.{layer_start}.{suffix}.{}",
199 parts[2]
200 )));
201 }
202 Ok(None)
203}
204
205struct HfLayerTensor<'a> {
206 layer: u32,
207 suffix: &'a str,
208}
209
210impl<'a> HfLayerTensor<'a> {
211 fn parse(name: &'a str) -> Result<Option<Self>> {
212 let Some(rest) = name.strip_prefix("model.layers.") else {
213 return Ok(None);
214 };
215 let Some((layer, suffix)) = rest.split_once('.') else {
216 bail!("malformed layer tensor name {name}");
217 };
218 let layer = layer
219 .parse::<u32>()
220 .map_err(|err| anyhow!("malformed layer id in {name}: {err}"))?;
221 Ok(Some(Self { layer, suffix }))
222 }
223
224 fn map(&self) -> Result<String> {
225 let bid = self.layer;
226 match self.suffix {
227 "input_layernorm.weight" => Ok(format!("blk.{bid}.attn_norm.weight")),
228 "post_attention_layernorm.weight" => Ok(format!("blk.{bid}.ffn_norm.weight")),
229 "self_attn.q_proj.weight" => Ok(format!("blk.{bid}.attn_q.weight")),
230 "self_attn.k_proj.weight" => Ok(format!("blk.{bid}.attn_k.weight")),
231 "self_attn.v_proj.weight" => Ok(format!("blk.{bid}.attn_v.weight")),
232 "self_attn.q_proj.bias" => Ok(format!("blk.{bid}.attn_q.bias")),
233 "self_attn.k_proj.bias" => Ok(format!("blk.{bid}.attn_k.bias")),
234 "self_attn.v_proj.bias" => Ok(format!("blk.{bid}.attn_v.bias")),
235 "self_attn.o_proj.weight" => Ok(format!("blk.{bid}.attn_output.weight")),
236 "mamba.in_proj.weight" => Ok(format!("blk.{bid}.ssm_in.weight")),
237 "mamba.conv1d.weight" => Ok(format!("blk.{bid}.ssm_conv1d.weight")),
238 "mamba.conv1d.bias" => Ok(format!("blk.{bid}.ssm_conv1d.bias")),
239 "mamba.dt_bias" => Ok(format!("blk.{bid}.ssm_dt.bias")),
240 "mamba.A_log" => Ok(format!("blk.{bid}.ssm_a")),
241 "mamba.D" => Ok(format!("blk.{bid}.ssm_d")),
242 "mamba.norm.weight" => Ok(format!("blk.{bid}.ssm_norm.weight")),
243 "mamba.out_proj.weight" => Ok(format!("blk.{bid}.ssm_out.weight")),
244 "self_attn.q_norm.weight" => Ok(format!("blk.{bid}.attn_q_norm.weight")),
245 "self_attn.k_norm.weight" => Ok(format!("blk.{bid}.attn_k_norm.weight")),
246 "self_attn.q_a_proj.weight" => Ok(format!("blk.{bid}.attn_q_a.weight")),
247 "self_attn.q_b_proj.weight" => Ok(format!("blk.{bid}.attn_q_b.weight")),
248 "self_attn.q_a_layernorm.weight" => Ok(format!("blk.{bid}.attn_q_a_norm.weight")),
249 "self_attn.kv_a_proj_with_mqa.weight" => Ok(format!("blk.{bid}.attn_kv_a_mqa.weight")),
250 "self_attn.kv_b_proj.weight" => Ok(format!("blk.{bid}.attn_kv_b.weight")),
251 "self_attn.kv_a_layernorm.weight" => Ok(format!("blk.{bid}.attn_kv_a_norm.weight")),
252 "attn_norm.weight" => Ok(format!("blk.{bid}.attn_norm.weight")),
253 "attn.wq_du.weight" => Ok(format!("blk.{bid}.attn_q.weight")),
254 "attn.wk_dv.weight" => Ok(format!("blk.{bid}.attn_k.weight")),
255 "attn.wv_dv.weight" => Ok(format!("blk.{bid}.attn_v.weight")),
256 "attn.wr_du.weight" => Ok(format!("blk.{bid}.attn_r.weight")),
257 "attn.wo_ud.weight" => Ok(format!("blk.{bid}.attn_output.weight")),
258 "attn.q_norm.weight" => Ok(format!("blk.{bid}.attn_q_norm.weight")),
259 "attn.k_norm.weight" => Ok(format!("blk.{bid}.attn_k_norm.weight")),
260 "attn.rel_logits_proj.proj" | "attn.rel_logits_proj.weight" => {
261 Ok(format!("blk.{bid}.attn_rel_proj.weight"))
262 }
263 "attn.k_sconv.weight" => Ok(format!("blk.{bid}.shortconv_k.weight")),
264 "attn.v_sconv.weight" => Ok(format!("blk.{bid}.shortconv_v.weight")),
265 "attn_sconv.weight" => Ok(format!("blk.{bid}.shortconv_attn.weight")),
266 "mlp_sconv.weight" => Ok(format!("blk.{bid}.shortconv_mlp.weight")),
267 "mlp_norm.weight" => Ok(format!("blk.{bid}.ffn_norm.weight")),
268 "mlp.w2_md.weight" => Ok(format!("blk.{bid}.ffn_down.weight")),
269 "mlp.global_scale" | "mlp.global_scale.weight" => {
270 Ok(format!("blk.{bid}.ffn_gscale.weight"))
271 }
272 "mlp.w13_dn.weight" => {
273 bail!("Inkling fused w13 tensor requires streaming deinterleave")
274 }
275 "self_attn.indexer.k_norm.weight" => Ok(format!("blk.{bid}.indexer.k_norm.weight")),
276 "self_attn.indexer.k_norm.bias" => Ok(format!("blk.{bid}.indexer.k_norm.bias")),
277 "self_attn.indexer.weights_proj.weight" => Ok(format!("blk.{bid}.indexer.proj.weight")),
278 "self_attn.indexer.wk.weight" => Ok(format!("blk.{bid}.indexer.attn_k.weight")),
279 "self_attn.indexer.wq_b.weight" => Ok(format!("blk.{bid}.indexer.attn_q_b.weight")),
280 "mlp.down_proj.weight" => Ok(format!("blk.{bid}.ffn_down.weight")),
281 "mlp.gate_proj.weight" => Ok(format!("blk.{bid}.ffn_gate.weight")),
282 "mlp.up_proj.weight" => Ok(format!("blk.{bid}.ffn_up.weight")),
283 "shared_mlp.output_linear.weight" => Ok(format!("blk.{bid}.ffn_down.weight")),
284 "mlp.gate.weight" => Ok(format!("blk.{bid}.ffn_gate_inp.weight")),
285 "mlp.shared_expert_gate" => Ok(format!("blk.{bid}.ffn_gate_inp_shexp.weight")),
286 "mlp.gate.e_score_correction_bias" => Ok(format!("blk.{bid}.exp_probs_b.bias")),
287 "mlp.shared_expert.down_proj.weight" => Ok(format!("blk.{bid}.ffn_down_shexp.weight")),
288 "mlp.shared_expert.gate_proj.weight" => Ok(format!("blk.{bid}.ffn_gate_shexp.weight")),
289 "mlp.shared_expert.up_proj.weight" => Ok(format!("blk.{bid}.ffn_up_shexp.weight")),
290 "mlp.shared_experts.down_proj.weight" => Ok(format!("blk.{bid}.ffn_down_shexp.weight")),
291 "mlp.shared_experts.gate_proj.weight" => Ok(format!("blk.{bid}.ffn_gate_shexp.weight")),
292 "mlp.shared_experts.up_proj.weight" => Ok(format!("blk.{bid}.ffn_up_shexp.weight")),
293 suffix if suffix.starts_with("mlp.experts.") => {
294 bail!(
295 "expert source tensor {suffix} requires streaming expert merge before GGUF write"
296 )
297 }
298 suffix => bail!("no HF->GGUF mapping for layer tensor suffix {suffix:?}"),
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn maps_glm_moe_lite_direct_tensor_names() {
309 assert_eq!(
310 TensorNameMap::HfToGguf
311 .map_tensor_name("model.embed_tokens.weight")
312 .unwrap(),
313 "token_embd.weight"
314 );
315 assert_eq!(
316 TensorNameMap::HfToGguf
317 .map_tensor_name("model.layers.7.self_attn.kv_a_proj_with_mqa.weight")
318 .unwrap(),
319 "blk.7.attn_kv_a_mqa.weight"
320 );
321 assert_eq!(
322 TensorNameMap::HfToGguf
323 .map_tensor_name("model.layers.7.mlp.shared_experts.gate_proj.weight")
324 .unwrap(),
325 "blk.7.ffn_gate_shexp.weight"
326 );
327 }
328
329 #[test]
330 fn maps_glm_dsa_indexer_tensor_names() {
331 assert_eq!(
332 TensorNameMap::HfToGguf
333 .map_tensor_name("model.layers.7.self_attn.indexer.k_norm.weight")
334 .unwrap(),
335 "blk.7.indexer.k_norm.weight"
336 );
337 assert_eq!(
338 TensorNameMap::HfToGguf
339 .map_tensor_name("model.layers.7.self_attn.indexer.k_norm.bias")
340 .unwrap(),
341 "blk.7.indexer.k_norm.bias"
342 );
343 assert_eq!(
344 TensorNameMap::HfToGguf
345 .map_tensor_name("model.layers.7.self_attn.indexer.weights_proj.weight")
346 .unwrap(),
347 "blk.7.indexer.proj.weight"
348 );
349 assert_eq!(
350 TensorNameMap::HfToGguf
351 .map_tensor_name("model.layers.7.self_attn.indexer.wk.weight")
352 .unwrap(),
353 "blk.7.indexer.attn_k.weight"
354 );
355 assert_eq!(
356 TensorNameMap::HfToGguf
357 .map_tensor_name("model.layers.7.self_attn.indexer.wq_b.weight")
358 .unwrap(),
359 "blk.7.indexer.attn_q_b.weight"
360 );
361 }
362
363 #[test]
364 fn rejects_expert_source_tensors_until_merge_exists() {
365 let err = TensorNameMap::HfToGguf
366 .map_tensor_name("model.layers.1.mlp.experts.0.down_proj.weight")
367 .unwrap_err()
368 .to_string();
369
370 assert!(err.contains("requires streaming expert merge"));
371 }
372
373 #[test]
374 fn maps_qwen_dense_tensor_names() {
375 assert_eq!(
376 TensorNameMap::HfToGguf
377 .map_tensor_name("model.layers.3.self_attn.q_proj.weight")
378 .unwrap(),
379 "blk.3.attn_q.weight"
380 );
381 assert_eq!(
382 TensorNameMap::HfToGguf
383 .map_tensor_name("model.layers.3.self_attn.k_norm.weight")
384 .unwrap(),
385 "blk.3.attn_k_norm.weight"
386 );
387 assert_eq!(
388 TensorNameMap::HfToGguf
389 .map_tensor_name("model.layers.3.self_attn.v_proj.bias")
390 .unwrap(),
391 "blk.3.attn_v.bias"
392 );
393 assert_eq!(
394 TensorNameMap::HfToGguf
395 .map_tensor_name("model.layers.3.mlp.up_proj.weight")
396 .unwrap(),
397 "blk.3.ffn_up.weight"
398 );
399 }
400
401 #[test]
402 fn maps_qwen2_moe_shared_expert_tensor_names() {
403 assert_eq!(
404 TensorNameMap::HfToGguf
405 .map_tensor_name("model.layers.3.mlp.shared_expert_gate")
406 .unwrap(),
407 "blk.3.ffn_gate_inp_shexp.weight"
408 );
409 assert_eq!(
410 TensorNameMap::HfToGguf
411 .map_tensor_name("model.layers.3.mlp.shared_expert.gate_proj.weight")
412 .unwrap(),
413 "blk.3.ffn_gate_shexp.weight"
414 );
415 assert_eq!(
416 TensorNameMap::HfToGguf
417 .map_tensor_name("model.layers.3.mlp.shared_expert.down_proj.weight")
418 .unwrap(),
419 "blk.3.ffn_down_shexp.weight"
420 );
421 assert_eq!(
422 TensorNameMap::HfToGguf
423 .map_tensor_name("model.layers.3.mlp.shared_expert.up_proj.weight")
424 .unwrap(),
425 "blk.3.ffn_up_shexp.weight"
426 );
427 }
428
429 #[test]
430 fn recognizes_known_nextn_mtp_source_tensors() {
431 for name in [
432 "pre_projection",
433 "pre_projection.weight",
434 "post_projection",
435 "d2t",
436 "model.layers.47.eh_proj.weight",
437 "model.layers.47.embed_tokens.weight",
438 "model.layers.47.enorm.weight",
439 "model.layers.47.hnorm.weight",
440 "model.layers.47.shared_head.head.weight",
441 "model.layers.47.shared_head.norm.weight",
442 ] {
443 assert!(is_mtp_source_tensor(name), "{name}");
444 }
445 assert!(!is_mtp_source_tensor(
446 "model.layers.0.self_attn.q_proj.weight"
447 ));
448 assert!(!is_mtp_source_tensor("model.embed_tokens.weight"));
449 }
450
451 #[test]
452 fn maps_known_nextn_mtp_source_tensors() {
453 assert_eq!(
454 TensorNameMap::HfToGguf
455 .map_tensor_name("model.layers.47.eh_proj.weight")
456 .unwrap(),
457 "blk.47.nextn.eh_proj.weight"
458 );
459 assert_eq!(
460 TensorNameMap::HfToGguf
461 .map_tensor_name("model.layers.47.shared_head.norm.weight")
462 .unwrap(),
463 "blk.47.nextn.shared_head_norm.weight"
464 );
465 assert_eq!(
466 TensorNameMap::HfToGguf
467 .map_tensor_name("embed_tokens.weight")
468 .unwrap(),
469 "token_embd.weight"
470 );
471 }
472
473 #[test]
474 fn recognizes_and_maps_qwen_style_mtp_source_tensors() {
475 for name in [
476 "mtp.fc.weight",
477 "model.mtp.pre_fc_norm_embedding.weight",
478 "mtp.pre_fc_norm_hidden.weight",
479 "mtp.norm.weight",
480 "mtp.layers.0.self_attn.q_proj.weight",
481 ] {
482 assert!(is_mtp_source_tensor(name), "{name}");
483 }
484 assert_eq!(
485 TensorNameMap::HfToGgufWithMtp { layer_start: 32 }
486 .map_tensor_name("mtp.fc.weight")
487 .unwrap(),
488 "blk.32.nextn.eh_proj.weight"
489 );
490 assert_eq!(
491 TensorNameMap::HfToGgufWithMtp { layer_start: 32 }
492 .map_tensor_name("model.mtp.norm.weight")
493 .unwrap(),
494 "blk.32.nextn.shared_head_norm.weight"
495 );
496 assert_eq!(
497 TensorNameMap::HfToGgufWithMtp { layer_start: 32 }
498 .map_tensor_name("mtp.layers.1.self_attn.q_proj.weight")
499 .unwrap(),
500 "blk.33.attn_q.weight"
501 );
502 }
503
504 #[test]
505 fn recognizes_and_maps_inkling_mtp_tensors() {
506 let map = TensorNameMap::HfToGgufWithMtp { layer_start: 66 };
507 for (source, expected) in [
508 (
509 "model.mtp.layers.0.embed_norm.weight",
510 "blk.66.nextn.enorm.weight",
511 ),
512 (
513 "model.mtp.layers.1.hidden_norm.weight",
514 "blk.67.nextn.hnorm.weight",
515 ),
516 (
517 "model.mtp.layers.2.input_proj.weight",
518 "blk.68.nextn.eh_proj.weight",
519 ),
520 (
521 "model.mtp.layers.3.transformer_block.attn.wq_du.weight",
522 "blk.69.attn_q.weight",
523 ),
524 (
525 "model.mtp.layers.4.transformer_block.attn.rel_logits_proj.proj",
526 "blk.70.attn_rel_proj.weight",
527 ),
528 (
529 "model.mtp.layers.7.transformer_block.mlp.global_scale",
530 "blk.73.ffn_gscale.weight",
531 ),
532 ] {
533 assert!(is_mtp_source_tensor(source));
534 assert_eq!(map.map_tensor_name(source).unwrap(), expected);
535 }
536 assert_eq!(
537 map.map_tensor_name("model.llm.embed_norm.weight").unwrap(),
538 "token_embd_norm.weight"
539 );
540 assert!(is_shared_mtp_context_tensor("model.llm.unembed.weight"));
541 assert!(is_inkling_fused_w13(
542 "model.mtp.layers.0.transformer_block.mlp.w13_dn.weight"
543 ));
544 }
545
546 #[test]
547 fn rejects_inkling_mtp_layer_overflow() {
548 let error = TensorNameMap::HfToGgufWithMtp {
549 layer_start: u32::MAX,
550 }
551 .map_tensor_name("model.mtp.layers.1.embed_norm.weight")
552 .unwrap_err();
553
554 assert!(error.to_string().contains("MTP layer id overflow"));
555 }
556}