1#![allow(unused_imports)]
11
12use anyhow::{Context, Result, bail, ensure};
13use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
14use metrale_model_weights::weights::{WeightDtype, WeightStore};
15
16use super::*;
17
18pub(super) fn load_moe_inner(
19 store: &WeightStore,
20 layer_prefix: &str,
21 num_experts: usize,
22 gpu: &dyn GpuBackend,
23 config: &metrale_config::ModelConfig,
24 variant: Nvfp4Variant,
25 qctx: QuantizeCtx,
26 skip_routed_experts: bool,
27) -> Result<MoeWeights> {
28 let p = format!("{layer_prefix}.mlp");
29 let inter = config.moe_intermediate_size;
30 let h = config.hidden_size;
31
32 let gate = dense(store, &format!("{p}.gate.weight"))?;
33 let shared_expert_gate = dense(store, &format!("{p}.shared_expert_gate.weight"))?;
34
35 let shared_expert = ExpertWeight {
37 gate_proj: quantized_any(
38 store,
39 &format!("{p}.shared_expert.gate_proj"),
40 inter,
41 h,
42 gpu,
43 variant,
44 qctx,
45 )?,
46 up_proj: quantized_any(
47 store,
48 &format!("{p}.shared_expert.up_proj"),
49 inter,
50 h,
51 gpu,
52 variant,
53 qctx,
54 )?,
55 down_proj: quantized_any(
56 store,
57 &format!("{p}.shared_expert.down_proj"),
58 h,
59 inter,
60 gpu,
61 variant,
62 qctx,
63 )?,
64 };
65
66 let mut experts = Vec::with_capacity(num_experts);
67 for e in 0..num_experts {
68 if skip_routed_experts || !config.is_local_expert(e) {
69 experts.push(ExpertWeight::null());
70 } else {
71 experts.push(ExpertWeight {
72 gate_proj: quantized_any(
73 store,
74 &format!("{p}.experts.{e}.gate_proj"),
75 inter,
76 h,
77 gpu,
78 variant,
79 qctx,
80 )?,
81 up_proj: quantized_any(
82 store,
83 &format!("{p}.experts.{e}.up_proj"),
84 inter,
85 h,
86 gpu,
87 variant,
88 qctx,
89 )?,
90 down_proj: quantized_any(
91 store,
92 &format!("{p}.experts.{e}.down_proj"),
93 h,
94 inter,
95 gpu,
96 variant,
97 qctx,
98 )?,
99 });
100 }
101 }
102
103 Ok(MoeWeights {
104 gate,
105 shared_expert,
106 shared_expert_gate,
107 experts,
108 router_pre_norm: None,
109 correction_bias: None,
110 })
111}
112
113pub fn load_moe_mistral(
116 store: &WeightStore,
117 layer: usize,
118 num_experts: usize,
119 gpu: &dyn GpuBackend,
120 config: &metrale_config::ModelConfig,
121) -> Result<MoeWeights> {
122 let p = format!("layers.{layer}");
124
125 let gate = dense(store, &format!("{p}.gate.weight"))
126 .or_else(|_| dense(store, &format!("model.layers.{layer}.gate.weight")))
127 .context("Mistral: MoE gate weight not found")?;
128
129 let se_prefix = format!("{p}.shared_experts");
132 let shared_expert = if store.contains(&format!("{se_prefix}.w1.weight_packed")) {
133 ExpertWeight {
134 gate_proj: quantized_v2(store, &format!("{se_prefix}.w1"), gpu)
135 .context("Mistral: shared expert w1")?,
136 up_proj: quantized_v2(store, &format!("{se_prefix}.w3"), gpu)
137 .context("Mistral: shared expert w3")?,
138 down_proj: quantized_v2(store, &format!("{se_prefix}.w2"), gpu)
139 .context("Mistral: shared expert w2")?,
140 }
141 } else {
142 tracing::warn!("L{layer}: no shared expert weights, using NULL");
143 ExpertWeight::null()
144 };
145 let shared_expert_gate = DenseWeight {
146 weight: metrale_gpu_runtime::gpu::DevicePtr::NULL,
147 };
148
149 let mut experts = Vec::with_capacity(num_experts);
150 for e in 0..num_experts {
151 if config.is_local_expert(e) {
152 let ep = format!("{p}.experts.{e}");
153 let gate_proj = quantized_v2(store, &format!("{ep}.w1"), gpu)
154 .with_context(|| format!("Mistral expert {e}: w1 not found"))?;
155 let up_proj = quantized_v2(store, &format!("{ep}.w3"), gpu)
156 .with_context(|| format!("Mistral expert {e}: w3 not found"))?;
157 let down_proj = quantized_v2(store, &format!("{ep}.w2"), gpu)
158 .with_context(|| format!("Mistral expert {e}: w2 not found"))?;
159 experts.push(ExpertWeight {
160 gate_proj,
161 up_proj,
162 down_proj,
163 });
164 } else {
165 experts.push(ExpertWeight::null());
166 }
167 }
168
169 Ok(MoeWeights {
170 gate,
171 shared_expert,
172 shared_expert_gate,
173 experts,
174 router_pre_norm: None,
175 correction_bias: None,
176 })
177}
178
179pub fn load_mtp(
188 store: &WeightStore,
189 num_experts: usize,
190 gpu: &dyn GpuBackend,
191 variant: Nvfp4Variant,
192) -> Result<MtpWeights> {
193 let p = "mtp.layers.0.self_attn";
194 let mlp = "mtp.layers.0.mlp";
195
196 let load = |name: &str| -> Result<DenseWeight> {
197 match variant {
198 Nvfp4Variant::Fp8Dequanted => dense_auto(store, name, gpu),
199 Nvfp4Variant::Bf16Raw => dense(store, name),
200 _ => dense_auto(store, name, gpu),
203 }
204 };
205
206 let dense_gate_proj = format!("{mlp}.gate_proj.weight");
209 let moe_router = format!("{mlp}.gate.weight");
210 if store.contains(&dense_gate_proj) && !store.contains(&moe_router) {
211 let dense_ffn = DenseExpertWeight {
212 gate_proj: load(&dense_gate_proj)?,
213 up_proj: load(&format!("{mlp}.up_proj.weight"))?,
214 down_proj: load(&format!("{mlp}.down_proj.weight"))?,
215 };
216 let null = DenseWeight {
217 weight: DevicePtr::NULL,
218 };
219 return Ok(MtpWeights {
220 pre_fc_norm_embedding: dense(store, "mtp.pre_fc_norm_embedding.weight")?,
221 pre_fc_norm_hidden: dense(store, "mtp.pre_fc_norm_hidden.weight")?,
222 fc: load("mtp.fc.weight")?,
223 input_layernorm: dense(store, "mtp.layers.0.input_layernorm.weight")?,
224 q_proj: load(&format!("{p}.q_proj.weight"))?,
225 k_proj: load(&format!("{p}.k_proj.weight"))?,
226 v_proj: load(&format!("{p}.v_proj.weight"))?,
227 o_proj: load(&format!("{p}.o_proj.weight"))?,
228 q_norm: dense(store, &format!("{p}.q_norm.weight"))?,
229 k_norm: dense(store, &format!("{p}.k_norm.weight"))?,
230 post_attn_layernorm: dense(store, "mtp.layers.0.post_attention_layernorm.weight")?,
231 moe_gate: null,
232 shared_expert: DenseExpertWeight {
233 gate_proj: null,
234 up_proj: null,
235 down_proj: null,
236 },
237 shared_expert_gate: null,
238 experts: Vec::new(),
239 dense_ffn: Some(dense_ffn),
240 norm: dense(store, "mtp.norm.weight")?,
241 fp8_experts: None,
242 });
243 }
244
245 let shared_expert = DenseExpertWeight {
246 gate_proj: load(&format!("{mlp}.shared_expert.gate_proj.weight"))?,
247 up_proj: load(&format!("{mlp}.shared_expert.up_proj.weight"))?,
248 down_proj: load(&format!("{mlp}.shared_expert.down_proj.weight"))?,
249 };
250
251 let stacked_gate_up = format!("{mlp}.experts.gate_up_proj");
256 let stacked_down = format!("{mlp}.experts.down_proj");
257 let stacked = store.contains(&stacked_gate_up) && store.contains(&stacked_down);
258 let experts = if stacked {
259 load_mtp_experts_stacked(store, mlp, num_experts)?
260 } else {
261 let mut v = Vec::with_capacity(num_experts);
262 for e in 0..num_experts {
263 v.push(DenseExpertWeight {
264 gate_proj: load(&format!("{mlp}.experts.{e}.gate_proj.weight"))?,
265 up_proj: load(&format!("{mlp}.experts.{e}.up_proj.weight"))?,
266 down_proj: load(&format!("{mlp}.experts.{e}.down_proj.weight"))?,
267 });
268 }
269 v
270 };
271 let fp8_experts =
275 if !stacked && variant != Nvfp4Variant::Bf16Raw && mtp_experts_native_fp8(store, mlp) {
276 Some(load_mtp_fp8_experts(store, mlp, num_experts, gpu)?)
277 } else {
278 None
279 };
280
281 Ok(MtpWeights {
282 pre_fc_norm_embedding: dense(store, "mtp.pre_fc_norm_embedding.weight")?,
283 pre_fc_norm_hidden: dense(store, "mtp.pre_fc_norm_hidden.weight")?,
284 fc: load("mtp.fc.weight")?,
285 input_layernorm: dense(store, "mtp.layers.0.input_layernorm.weight")?,
286 q_proj: load(&format!("{p}.q_proj.weight"))?,
287 k_proj: load(&format!("{p}.k_proj.weight"))?,
288 v_proj: load(&format!("{p}.v_proj.weight"))?,
289 o_proj: load(&format!("{p}.o_proj.weight"))?,
290 q_norm: dense(store, &format!("{p}.q_norm.weight"))?,
291 k_norm: dense(store, &format!("{p}.k_norm.weight"))?,
292 post_attn_layernorm: dense(store, "mtp.layers.0.post_attention_layernorm.weight")?,
293 moe_gate: dense(store, &format!("{mlp}.gate.weight"))?,
294 shared_expert,
295 shared_expert_gate: dense(store, &format!("{mlp}.shared_expert_gate.weight"))?,
296 experts,
297 dense_ffn: None,
298 norm: dense(store, "mtp.norm.weight")?,
299 fp8_experts,
300 })
301}
302
303pub(crate) fn mtp_experts_native_fp8(store: &WeightStore, mlp: &str) -> bool {
307 let weight = format!("{mlp}.experts.0.gate_proj.weight");
308 store
309 .get(&weight)
310 .map(|w| w.dtype == WeightDtype::FP8E4M3)
311 .unwrap_or(false)
312 && store.contains(&format!("{mlp}.experts.0.gate_proj.weight_scale_inv"))
313}
314
315fn load_mtp_fp8_experts(
318 store: &WeightStore,
319 mlp: &str,
320 num_experts: usize,
321 gpu: &dyn GpuBackend,
322) -> Result<MtpFp8Experts> {
323 let proj = |prefix: &str, name: &str| -> Result<Fp8Weight> {
324 load_fp8_block_scaled_as_fp8weight(store, &format!("{prefix}.{name}"), gpu)
325 };
326 let load = |prefix: &str| -> Result<Fp8ExpertWeight> {
327 Ok(Fp8ExpertWeight {
328 gate_proj: proj(prefix, "gate_proj")?,
329 up_proj: proj(prefix, "up_proj")?,
330 down_proj: proj(prefix, "down_proj")?,
331 })
332 };
333 let mut experts = Vec::with_capacity(num_experts);
334 for e in 0..num_experts {
335 experts.push(
336 load(&format!("{mlp}.experts.{e}"))
337 .with_context(|| format!("MTP expert {e}: native FP8 tables"))?,
338 );
339 }
340 let shared_expert =
341 load(&format!("{mlp}.shared_expert")).context("MTP shared expert: native FP8 tables")?;
342 Ok(MtpFp8Experts {
343 experts,
344 shared_expert,
345 })
346}
347
348#[cfg(test)]
349mod mtp_fp8_detect_tests {
350 use super::*;
351 use metrale_model_weights::weights::WeightTensor;
352 use std::collections::HashMap;
353
354 const MLP: &str = "mtp.layers.0.mlp";
355
356 fn store(entries: Vec<(String, WeightDtype)>) -> WeightStore {
357 let mut m = HashMap::new();
358 for (name, dtype) in entries {
359 m.insert(
360 name,
361 WeightTensor {
362 ptr: DevicePtr::NULL,
363 shape: vec![512, 2048],
364 dtype,
365 },
366 );
367 }
368 WeightStore::from_map(m)
369 }
370
371 fn gate0(suffix: &str) -> String {
372 format!("{MLP}.experts.0.gate_proj.{suffix}")
373 }
374
375 #[test]
376 fn fp8_weight_with_block_scale_is_native_fp8() {
377 let s = store(vec![
378 (gate0("weight"), WeightDtype::FP8E4M3),
379 (gate0("weight_scale_inv"), WeightDtype::BF16),
380 ]);
381 assert!(mtp_experts_native_fp8(&s, MLP));
382 }
383
384 #[test]
385 fn bf16_experts_are_not_native_fp8() {
386 let s = store(vec![(gate0("weight"), WeightDtype::BF16)]);
387 assert!(!mtp_experts_native_fp8(&s, MLP));
388 }
389
390 #[test]
391 fn fp8_without_a_block_scale_is_refused() {
392 let s = store(vec![(gate0("weight"), WeightDtype::FP8E4M3)]);
393 assert!(!mtp_experts_native_fp8(&s, MLP));
394 }
395
396 #[test]
397 fn absent_experts_are_not_native_fp8() {
398 assert!(!mtp_experts_native_fp8(&store(vec![]), MLP));
399 }
400}