1use crate::kv_cache::LayerKvCache;
15use crate::linear_core::{
16 GdnCfg, GdnWeights, ShortConvCfg, ShortConvWeights, VmfPhaseCfg, VmfPhaseWeights,
17};
18use crate::pipeline::{
19 AttnKind, DenseFfn, FfnKind, LayerWeights, MoeFfn, MtpModule, Pipeline, PipelineWeights,
20};
21use crate::qtensor::QTensor;
22use crate::sampler::SamplerConfig;
23use crate::tokenizer::Tokenizer;
24use cortiq_core::quant::dequant_tensor;
25use cortiq_core::{CmfError, CmfModel, LayerType, ModelArch};
26use std::sync::Arc;
27
28pub const DECISION_MODEL_REFUSAL: &str =
32 "this is a DECISION model; use `cortiq decide` or `cortiq serve`";
33
34const FLINT_NO_ROPE_LAYERS: [usize; 14] = [0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 48];
35
36fn validate_flint_profile(model: &CmfModel, arch: &ModelArch) -> Result<(), CmfError> {
40 if arch.arch_name != "flint" {
41 return Ok(());
42 }
43 if arch.num_layers != 49
44 || arch.hidden_size != 2048
45 || arch.num_attention_heads != 32
46 || arch.num_kv_heads != 4
47 || arch.head_dim != 128
48 || arch.vocab_size != 150_144
49 {
50 return Err(CmfError::Parse(
51 "Flint geometry is not the released RecursiveSI/flint profile".into(),
52 ));
53 }
54 if arch.sliding_window != Some(513) {
55 return Err(CmfError::Parse(
56 "Flint requires arch.sliding_window=513; run `cortiq repair-flint <model.cmf>` for legacy exports"
57 .into(),
58 ));
59 }
60 let expected_no_rope: Vec<bool> = (0..arch.num_layers)
61 .map(|li| FLINT_NO_ROPE_LAYERS.contains(&li))
62 .collect();
63 if arch.no_rope_layers.as_deref() != Some(expected_no_rope.as_slice()) {
64 return Err(CmfError::Parse(
65 "Flint requires its exact 14-layer no-RoPE schedule; run `cortiq repair-flint <model.cmf>` for legacy exports"
66 .into(),
67 ));
68 }
69 if !arch.attention_sink {
70 return Err(CmfError::Parse(
71 "Flint requires arch.attention_sink=true; run `cortiq repair-flint <model.cmf>` for legacy exports"
72 .into(),
73 ));
74 }
75 let Some(moe) = arch.moe.as_ref() else {
76 return Err(CmfError::Parse("Flint requires an MoE descriptor".into()));
77 };
78 if !(moe.router_sigmoid
79 && moe.norm_topk_prob
80 && moe.num_experts == 256
81 && moe.top_k == 6
82 && moe.moe_intermediate_size == 512
83 && moe.shared_expert_intermediate_size == Some(1024))
84 {
85 return Err(CmfError::Parse(
86 "Flint requires sigmoid top-6 normalized routing with its 256×512 + shared-1024 MoE profile"
87 .into(),
88 ));
89 }
90 for li in 0..arch.num_layers {
91 let name = format!("model.layers.{li}.self_attn.softmax_offset");
92 let Some(sink) = model.tensor(&name) else {
93 return Err(CmfError::MissingTensor(name));
94 };
95 if sink.shape.as_slice() != [arch.num_attention_heads] {
96 return Err(CmfError::Parse(format!(
97 "{name} has shape {:?}, expected [{}]",
98 sink.shape, arch.num_attention_heads
99 )));
100 }
101 }
102 Ok(())
103}
104
105pub enum Overlay<'a> {
108 None,
109 One(&'a str),
110 Blend(&'a [(String, f32)]),
112}
113
114impl Overlay<'_> {
115 fn blend_touches(&self, model: &CmfModel, name: &str) -> bool {
116 match self {
117 Overlay::Blend(list) => list
118 .iter()
119 .any(|(sid, _)| model.tensor(&format!("skill.{sid}.{name}")).is_some()),
120 _ => false,
121 }
122 }
123}
124
125fn dequant_by_name(model: &CmfModel, name: &str) -> Result<Vec<f32>, String> {
126 let entry = model
127 .tensor(name)
128 .ok_or_else(|| format!("tensor '{name}' not found in CMF directory"))?;
129 let mut out = vec![0.0f32; entry.n_elems()];
130 dequant_tensor(entry, model.entry_bytes(entry), &mut out)?;
131 Ok(out)
132}
133
134fn blend_f32(model: &CmfModel, name: &str, list: &[(String, f32)]) -> Result<Vec<f32>, String> {
137 let mut acc: Option<Vec<f32>> = None;
138 for (sid, w) in list {
139 let sname = format!("skill.{sid}.{name}");
140 let src = if model.tensor(&sname).is_some() {
141 &sname
142 } else {
143 name
144 };
145 let t = dequant_by_name(model, src)?;
146 match &mut acc {
147 None => {
148 let mut t = t;
149 for v in t.iter_mut() {
150 *v *= w;
151 }
152 acc = Some(t);
153 }
154 Some(a) => {
155 for (av, tv) in a.iter_mut().zip(&t) {
156 *av += w * tv;
157 }
158 }
159 }
160 }
161 acc.ok_or_else(|| "empty blend".into())
162}
163
164pub(crate) fn load_f32(model: &CmfModel, name: &str, ov: &Overlay) -> Result<Vec<f32>, String> {
166 if ov.blend_touches(model, name) {
167 if let Overlay::Blend(list) = ov {
168 return blend_f32(model, name, list);
169 }
170 }
171 let skill = match ov {
172 Overlay::One(s) => Some(*s),
173 _ => None,
174 };
175 let entry = model
176 .resolve_tensor(name, skill)
177 .ok_or_else(|| format!("tensor '{name}' not found in CMF directory"))?;
178 let bytes = model.entry_bytes(entry);
179 let mut out = vec![0.0f32; entry.n_elems()];
180 dequant_tensor(entry, bytes, &mut out)?;
181 Ok(out)
182}
183
184pub(crate) fn build_layer_ffn(
190 model: &Arc<CmfModel>,
191 arch: &ModelArch,
192 li: usize,
193 force_f32: bool,
194 ov: &Overlay,
195) -> Result<FfnKind, CmfError> {
196 build_ffn_at(model, arch, &format!("model.layers.{li}."), force_f32, ov)
197}
198
199pub(crate) fn build_ffn_at(
204 model: &Arc<CmfModel>,
205 arch: &ModelArch,
206 prefix: &str,
207 force_f32: bool,
208 ov: &Overlay,
209) -> Result<FfnKind, CmfError> {
210 let prefix = prefix.to_string();
211 let load_dense = |p: &str| -> Result<DenseFfn, CmfError> {
212 let gate_proj = load_matrix(model, &format!("{p}gate_proj.weight"), force_f32, ov)?;
213 let up_proj = load_matrix(model, &format!("{p}up_proj.weight"), force_f32, ov)?;
214 let down_proj = load_matrix(model, &format!("{p}down_proj.weight"), force_f32, ov)?;
215 let inter = gate_proj.rows();
219 if up_proj.rows() != inter || down_proj.cols() != inter {
220 return Err(CmfError::Parse(format!(
221 "{p}: FFN dims disagree (gate.rows={inter}, up.rows={}, \
222 down.cols={}); all three must equal inter'",
223 up_proj.rows(),
224 down_proj.cols()
225 )));
226 }
227 if down_proj.rows() != arch.hidden_size {
228 return Err(CmfError::Parse(format!(
229 "{p}: down_proj.rows={} != hidden_size={}",
230 down_proj.rows(),
231 arch.hidden_size
232 )));
233 }
234 let dt_name = format!("{p}down_proj.t.weight");
237 let down_t = match model.tensor(&dt_name) {
238 Some(_) => Some(load_matrix(model, &dt_name, force_f32, ov)?),
239 None => None,
240 };
241 if let Some(t) = &down_t
242 && (t.rows() != inter || t.cols() != arch.hidden_size)
243 {
244 return Err(CmfError::Parse(format!(
245 "{p}down_proj.t: [{}, {}] != [{inter}, {}]",
246 t.rows(),
247 t.cols(),
248 arch.hidden_size
249 )));
250 }
251 let mut segs = Vec::new();
257 let mut start = inter;
258 for k in 1.. {
259 let gn = format!("{p}gate_proj.tube{k}.weight");
260 if model.tensor(&gn).is_none() {
261 break;
262 }
263 let gate = load_matrix(model, &gn, force_f32, ov)?;
264 let up = load_matrix(model, &format!("{p}up_proj.tube{k}.weight"), force_f32, ov)?;
265 let down = load_matrix(
266 model,
267 &format!("{p}down_proj.tube{k}.weight"),
268 force_f32,
269 ov,
270 )?;
271 let width = gate.rows();
272 if up.rows() != width || down.cols() != width || down.rows() != arch.hidden_size {
273 return Err(CmfError::Parse(format!(
274 "{p}tube{k}: dims disagree (gate.rows={width}, up.rows={}, \
275 down=[{}, {}], hidden={})",
276 up.rows(),
277 down.rows(),
278 down.cols(),
279 arch.hidden_size
280 )));
281 }
282 segs.push(crate::pipeline::FfnSeg {
283 gate,
284 up,
285 down,
286 start,
287 width,
288 });
289 start += width;
290 }
291 Ok(DenseFfn {
292 gate_proj,
293 up_proj,
294 down_proj,
295 act: crate::pipeline::Act::from_arch_full(arch),
296 down_t,
297 segs,
298 })
299 };
300 let router_name = format!("{prefix}mlp.gate.weight");
301 let resonance_moe = model.tensor(&router_name).is_none()
305 && arch.moe.as_ref().is_some_and(|m| m.router_resonance)
306 && model
307 .tensor(&format!("{prefix}mlp.experts.0.gate_proj.weight"))
308 .is_some();
309 if model.tensor(&router_name).is_none() && !resonance_moe {
310 return Ok(FfnKind::Dense(load_dense(&format!("{prefix}mlp."))?));
311 }
312 let cfg = arch.moe.as_ref().ok_or_else(|| {
313 CmfError::Parse(format!(
314 "{router_name} present but header has no arch.moe block"
315 ))
316 })?;
317 let mut experts = Vec::new();
322 for e in 0..cfg.num_experts {
323 if model
324 .tensor(&format!("{prefix}mlp.experts.{e}.gate_proj.weight"))
325 .is_none()
326 {
327 break;
328 }
329 experts.push(load_dense(&format!("{prefix}mlp.experts.{e}."))?);
330 }
331 if experts.is_empty() {
332 return Err(CmfError::Parse(format!(
333 "{prefix}: router present but no expert tensors"
334 )));
335 }
336 let e0 = experts.len();
341 let grown_loads = if resonance_moe {
342 mount_growth(model, &prefix, ov, &load_dense)?
343 } else {
344 Vec::new()
345 };
346 let mut grown_desc: Vec<(Vec<f32>, Vec<f32>, f32, f32)> = Vec::new();
347 let mut grown: Vec<crate::pipeline::GrownExpert> = Vec::new();
348 for g in grown_loads {
349 experts.push(g.ffn);
350 grown_desc.push((g.mu, g.u, g.bias, g.shell));
351 grown.push(g.meta);
352 }
353 let shared = if model
354 .tensor(&format!("{prefix}mlp.shared_expert.gate_proj.weight"))
355 .is_some()
356 {
357 let gate_name = format!("{prefix}mlp.shared_expert_gate.weight");
358 Some((
359 load_dense(&format!("{prefix}mlp.shared_expert."))?,
360 if model.tensor(&gate_name).is_some() {
361 Some(load_matrix(model, &gate_name, force_f32, ov)?)
362 } else {
363 None
364 },
365 ))
366 } else {
367 None
368 };
369 let bias_name = format!("{prefix}mlp.expert_bias");
372 let expert_bias = if model.tensor(&bias_name).is_some() {
373 Some(load_f32(model, &bias_name, ov).map_err(CmfError::Parse)?)
374 } else {
375 None
376 };
377 let top_k = std::env::var("CMF_MOE_TOPK")
383 .ok()
384 .and_then(|v| v.parse::<usize>().ok())
385 .filter(|&k| k >= 1 && k <= cfg.top_k)
386 .inspect(|k| tracing::info!("MoE top_k override: {} (header {})", k, cfg.top_k))
387 .unwrap_or(cfg.top_k);
388 let route_tau = std::env::var("CMF_MOE_TAU")
390 .ok()
391 .and_then(|v| v.parse::<f32>().ok())
392 .filter(|&t| t > 0.0 && t < 1.0)
393 .inspect(|t| tracing::info!("MoE adaptive routing: tau {t}"));
394 let mask = moe_task_mask(model, &prefix, experts.len());
395 let router = if resonance_moe {
396 QTensor::from_f32(
398 vec![0.0; experts.len() * arch.hidden_size],
399 experts.len(),
400 arch.hidden_size,
401 )
402 } else {
403 load_matrix(model, &router_name, force_f32, ov)?
404 };
405 if router.rows() != experts.len() {
406 return Err(CmfError::Parse(format!(
407 "{router_name}: {} rows != {} experts",
408 router.rows(),
409 experts.len()
410 )));
411 }
412 let top_k = top_k.min(experts.len());
413 let pes_name = format!("{prefix}mlp.per_expert_scale");
417 let per_expert_scale = if model.tensor(&pes_name).is_some() {
418 Some(load_f32(model, &pes_name, ov).map_err(CmfError::Parse)?)
419 } else {
420 None
421 };
422 let router_input_norm = per_expert_scale.is_some();
423 let per_expert = model
428 .tensor(&format!("{prefix}mlp.experts.0.desc.mu"))
429 .is_some();
430 if !per_expert && !grown.is_empty() {
431 return Err(CmfError::Parse(format!(
432 "{prefix}: expert_append records need per-expert descriptors \
433 (mlp.experts.0.desc.mu); the stacked mlp.desc.* form cannot be grown"
434 )));
435 }
436 let resonance = if per_expert {
437 let ne_d = e0;
441 let hidden = arch.hidden_size;
442 let mut mu = Vec::with_capacity(experts.len() * hidden);
443 let mut u = Vec::new();
444 let mut bias = Vec::with_capacity(experts.len());
445 let mut k = 0usize;
446 for e in 0..ne_d {
447 let m = load_f32(model, &format!("{prefix}mlp.experts.{e}.desc.mu"), ov)
448 .map_err(CmfError::Parse)?;
449 if m.len() != hidden {
450 return Err(CmfError::Parse(format!(
451 "{prefix}mlp.experts.{e}.desc.mu: {} != {hidden}",
452 m.len()
453 )));
454 }
455 mu.extend_from_slice(&m);
456 let un = format!("{prefix}mlp.experts.{e}.desc.u");
457 if model.tensor(&un).is_some() {
458 let ue = load_f32(model, &un, ov).map_err(CmfError::Parse)?;
459 let ke = ue.len() / hidden.max(1);
460 if e == 0 {
461 k = ke;
462 }
463 if ke != k {
464 return Err(CmfError::Parse(format!("{un}: rank {ke} != {k}")));
465 }
466 u.extend_from_slice(&ue);
467 }
468 let bn = format!("{prefix}mlp.experts.{e}.desc.bias");
469 bias.push(if model.tensor(&bn).is_some() {
470 load_f32(model, &bn, ov)
471 .map_err(CmfError::Parse)?
472 .first()
473 .copied()
474 .unwrap_or(0.0)
475 } else {
476 0.0
477 });
478 }
479 let mut shell = vec![f32::INFINITY; ne_d];
480 for (gm, gu, gb, gs) in &grown_desc {
481 if gm.len() != hidden || gu.len() != k * hidden {
482 return Err(CmfError::Parse(format!(
483 "{prefix}: grown expert descriptor {}×{} != trunk {hidden}×{k}",
484 gm.len(),
485 gu.len() / hidden.max(1)
486 )));
487 }
488 mu.extend_from_slice(gm);
489 u.extend_from_slice(gu);
490 bias.push(*gb);
491 shell.push(*gs);
492 }
493 Some(crate::pipeline::Resonance {
494 mu,
495 u,
496 k,
497 bias,
498 shell,
499 })
500 } else if model.tensor(&format!("{prefix}mlp.desc.mu")).is_some() {
501 let mu = load_f32(model, &format!("{prefix}mlp.desc.mu"), ov).map_err(CmfError::Parse)?;
502 let ne_d = experts.len();
503 let hidden = arch.hidden_size;
504 if mu.len() != ne_d * hidden {
505 return Err(CmfError::Parse(format!(
506 "{prefix}mlp.desc.mu: {} != {ne_d}×{hidden}",
507 mu.len()
508 )));
509 }
510 let u_name = format!("{prefix}mlp.desc.u");
511 let (u, k) = if model.tensor(&u_name).is_some() {
512 let u = load_f32(model, &u_name, ov).map_err(CmfError::Parse)?;
513 let k = u.len() / (ne_d * hidden).max(1);
514 (u, k)
515 } else {
516 (Vec::new(), 0)
517 };
518 let b_name = format!("{prefix}mlp.desc.bias");
519 let bias = if model.tensor(&b_name).is_some() {
520 load_f32(model, &b_name, ov).map_err(CmfError::Parse)?
521 } else {
522 vec![0.0; ne_d]
523 };
524 Some(crate::pipeline::Resonance {
525 mu,
526 u,
527 k,
528 bias,
529 shell: Vec::new(),
530 })
531 } else {
532 None
533 };
534 let moe = MoeFfn {
535 grown,
536 router,
537 experts,
538 top_k,
539 route_tau,
540 norm_topk_prob: cfg.norm_topk_prob,
541 router_sigmoid: cfg.router_sigmoid,
542 sigmoid_denom_eps: match arch.arch_name.as_str() {
547 "flint" | "minimax_m3_vl" => 1e-20,
548 _ => 1e-6,
549 },
550 expert_bias,
551 routed_scaling: cfg.routed_scaling_factor.unwrap_or(1.0),
552 shared,
553 stats: std::cell::RefCell::new(Vec::new()),
554 act_sq: std::cell::RefCell::new(Vec::new()),
555 act_rows: std::cell::RefCell::new(Vec::new()),
556 mask,
557 per_expert_scale,
558 router_input_norm,
559 resonance,
560 };
561 if model
564 .tensor(&format!("{prefix}mlp.gate_proj.weight"))
565 .is_some()
566 {
567 let norm = |suffix: &str| -> Result<Vec<f32>, CmfError> {
568 load_f32(model, &format!("{prefix}{suffix}.weight"), ov).map_err(CmfError::Parse)
569 };
570 return Ok(FfnKind::DenseMoe(Box::new(crate::pipeline::DenseMoeFfn {
571 dense: load_dense(&format!("{prefix}mlp."))?,
572 moe,
573 post_norm_1: norm("post_feedforward_layernorm_1")?,
574 pre_norm_2: norm("pre_feedforward_layernorm_2")?,
575 post_norm_2: norm("post_feedforward_layernorm_2")?,
576 })));
577 }
578 Ok(FfnKind::Moe(moe))
579}
580
581fn validate_generic_mtp_ffn_width(
590 cfg: &cortiq_core::MtpConfig,
591 ffn: &FfnKind,
592) -> Result<(), CmfError> {
593 let Some(widths) = cfg.ffn_intermediate_sizes.as_deref() else {
594 return Ok(());
595 };
596 if widths.len() != cfg.num_layers {
597 return Err(CmfError::Parse(format!(
598 "MTP ffn_intermediate_sizes has {} entries, expected {} (one per draft layer)",
599 widths.len(),
600 cfg.num_layers
601 )));
602 }
603 if widths.iter().any(|&width| width == 0) {
604 return Err(CmfError::Parse(
605 "MTP ffn_intermediate_sizes must contain only positive widths".into(),
606 ));
607 }
608 let FfnKind::Dense(dense) = ffn else {
609 return Err(CmfError::Parse(
610 "MTP ffn_intermediate_sizes is valid only for a dense MTP FFN; omit it for routed MTP"
611 .into(),
612 ));
613 };
614 if !dense.segs.is_empty() {
615 return Err(CmfError::Parse(
616 "MTP ffn_intermediate_sizes requires an unsplit dense MTP FFN; omit it for tube-segmented MTP"
617 .into(),
618 ));
619 }
620 let actual = dense.gate_proj.rows();
621 let declared = widths[0];
622 if declared != actual {
623 return Err(CmfError::Parse(format!(
624 "MTP ffn_intermediate_sizes[0]={declared}, but model.mtp.layers.0.mlp.gate_proj.weight has {actual} rows"
625 )));
626 }
627 Ok(())
628}
629
630#[derive(Clone, Copy, PartialEq, Eq, Debug)]
634pub enum GrowthMode {
635 Active,
637 All,
640 Off,
642}
643
644impl GrowthMode {
645 pub fn label(self) -> &'static str {
646 match self {
647 Self::Active => "active",
648 Self::All => "all",
649 Self::Off => "off",
650 }
651 }
652
653 pub fn admits(self, status: Option<&str>) -> bool {
655 match self {
656 Self::Off => false,
657 Self::Active => status == Some("active"),
658 Self::All => status != Some("retired"),
659 }
660 }
661}
662
663pub fn growth_mode() -> GrowthMode {
665 match std::env::var("CMF_GROWTH")
666 .ok()
667 .as_deref()
668 .map(|v| v.trim().to_ascii_lowercase())
669 .as_deref()
670 {
671 Some("off") | Some("0") | Some("none") => GrowthMode::Off,
672 Some("all") => GrowthMode::All,
673 _ => GrowthMode::Active,
674 }
675}
676
677pub fn mounted_growth_records(
681 header: &cortiq_core::CmfHeader,
682) -> Vec<(usize, &cortiq_core::SkillRecord)> {
683 let mode = growth_mode();
684 header
685 .skills
686 .iter()
687 .enumerate()
688 .filter(|(_, s)| {
689 s.kind.as_deref() == Some(cortiq_core::knowledge::skill_kind::EXPERT_APPEND)
690 && mode.admits(s.status.as_deref())
691 })
692 .collect()
693}
694
695struct GrownLoad {
697 ffn: DenseFfn,
698 mu: Vec<f32>,
699 u: Vec<f32>,
700 bias: f32,
701 shell: f32,
702 meta: crate::pipeline::GrownExpert,
703}
704
705fn layer_of_prefix(prefix: &str) -> Option<usize> {
708 prefix
709 .strip_prefix("model.layers.")?
710 .strip_suffix('.')?
711 .parse()
712 .ok()
713}
714
715fn mount_growth(
721 model: &Arc<CmfModel>,
722 prefix: &str,
723 ov: &Overlay,
724 load_dense: &dyn Fn(&str) -> Result<DenseFfn, CmfError>,
725) -> Result<Vec<GrownLoad>, CmfError> {
726 use cortiq_core::knowledge::expert_leaf;
727 let Some(layer) = layer_of_prefix(prefix) else {
728 return Ok(Vec::new());
729 };
730 let records = mounted_growth_records(&model.header);
731 let mut out = Vec::new();
732 for (at, rec) in records {
733 if !rec.layers.contains(&layer) {
734 continue;
735 }
736 let plan = cortiq_core::expert_append_layout(&model.header, &model.tensors, at, rec)
737 .map_err(|e| CmfError::Parse(format!("{prefix}: growth record '{}': {e}", rec.id)))?;
738 let mut by_expert: std::collections::BTreeMap<usize, Vec<&cortiq_core::ExpertTensorSpec>> =
739 Default::default();
740 for p in plan.iter().filter(|p| p.layer == layer) {
741 by_expert.entry(p.expert).or_default().push(p);
742 }
743 let first = out.len();
744 for (e, leaves) in &by_expert {
745 let name = |leaf: &str| -> Result<String, CmfError> {
746 leaves
747 .iter()
748 .find(|p| p.leaf == leaf)
749 .map(|p| p.name.clone())
750 .ok_or_else(|| {
751 CmfError::Parse(format!(
752 "{prefix}: growth record '{}' expert {e}: layout has no '{leaf}'",
753 rec.id
754 ))
755 })
756 };
757 let ffn = load_dense(&format!("skill.{}.{prefix}mlp.experts.{e}.", rec.id))?;
758 let mu = load_f32(model, &name(expert_leaf::MU)?, ov).map_err(CmfError::Parse)?;
759 let u = if leaves.iter().any(|p| p.leaf == expert_leaf::U) {
760 load_f32(model, &name(expert_leaf::U)?, ov).map_err(CmfError::Parse)?
761 } else {
762 Vec::new()
763 };
764 let scalar = |leaf: &str| -> Result<f32, CmfError> {
765 load_f32(model, &name(leaf)?, ov)
766 .map_err(CmfError::Parse)?
767 .first()
768 .copied()
769 .ok_or_else(|| {
770 CmfError::Parse(format!(
771 "{prefix}: growth record '{}' expert {e}: empty '{leaf}'",
772 rec.id
773 ))
774 })
775 };
776 let bias = scalar(expert_leaf::BIAS)?;
777 let shell = scalar(expert_leaf::SHELL)?;
778 if !shell.is_finite() {
779 return Err(CmfError::Parse(format!(
780 "{prefix}: growth record '{}' expert {e}: desc.shell {shell} is not finite",
781 rec.id
782 )));
783 }
784 out.push(GrownLoad {
785 ffn,
786 mu,
787 u,
788 bias,
789 shell,
790 meta: crate::pipeline::GrownExpert {
791 record: rec.id.clone(),
792 record_index: at,
793 layer,
794 expert: *e,
795 },
796 });
797 }
798 let declared: Vec<usize> = by_expert.keys().copied().collect();
799 tracing::info!(
800 "growth: layer {layer}: mounted expert_append '{}' (status {}) — {} experts, declared {:?}, shells {:?}",
801 rec.id,
802 rec.status.as_deref().unwrap_or("?"),
803 by_expert.len(),
804 declared,
805 out[first..].iter().map(|g| g.shell).collect::<Vec<_>>()
806 );
807 }
808 Ok(out)
809}
810
811pub(crate) fn moe_task_mask(
822 _model: &std::sync::Arc<CmfModel>,
823 prefix: &str,
824 ne: usize,
825) -> Option<Vec<bool>> {
826 use std::sync::OnceLock;
827 static CFG: OnceLock<Option<(std::collections::HashMap<usize, Vec<u64>>, f64)>> =
828 OnceLock::new();
829 let cfg = CFG.get_or_init(|| {
830 let path = std::path::PathBuf::from(std::env::var("CMF_MOE_MASK").ok()?);
831 let shown = path.display();
832 let text = std::fs::read_to_string(&path)
833 .map_err(|e| tracing::warn!("CMF_MOE_MASK: cannot read {shown}: {e}"))
834 .ok()?;
835 let map: std::collections::HashMap<String, Vec<u64>> = serde_json::from_str(&text)
836 .map_err(|e| tracing::warn!("CMF_MOE_MASK: bad JSON in {shown}: {e}"))
837 .ok()?;
838 let weighted = map
843 .values()
844 .any(|row| row.iter().copied().sum::<u64>() >= 1_000_000);
845 let cover = std::env::var("CMF_MOE_MASK_COVER")
846 .ok()
847 .and_then(|v| v.parse::<f64>().ok())
848 .filter(|&c| c > 0.0 && c <= 1.0)
849 .unwrap_or(if weighted { 0.925 } else { 0.9 });
850 tracing::info!("MoE task mask: {shown}, cover {cover}");
851 Some((
852 map.into_iter()
853 .filter_map(|(k, v)| Some((k.parse::<usize>().ok()?, v)))
854 .collect(),
855 cover,
856 ))
857 });
858 let (stats, cover) = cfg.as_ref()?;
859 let li: usize = prefix
861 .split("layers.")
862 .nth(1)?
863 .split('.')
864 .next()?
865 .parse()
866 .ok()?;
867 let counts = stats.get(&li)?;
868 if counts.len() != ne {
869 tracing::warn!(
870 "CMF_MOE_MASK: layer {li} has {} counts, model has {ne} experts — skipped",
871 counts.len()
872 );
873 return None;
874 }
875 let total: u64 = counts.iter().sum();
876 if total == 0 {
877 return None;
878 }
879 let mut order: Vec<usize> = (0..ne).collect();
880 order.sort_unstable_by_key(|&e| std::cmp::Reverse(counts[e]));
881 let mut mask = vec![false; ne];
882 let mut acc = 0u64;
883 let mut kept = 0usize;
884 for &e in &order {
885 mask[e] = true;
886 acc += counts[e];
887 kept += 1;
888 if (acc as f64) >= cover * (total as f64) {
889 break;
890 }
891 }
892 tracing::info!(
893 "MoE task mask L{li}: {kept}/{ne} experts for {:.0}% mass",
894 cover * 100.0
895 );
896 Some(mask)
897}
898
899pub(crate) fn load_matrix(
900 model: &Arc<CmfModel>,
901 name: &str,
902 force_f32: bool,
903 ov: &Overlay,
904) -> Result<QTensor, CmfError> {
905 if ov.blend_touches(model, name) {
909 if let Overlay::Blend(list) = ov {
910 let entry = model
911 .tensor(name)
912 .ok_or_else(|| CmfError::MissingTensor(name.to_string()))?;
913 let data =
914 blend_f32(model, name, list).map_err(|e| CmfError::Parse(format!("blend: {e}")))?;
915 return Ok(QTensor::from_f32(data, entry.shape[0], entry.shape[1]));
916 }
917 }
918 let skill = match ov {
919 Overlay::One(s) => Some(*s),
920 _ => None,
921 };
922 let name: &str = &match skill {
925 Some(sid) if model.tensor(&format!("skill.{sid}.{name}")).is_some() => {
926 format!("skill.{sid}.{name}")
927 }
928 _ => name.to_string(),
929 };
930 let err = |e: String| CmfError::Parse(format!("weight loading: {e}"));
931 if force_f32 {
932 let entry = model
933 .tensor(name)
934 .ok_or_else(|| CmfError::MissingTensor(name.to_string()))?;
935 if entry.shape.len() != 2 {
936 return Err(err(format!("'{name}' is not 2-D")));
937 }
938 let data = load_f32(model, name, &Overlay::None).map_err(err)?;
939 Ok(QTensor::from_f32(data, entry.shape[0], entry.shape[1]))
940 } else {
941 QTensor::from_model(model, name).map_err(err)
942 }
943}
944
945impl Pipeline {
946 pub fn from_model(
948 model: &Arc<CmfModel>,
949 sampler_config: SamplerConfig,
950 ) -> Result<Self, CmfError> {
951 Self::from_model_with_skill(model, sampler_config, None)
952 }
953
954 pub fn from_encoder_model(
967 model: &Arc<CmfModel>,
968 sampler_config: SamplerConfig,
969 ) -> Result<Self, CmfError> {
970 Self::from_model_with_overlay(model, sampler_config, &Overlay::None, true)
971 }
972
973 pub fn from_model_with_skill(
979 model: &Arc<CmfModel>,
980 sampler_config: SamplerConfig,
981 skill: Option<&str>,
982 ) -> Result<Self, CmfError> {
983 match skill {
984 Some(s) => {
985 Self::from_model_with_overlay(model, sampler_config, &Overlay::One(s), false)
986 }
987 None => Self::from_model_with_overlay(model, sampler_config, &Overlay::None, false),
988 }
989 }
990
991 pub fn from_model_with_blend(
994 model: &Arc<CmfModel>,
995 sampler_config: SamplerConfig,
996 blend: &[(String, f32)],
997 ) -> Result<Self, CmfError> {
998 Self::from_model_with_overlay(model, sampler_config, &Overlay::Blend(blend), false)
999 }
1000
1001 fn skill_file_guard(model: &CmfModel) -> Result<(), CmfError> {
1002 if model.required_features & cortiq_core::format::features::DECISION != 0 {
1006 return Err(CmfError::Parse(DECISION_MODEL_REFUSAL.into()));
1007 }
1008 if model.required_features & cortiq_core::format::features::SKILL_FILE != 0 {
1011 return Err(CmfError::Parse(
1012 "this file is a standalone SKILL, not a runnable model — attach it: \
1013 cortiq skill apply <base.cmf> <this file> -o specialist.cmf"
1014 .into(),
1015 ));
1016 }
1017 Ok(())
1018 }
1019
1020 fn from_model_with_overlay(
1021 model: &Arc<CmfModel>,
1022 sampler_config: SamplerConfig,
1023 ov: &Overlay,
1024 encoder_only: bool,
1025 ) -> Result<Self, CmfError> {
1026 Self::skill_file_guard(model)?;
1030 if let Some(dir) = model.path.parent() {
1035 crate::gpu::set_cache_dir(dir.to_path_buf());
1036 }
1037 let skill = match ov {
1043 Overlay::One(s) => Some(*s),
1044 _ => None,
1045 };
1046 if let Some(sid) = skill {
1047 let known = model.header.skills.iter().any(|s| s.id == sid)
1048 || model.skill_tensors(sid).next().is_some();
1049 if !known {
1050 return Err(CmfError::Parse(format!(
1051 "skill '{sid}' not in this container (header.skills: {:?})",
1052 model
1053 .header
1054 .skills
1055 .iter()
1056 .map(|s| &s.id)
1057 .collect::<Vec<_>>()
1058 )));
1059 }
1060 tracing::info!(
1061 "skill '{sid}': {} replacement tensors overlaid",
1062 model.skill_tensors(sid).count()
1063 );
1064 }
1065 let arch = model.arch().clone();
1066 let err = |e: String| CmfError::Parse(format!("weight loading: {e}"));
1067 if let Some(heads) = &arch.attention_heads_per_layer {
1068 if heads.len() != arch.num_layers {
1069 return Err(CmfError::Parse(format!(
1070 "arch.attention_heads_per_layer has {} entries, expected {}",
1071 heads.len(),
1072 arch.num_layers
1073 )));
1074 }
1075 if let Some((li, &nh)) = heads
1076 .iter()
1077 .enumerate()
1078 .find(|(_, nh)| **nh == 0 || **nh % arch.num_kv_heads != 0)
1079 {
1080 return Err(CmfError::Parse(format!(
1081 "layer {li} has {nh} Q heads, which must be nonzero and divisible by {} KV heads",
1082 arch.num_kv_heads
1083 )));
1084 }
1085 }
1086 if arch
1087 .layer_types
1088 .iter()
1089 .any(|t| matches!(t, LayerType::SlidingAttention))
1090 && arch.sliding_window.is_none()
1091 {
1092 return Err(CmfError::Parse(
1093 "model has SlidingAttention layers but no arch.sliding_window".into(),
1094 ));
1095 }
1096 validate_flint_profile(model, &arch)?;
1097
1098 let heads_masked = model.masks.masks.iter().any(|m| {
1108 m.head_masks.iter().any(|row| {
1109 let mut bits = 0usize;
1110 for &b in row.iter() {
1111 bits += b.count_ones() as usize;
1112 }
1113 !row.is_empty() && bits < arch.num_attention_heads
1114 })
1115 });
1116 let force_f32 = heads_masked; let mut tokenizer = if let Some(vocab_bytes) = &model.vocab {
1120 Tokenizer::from_bytes(vocab_bytes)
1121 .map_err(|e| CmfError::Parse(format!("embedded tokenizer: {e}")))?
1122 } else if let Some(g) = &model.header.genome {
1123 return Err(CmfError::Parse(format!(
1127 "genome '{}' carries no embedded tokenizer (VOCAB) — a sidecar tokenizer.json \
1128 is not part of the genome; refusing",
1129 g.id
1130 )));
1131 } else {
1132 let sidecar = model.path.with_file_name("tokenizer.json");
1133 if sidecar.exists() {
1134 Tokenizer::from_file(&sidecar)
1135 .map_err(|e| CmfError::Parse(format!("sidecar tokenizer: {e}")))?
1136 } else {
1137 tracing::warn!("no tokenizer in file or sidecar — using byte-level fallback");
1138 Tokenizer::byte_level()
1139 }
1140 };
1141 if let Some(tc) = &model.header.tokenizer_config {
1143 tokenizer.chat_template = tc.chat_template.clone();
1144 tokenizer.extra_eos.extend(tc.eos_token_ids.iter().copied());
1145 if tokenizer.bos_token_id.is_none() {
1146 tokenizer.bos_token_id = tc.bos_token_id;
1147 }
1148 tracing::info!(
1149 "chat bundle: template {} chars, {} stop ids",
1150 tc.chat_template.as_deref().map(str::len).unwrap_or(0),
1151 tc.eos_token_ids.len()
1152 );
1153 }
1154 if arch.arch_name.to_lowercase().contains("gemma") && tokenizer.bos_token_id.is_some() {
1158 tokenizer.add_bos = true;
1159 }
1160
1161 let embed_tokens = load_matrix(model, "model.embed_tokens.weight", false, ov)?;
1163 let final_norm = if arch.qwen4_exp.is_some() || arch.dory.is_some() {
1164 vec![0.0; arch.hidden_size]
1168 } else {
1169 load_f32(model, "model.norm.weight", ov).map_err(err)?
1170 };
1171 let lm_head = if model.tensor("lm_head.weight").is_some() {
1172 load_matrix(model, "lm_head.weight", false, ov)?
1173 } else if arch.tie_word_embeddings {
1174 load_matrix(model, "model.embed_tokens.weight", false, ov)?
1176 } else if encoder_only {
1177 QTensor::from_f32(vec![0.0; arch.hidden_size], 1, arch.hidden_size)
1182 } else {
1183 return Err(CmfError::MissingTensor(
1184 "lm_head.weight (and tie_word_embeddings is false)".into(),
1185 ));
1186 };
1187
1188 let has_linear = arch
1190 .layer_types
1191 .iter()
1192 .any(|t| matches!(t, LayerType::LinearAttention));
1193 let mut vmf_cfg = None;
1194 let mut gdn_cfg = None;
1195 let mut phase_delta_layers: Option<Vec<usize>> = None;
1200 if !has_linear
1201 && arch
1202 .linear_core
1203 .as_ref()
1204 .is_some_and(|lc| lc.phase_delta_layers.is_some())
1205 {
1206 return Err(CmfError::Parse(
1207 "phase_delta_layers is only valid with LinearAttention and \
1208 vmf_phase_delta_v1"
1209 .into(),
1210 ));
1211 }
1212 if has_linear {
1213 let lc = arch.linear_core.as_ref().ok_or_else(|| {
1214 CmfError::Parse(
1215 "model has LinearAttention layers but no arch.linear_core — \
1216 reconvert with the current converter"
1217 .into(),
1218 )
1219 })?;
1220 let need = |v: Option<usize>, name: &str| {
1221 v.ok_or_else(|| CmfError::Parse(format!("linear core needs arch.{name}")))
1222 };
1223 match lc.kind.as_str() {
1224 "vmf_phase" => {
1225 if lc.phase_delta_layers.is_some() {
1226 return Err(CmfError::Parse(
1227 "legacy vmf_phase cannot carry phase_delta_layers; use vmf_phase_delta_v1"
1228 .into(),
1229 ));
1230 }
1231 vmf_cfg = Some(VmfPhaseCfg {
1232 num_heads: lc.num_heads,
1233 nphase: need(lc.nphase, "linear_core.nphase")?,
1234 value_head_dim: lc.value_head_dim,
1235 hidden_size: arch.hidden_size,
1236 phase_mass: std::env::var("CMF_PHASE_MASS")
1239 .ok()
1240 .and_then(|v| v.parse().ok())
1241 .unwrap_or(0.0),
1242 });
1243 }
1244 "vmf_phase_delta_v1" => {
1245 let mut selected = lc.phase_delta_layers.clone().ok_or_else(|| {
1246 CmfError::Parse(
1247 "vmf_phase_delta_v1 requires a non-empty phase_delta_layers selector"
1248 .into(),
1249 )
1250 })?;
1251 if selected.is_empty() {
1252 return Err(CmfError::Parse(
1253 "vmf_phase_delta_v1 phase_delta_layers is empty".into(),
1254 ));
1255 }
1256 if arch.layer_types.len() != arch.num_layers {
1257 return Err(CmfError::Parse(format!(
1258 "phase_delta layer schedule has {} entries, expected {}",
1259 arch.layer_types.len(),
1260 arch.num_layers
1261 )));
1262 }
1263 selected.sort_unstable();
1264 for pair in selected.windows(2) {
1265 if pair[0] == pair[1] {
1266 return Err(CmfError::Parse(format!(
1267 "phase_delta_layers contains duplicate layer {}",
1268 pair[0]
1269 )));
1270 }
1271 }
1272 for &li in &selected {
1273 if li >= arch.num_layers {
1274 return Err(CmfError::Parse(format!(
1275 "phase_delta layer {li} out of range for {} layers",
1276 arch.num_layers
1277 )));
1278 }
1279 if !matches!(arch.layer_types[li], LayerType::LinearAttention) {
1280 return Err(CmfError::Parse(format!(
1281 "phase_delta layer {li} is not a LinearAttention layer"
1282 )));
1283 }
1284 }
1285 let nphase = need(lc.nphase, "linear_core.nphase")?;
1286 if lc.num_heads == 0 || nphase == 0 || lc.value_head_dim == 0 {
1287 return Err(CmfError::Parse(
1288 "vmf_phase_delta_v1 requires positive heads, nphase, and value_head_dim"
1289 .into(),
1290 ));
1291 }
1292 phase_delta_layers = Some(selected);
1293 vmf_cfg = Some(VmfPhaseCfg {
1294 num_heads: lc.num_heads,
1295 nphase,
1296 value_head_dim: lc.value_head_dim,
1297 hidden_size: arch.hidden_size,
1298 phase_mass: 0.0,
1303 });
1304 }
1305 "gated_delta_net" => {
1306 if lc.phase_delta_layers.is_some() {
1307 return Err(CmfError::Parse(
1308 "gated_delta_net cannot carry phase_delta_layers".into(),
1309 ));
1310 }
1311 gdn_cfg = Some(GdnCfg {
1312 num_v_heads: lc.num_heads,
1313 num_k_heads: need(arch.linear_num_key_heads, "linear_num_key_heads")?,
1314 key_head_dim: need(arch.linear_key_head_dim, "linear_key_head_dim")?,
1315 value_head_dim: lc.value_head_dim,
1316 conv_kernel: need(arch.linear_conv_kernel_dim, "linear_conv_kernel_dim")?,
1317 hidden_size: arch.hidden_size,
1318 rms_eps: arch.rms_norm_eps,
1319 output_gate_sigmoid: false,
1320 });
1321 }
1322 other => {
1323 return Err(CmfError::Parse(format!(
1324 "unknown linear core '{other}' (this runtime executes: \
1325 gated_delta_net, vmf_phase, vmf_phase_delta_v1)"
1326 )));
1327 }
1328 }
1329 }
1330
1331 let has_kda = arch.layer_types.iter().any(|t| matches!(t, LayerType::Kda));
1333 let kda_cfg = if has_kda {
1334 let need = |v: Option<usize>, name: &str| {
1335 v.ok_or_else(|| CmfError::Parse(format!("KDA core needs arch.{name}")))
1336 };
1337 Some(crate::linear_core::KdaCfg {
1338 num_heads: need(arch.linear_num_key_heads, "linear_num_key_heads")?,
1339 head_k_dim: need(arch.linear_key_head_dim, "linear_key_head_dim")?,
1340 head_v_dim: need(arch.linear_value_head_dim, "linear_value_head_dim")?,
1341 conv_kernel: need(arch.linear_conv_kernel_dim, "linear_conv_kernel_dim")?,
1342 hidden_size: arch.hidden_size,
1343 rms_eps: arch.rms_norm_eps,
1344 })
1345 } else {
1346 None
1347 };
1348
1349 let has_short_conv = arch
1351 .layer_types
1352 .iter()
1353 .any(|t| matches!(t, LayerType::ShortConv));
1354 let short_conv_cfg = if has_short_conv {
1355 Some(ShortConvCfg {
1356 hidden_size: arch.hidden_size,
1357 kernel: arch.linear_conv_kernel_dim.ok_or_else(|| {
1358 CmfError::Parse(
1359 "model has ShortConv layers but no arch.linear_conv_kernel_dim — \
1360 reconvert with the current converter"
1361 .into(),
1362 )
1363 })?,
1364 })
1365 } else {
1366 None
1367 };
1368
1369 let load_full_attn = |prefix: &str, layer: Option<usize>| -> Result<AttnKind, CmfError> {
1371 let t = |suffix: &str| load_matrix(model, &format!("{prefix}{suffix}"), force_f32, ov);
1372 let n = |suffix: &str| -> Option<Vec<f32>> {
1373 model
1374 .tensor(&format!("{prefix}{suffix}"))
1375 .and_then(|_| load_f32(model, &format!("{prefix}{suffix}"), ov).ok())
1376 };
1377 if let Some(mla) = arch.mla.as_ref() {
1379 let (q_proj, q_a, q_a_norm) = if mla.q_lora_rank.is_some() {
1381 (
1382 t("self_attn.q_b_proj.weight")?,
1383 Some(t("self_attn.q_a_proj.weight")?),
1384 Some(n("self_attn.q_a_layernorm.weight").ok_or_else(|| {
1385 CmfError::Parse(format!("{prefix}: MLA needs q_a_layernorm"))
1386 })?),
1387 )
1388 } else {
1389 (t("self_attn.q_proj.weight")?, None, None)
1390 };
1391 let hd = mla.qk_rope_head_dim + mla.qk_nope_head_dim;
1392 let nh = q_proj.rows() / hd;
1393 let mut scale = 1.0 / (hd as f32).sqrt();
1396 if let Some(y) = arch.yarn.as_ref() {
1397 if let Some(m) = y.mscale_all_dim.filter(|&m| m > 0.0) {
1398 let ms = 0.1 * m * y.factor.ln() + 1.0;
1399 scale *= ms * ms;
1400 }
1401 }
1402 return Ok(AttnKind::Mla(Box::new(crate::pipeline::MlaWeights {
1403 q_proj,
1404 q_a,
1405 q_a_norm,
1406 kv_a: t("self_attn.kv_a_proj_with_mqa.weight")?,
1407 kv_a_norm: n("self_attn.kv_a_layernorm.weight").ok_or_else(|| {
1408 CmfError::Parse(format!("{prefix}: MLA needs kv_a_layernorm"))
1409 })?,
1410 kv_b: t("self_attn.kv_b_proj.weight")?,
1411 o_proj: t("self_attn.o_proj.weight")?,
1412 nh,
1413 qk_rope: mla.qk_rope_head_dim,
1414 qk_nope: mla.qk_nope_head_dim,
1415 v_dim: mla.v_head_dim,
1416 lora: mla.kv_lora_rank,
1417 scale,
1418 nope: mla.nope,
1419 })));
1420 }
1421 let wq = t("self_attn.q_proj.weight")?;
1422 let nh = layer
1423 .and_then(|li| {
1424 arch.attention_heads_per_layer
1425 .as_ref()
1426 .and_then(|v| v.get(li).copied())
1427 })
1428 .unwrap_or(arch.num_attention_heads);
1429 let output_gate = arch.global_head_dim.is_none() && wq.rows() == 2 * nh * arch.head_dim;
1433 let is_global_layer = arch.global_head_dim.is_some()
1436 && layer.is_some_and(|li| {
1437 arch.sliding_window_pattern
1438 .is_some_and(|p| p > 0 && (li + 1) % p == 0)
1439 });
1440 let expect = if is_global_layer {
1441 nh * arch.global_head_dim.unwrap_or(arch.head_dim)
1442 } else {
1443 nh * arch.head_dim
1444 };
1445 if !output_gate && wq.rows() != expect {
1446 return Err(CmfError::Parse(format!(
1447 "{prefix}self_attn.q_proj.weight rows={} != heads({nh}) * head_dim({})",
1448 wq.rows(),
1449 expect / nh.max(1)
1450 )));
1451 }
1452 let gate_name = format!("{prefix}self_attn.g_proj.weight");
1453 let softplus_gate = if model.tensor(&gate_name).is_some() {
1454 let gate = load_matrix(model, &gate_name, force_f32, ov)?;
1455 if gate.cols() != arch.hidden_size {
1456 return Err(CmfError::Parse(format!(
1457 "{gate_name} cols={} != hidden_size ({})",
1458 gate.cols(),
1459 arch.hidden_size
1460 )));
1461 }
1462 let per_head = if gate.rows() == nh {
1463 true
1464 } else if gate.rows() == nh * arch.head_dim {
1465 false
1466 } else {
1467 return Err(CmfError::Parse(format!(
1468 "{gate_name} rows={} must equal heads ({nh}) or heads*head_dim ({})",
1469 gate.rows(),
1470 nh * arch.head_dim
1471 )));
1472 };
1473 Some((gate, per_head))
1474 } else {
1475 None
1476 };
1477 let bias = match (
1479 n("self_attn.q_proj.bias"),
1480 n("self_attn.k_proj.bias"),
1481 n("self_attn.v_proj.bias"),
1482 ) {
1483 (Some(a), Some(b), Some(c)) => Some((a, b, c)),
1484 _ => None,
1485 };
1486 let wk = t("self_attn.k_proj.weight")?;
1487 let wv = t("self_attn.v_proj.weight")?;
1488 let wo = t("self_attn.o_proj.weight")?;
1489 let (nkv_l, hd_l) = if is_global_layer {
1497 (
1498 arch.num_global_kv_heads.unwrap_or(arch.num_kv_heads),
1499 arch.global_head_dim.unwrap_or(arch.head_dim),
1500 )
1501 } else {
1502 (
1503 layer
1504 .and_then(|li| {
1505 arch.kv_heads_per_layer
1506 .as_ref()
1507 .and_then(|v| v.get(li).copied())
1508 })
1509 .unwrap_or(arch.num_kv_heads),
1510 arch.head_dim,
1511 )
1512 };
1513 let vd = if is_global_layer {
1514 hd_l
1515 } else {
1516 arch.v_head_dim.unwrap_or(hd_l)
1517 };
1518 let shape_err = |what: String| {
1519 CmfError::Parse(format!(
1520 "{prefix}self_attn: {what} (heads {nh}, kv heads {nkv_l}, head_dim {hd_l}, \
1521 v_head_dim {vd}) — file and header disagree"
1522 ))
1523 };
1524 if nkv_l == 0 || nh % nkv_l != 0 {
1525 return Err(shape_err(format!(
1526 "{nkv_l} KV heads must be nonzero and divide {nh} Q heads"
1527 )));
1528 }
1529 if vd == 0 || vd > hd_l {
1530 return Err(shape_err(format!("v_head_dim {vd} must be in 1..={hd_l}")));
1531 }
1532 if wk.rows() != nkv_l * hd_l {
1533 return Err(shape_err(format!(
1534 "k_proj rows={} != kv_heads*head_dim={}",
1535 wk.rows(),
1536 nkv_l * hd_l
1537 )));
1538 }
1539 if wv.rows() != nkv_l * vd {
1540 return Err(shape_err(format!(
1541 "v_proj rows={} != kv_heads*v_head_dim={}",
1542 wv.rows(),
1543 nkv_l * vd
1544 )));
1545 }
1546 if wo.cols() != nh * vd {
1547 return Err(shape_err(format!(
1548 "o_proj cols={} != heads*v_head_dim={}",
1549 wo.cols(),
1550 nh * vd
1551 )));
1552 }
1553 if vd < hd_l && (output_gate || softplus_gate.is_some()) {
1554 return Err(shape_err(
1555 "an attention output gate with V heads narrower than Q/K is not supported"
1556 .into(),
1557 ));
1558 }
1559 Ok(AttnKind::Full {
1560 wq,
1561 wk,
1562 wv,
1563 wo,
1564 q_norm: n("self_attn.q_norm.weight"),
1565 k_norm: n("self_attn.k_norm.weight"),
1566 output_gate,
1567 softplus_gate,
1568 bias,
1569 })
1570 };
1571
1572 let load_linear_attn = |prefix: &str, li: usize| -> Result<AttnKind, CmfError> {
1573 if gdn_cfg.is_some() {
1574 let t = |suffix: &str| {
1576 load_matrix(
1577 model,
1578 &format!("{prefix}linear_attn.{suffix}"),
1579 force_f32,
1580 ov,
1581 )
1582 };
1583 let f = |suffix: &str| {
1584 load_f32(model, &format!("{prefix}linear_attn.{suffix}"), ov).map_err(err)
1585 };
1586 return Ok(AttnKind::LinearGdn(GdnWeights {
1587 in_proj_qkv: t("in_proj_qkv.weight")?,
1588 in_proj_z: t("in_proj_z.weight")?,
1589 in_proj_a: t("in_proj_a.weight")?,
1590 in_proj_b: t("in_proj_b.weight")?,
1591 conv1d: f("conv1d.weight")?,
1592 a_log: f("A_log")?,
1593 dt_bias: f("dt_bias")?,
1594 norm: f("norm.weight")?,
1595 out_proj: t("out_proj.weight")?,
1596 }));
1597 }
1598 let t = |suffix: &str| {
1599 load_matrix(model, &format!("{prefix}vmf_attn.{suffix}"), force_f32, ov)
1600 };
1601 let a_log = load_f32(model, &format!("{prefix}vmf_attn.A_log"), ov).map_err(err)?;
1602 let k_gate = if model
1606 .tensor(&format!("{prefix}vmf_attn.k_gate.weight"))
1607 .is_some()
1608 {
1609 Some((
1610 t("k_gate.weight")?,
1611 load_f32(model, &format!("{prefix}vmf_attn.k_gate.bias"), ov).map_err(err)?,
1612 ))
1613 } else {
1614 None
1615 };
1616 let conv = if model
1619 .tensor(&format!("{prefix}vmf_attn.conv1d.weight"))
1620 .is_some()
1621 {
1622 Some(load_f32(model, &format!("{prefix}vmf_attn.conv1d.weight"), ov).map_err(err)?)
1623 } else {
1624 None
1625 };
1626 let thq = t("thq.weight")?;
1627 let thk = t("thk.weight")?;
1628 let v_proj = t("v_proj.weight")?;
1629 let out_proj = t("out_proj.weight")?;
1630 let phase_delta = phase_delta_layers
1631 .as_ref()
1632 .is_some_and(|layers| layers.binary_search(&li).is_ok());
1633 if phase_delta {
1634 let cfg = vmf_cfg.ok_or_else(|| {
1635 CmfError::Parse("phase_delta layer has no VMF geometry".into())
1636 })?;
1637 let expect = |name: &str, got: (usize, usize), want: (usize, usize)| {
1638 if got != want {
1639 Err(CmfError::Parse(format!(
1640 "phase_delta {name} geometry [{}, {}] != [{}, {}]",
1641 got.0, got.1, want.0, want.1
1642 )))
1643 } else {
1644 Ok(())
1645 }
1646 };
1647 expect(
1648 "thq",
1649 (thq.rows(), thq.cols()),
1650 (cfg.num_heads * cfg.nphase, cfg.hidden_size),
1651 )?;
1652 expect(
1653 "thk",
1654 (thk.rows(), thk.cols()),
1655 (cfg.num_heads * cfg.nphase, cfg.hidden_size),
1656 )?;
1657 expect(
1658 "v_proj",
1659 (v_proj.rows(), v_proj.cols()),
1660 (cfg.num_heads * cfg.value_head_dim, cfg.hidden_size),
1661 )?;
1662 expect(
1663 "out_proj",
1664 (out_proj.rows(), out_proj.cols()),
1665 (cfg.hidden_size, cfg.num_heads * cfg.value_head_dim),
1666 )?;
1667 if a_log.len() != cfg.num_heads * 2 * cfg.nphase {
1668 return Err(CmfError::Parse(format!(
1669 "phase_delta A_log length {} != {}",
1670 a_log.len(),
1671 cfg.num_heads * 2 * cfg.nphase
1672 )));
1673 }
1674 if let Some((kw, kb)) = &k_gate {
1675 expect(
1676 "k_gate",
1677 (kw.rows(), kw.cols()),
1678 (cfg.num_heads, cfg.hidden_size),
1679 )?;
1680 if kb.len() != cfg.num_heads {
1681 return Err(CmfError::Parse(format!(
1682 "phase_delta k_gate.bias length {} != {}",
1683 kb.len(),
1684 cfg.num_heads
1685 )));
1686 }
1687 }
1688 if let Some(c) = &conv {
1689 let n = model
1690 .tensor(&format!("{prefix}vmf_attn.conv1d.weight"))
1691 .ok_or_else(|| {
1692 CmfError::Parse(
1693 "phase_delta conv tensor disappeared during load".into(),
1694 )
1695 })?;
1696 if n.shape.len() != 3
1697 || n.shape[0] != cfg.hidden_size
1698 || n.shape[1] != 1
1699 || n.shape[2] < 2
1700 || c.len() != n.shape.iter().product::<usize>()
1701 {
1702 return Err(CmfError::Parse(format!(
1703 "phase_delta conv geometry {:?} / {} bytes is incompatible",
1704 n.shape,
1705 c.len()
1706 )));
1707 }
1708 }
1709 }
1710 Ok(AttnKind::Linear(VmfPhaseWeights {
1711 thq,
1712 conv,
1713 thk,
1714 v_proj,
1715 out_proj,
1716 decay: a_log.iter().map(|&a| (-(a as f64).exp()).exp()).collect(),
1717 k_gate,
1718 phase_delta,
1719 }))
1720 };
1721
1722 let load_short_conv = |prefix: &str| -> Result<AttnKind, CmfError> {
1726 let t = |suffix: &str| {
1727 load_matrix(
1728 model,
1729 &format!("{prefix}short_conv.{suffix}"),
1730 force_f32,
1731 ov,
1732 )
1733 };
1734 Ok(AttnKind::ShortConv(ShortConvWeights {
1735 in_proj: t("in_proj.weight")?,
1736 conv: load_f32(model, &format!("{prefix}short_conv.conv.weight"), ov)
1737 .map_err(err)?,
1738 out_proj: t("out_proj.weight")?,
1739 }))
1740 };
1741
1742 let load_kda = |prefix: &str| -> Result<AttnKind, CmfError> {
1746 let t = |suffix: &str| {
1747 load_matrix(model, &format!("{prefix}kda_attn.{suffix}"), force_f32, ov)
1748 };
1749 let f = |suffix: &str| {
1750 load_f32(model, &format!("{prefix}kda_attn.{suffix}"), ov).map_err(err)
1751 };
1752 let gate = if model
1753 .tensor(&format!("{prefix}kda_attn.g_proj.weight"))
1754 .is_some()
1755 {
1756 crate::linear_core::KdaOutGate::Full(t("g_proj.weight")?)
1757 } else {
1758 crate::linear_core::KdaOutGate::LowRank(
1759 t("g_a_proj.weight")?,
1760 t("g_b_proj.weight")?,
1761 )
1762 };
1763 Ok(AttnKind::Kda(Box::new(crate::linear_core::KdaWeights {
1764 q_proj: t("q_proj.weight")?,
1765 k_proj: t("k_proj.weight")?,
1766 v_proj: t("v_proj.weight")?,
1767 conv_q: f("q_conv1d.weight")?,
1768 conv_k: f("k_conv1d.weight")?,
1769 conv_v: f("v_conv1d.weight")?,
1770 f_a: t("f_a_proj.weight")?,
1771 f_b: t("f_b_proj.weight")?,
1772 dt_bias: f("dt_bias")?,
1773 a_log: f("A_log")?,
1774 b_proj: t("b_proj.weight")?,
1775 gate,
1776 o_norm: f("o_norm.weight")?,
1777 o_proj: t("o_proj.weight")?,
1778 gate_lower_bound: arch.kda_gate_lower_bound.map(|v| v as f32),
1779 })))
1780 };
1781
1782 let load_bounded_attn = |prefix: &str, li: usize| -> Result<AttnKind, CmfError> {
1789 let ac = arch.anchor_core.as_ref().ok_or_else(|| {
1790 CmfError::Parse(format!(
1791 "layer {li} is BoundedAttention but the header carries no anchor_core"
1792 ))
1793 })?;
1794 let t = |suffix: &str| load_matrix(model, &format!("{prefix}{suffix}"), force_f32, ov);
1795 let (nkv, hd, nh) = (arch.num_kv_heads, arch.head_dim, arch.num_attention_heads);
1796 for extra in [
1797 "self_attn.q_norm.weight",
1798 "self_attn.k_norm.weight",
1799 "self_attn.g_proj.weight",
1800 "self_attn.q_proj.bias",
1801 "self_attn.k_proj.bias",
1802 "self_attn.v_proj.bias",
1803 ] {
1804 if model.tensor(&format!("{prefix}{extra}")).is_some() {
1805 return Err(CmfError::Parse(format!(
1806 "{prefix}{extra}: the bounded anchor operator carries no qk-norm, \
1807 gate or bias (docs/EMBRYO_BOUNDED_ANCHOR.md §1)"
1808 )));
1809 }
1810 }
1811 let sink = |name: &str| -> Result<Vec<f32>, CmfError> {
1812 let full = format!("{prefix}self_attn.{name}.weight");
1813 let e = model.tensor(&full).ok_or_else(|| {
1814 CmfError::MissingTensor(format!(
1815 "{full} (a bounded anchor needs its trained sinks)"
1816 ))
1817 })?;
1818 if e.shape.len() != 3
1819 || e.shape[0] != nkv
1820 || e.shape[1] != ac.sink
1821 || e.shape[2] != hd
1822 {
1823 return Err(CmfError::Parse(format!(
1824 "{full}: shape {:?} != [{nkv}, {}, {hd}] (num_kv_heads, anchor_core.sink, head_dim)",
1825 e.shape, ac.sink
1826 )));
1827 }
1828 load_f32(model, &full, ov).map_err(err)
1829 };
1830 let wq = t("self_attn.q_proj.weight")?;
1831 let wk = t("self_attn.k_proj.weight")?;
1832 let wv = t("self_attn.v_proj.weight")?;
1833 let wo = t("self_attn.o_proj.weight")?;
1834 let expect = |name: &str, got: (usize, usize), want: (usize, usize)| {
1835 if got != want {
1836 Err(CmfError::Parse(format!(
1837 "{prefix}self_attn.{name}.weight is {}x{}, expected {}x{}",
1838 got.0, got.1, want.0, want.1
1839 )))
1840 } else {
1841 Ok(())
1842 }
1843 };
1844 expect("q_proj", (wq.rows(), wq.cols()), (nh * hd, arch.hidden_size))?;
1845 expect("k_proj", (wk.rows(), wk.cols()), (nkv * hd, arch.hidden_size))?;
1846 expect("v_proj", (wv.rows(), wv.cols()), (nkv * hd, arch.hidden_size))?;
1847 expect("o_proj", (wo.rows(), wo.cols()), (arch.hidden_size, nh * hd))?;
1848 Ok(AttnKind::Bounded(Box::new(crate::bounded::BoundedWeights {
1849 wq,
1850 wk,
1851 wv,
1852 wo,
1853 sink_k: sink("sink_k")?,
1854 sink_v: sink("sink_v")?,
1855 sink: ac.sink,
1856 window: ac.window,
1857 group_windows: ac.group_windows.clone(),
1858 })))
1859 };
1860
1861 fn anyhow_like(ok: bool) -> Result<(), ()> {
1862 if ok { Ok(()) } else { Err(()) }
1863 }
1864 let mut layers = Vec::with_capacity(arch.num_layers);
1865 let is_g3n = arch.g3n.is_some();
1866 let owns_its_layers = is_g3n
1871 || arch.arch_name == "deepseek_v4"
1872 || arch.arch_name == "deepseek_v41"
1873 || arch.dory.is_some()
1874 || arch.qwen4_exp.is_some();
1875 for li in 0..(if owns_its_layers { 0 } else { arch.num_layers }) {
1876 let prefix = format!("model.layers.{li}.");
1877 let attn = match arch.layer_types.get(li) {
1878 Some(LayerType::LinearAttention) => load_linear_attn(&prefix, li)?,
1879 Some(LayerType::Kda) => load_kda(&prefix)?,
1880 Some(LayerType::ShortConv) => load_short_conv(&prefix)?,
1881 Some(LayerType::BoundedAttention) => load_bounded_attn(&prefix, li)?,
1882 _ => load_full_attn(&prefix, Some(li))?,
1883 };
1884 let pre_ffn = format!("{prefix}pre_feedforward_layernorm.weight");
1888 let sandwich = model.tensor(&pre_ffn).is_some();
1889 layers.push(LayerWeights {
1890 input_norm: load_f32(model, &format!("{prefix}input_layernorm.weight"), ov)
1891 .map_err(err)?,
1892 post_norm: if sandwich {
1893 load_f32(model, &pre_ffn, ov).map_err(err)?
1894 } else {
1895 load_f32(
1896 model,
1897 &format!("{prefix}post_attention_layernorm.weight"),
1898 ov,
1899 )
1900 .map_err(err)?
1901 },
1902 attn_out_norm: if sandwich {
1903 Some(
1904 load_f32(
1905 model,
1906 &format!("{prefix}post_attention_layernorm.weight"),
1907 ov,
1908 )
1909 .map_err(err)?,
1910 )
1911 } else {
1912 None
1913 },
1914 ffn_out_norm: if sandwich {
1915 Some(
1916 load_f32(
1917 model,
1918 &format!("{prefix}post_feedforward_layernorm.weight"),
1919 ov,
1920 )
1921 .map_err(err)?,
1922 )
1923 } else {
1924 None
1925 },
1926 layer_scale: model
1928 .tensor(&format!("{prefix}layer_scalar"))
1929 .and_then(|_| {
1930 load_f32(model, &format!("{prefix}layer_scalar"), ov)
1931 .ok()
1932 .and_then(|v| v.first().copied())
1933 }),
1934 ffn: build_layer_ffn(model, &arch, li, false, ov)?,
1936 attn,
1937 });
1938 }
1939
1940 let mtp_present = model
1949 .tensor("model.mtp.layers.0.self_attn.q_proj.weight")
1950 .is_some()
1951 || model.tensor("model.mtp.eh_proj.weight").is_some();
1952 let dsv4_mtp = model.tensor("model.mtp.0.main_proj.weight").is_some();
1957 if arch.mtp.is_some() && !mtp_present && !dsv4_mtp {
1958 tracing::info!(
1959 "header declares an MTP head but the file carries none — \
1960 loading without it"
1961 );
1962 }
1963 let mtp = if let Some(cfg) = arch
1966 .mtp
1967 .as_ref()
1968 .filter(|_| mtp_present && arch.arch_name != "mimo_v2")
1969 {
1970 if cfg.num_layers != 1 {
1971 return Err(CmfError::Parse(format!(
1972 "MTP with {} blocks not supported yet (only 1)",
1973 cfg.num_layers
1974 )));
1975 }
1976 let p = "model.mtp.";
1977 let attn = load_full_attn("model.mtp.layers.0.", None)?;
1978 let ffn = build_ffn_at(model, &arch, &format!("{p}layers.0."), false, ov)?;
1983 validate_generic_mtp_ffn_width(cfg, &ffn)?;
1984 Some(MtpModule {
1985 enorm: load_f32(model, &format!("{p}enorm.weight"), ov).map_err(err)?,
1986 hnorm: load_f32(model, &format!("{p}hnorm.weight"), ov).map_err(err)?,
1987 eh_proj: load_matrix(model, &format!("{p}eh_proj.weight"), false, ov)?,
1988 layer: LayerWeights {
1989 attn_out_norm: None,
1990 ffn_out_norm: None,
1991 layer_scale: None,
1992 input_norm: load_f32(model, &format!("{p}layers.0.input_layernorm.weight"), ov)
1993 .map_err(err)?,
1994 post_norm: load_f32(
1995 model,
1996 &format!("{p}layers.0.post_attention_layernorm.weight"),
1997 ov,
1998 )
1999 .map_err(err)?,
2000 ffn,
2004 attn,
2005 },
2006 final_norm: load_f32(model, &format!("{p}norm.weight"), ov).map_err(err)?,
2007 kv: LayerKvCache::new(arch.num_kv_heads, arch.head_dim),
2008 })
2009 } else {
2010 None
2011 };
2012
2013 tracing::info!(
2014 "Pipeline loaded: {} | {}L ({} linear) | {:.2}B params | storage: {} | MTP: {}",
2015 arch.arch_name,
2016 arch.num_layers,
2017 arch.layer_types
2018 .iter()
2019 .filter(|t| matches!(t, LayerType::LinearAttention))
2020 .count(),
2021 model.total_param_count() as f64 / 1e9,
2022 if force_f32 {
2023 "f32 (masked)"
2024 } else {
2025 "quantized mmap"
2026 },
2027 if mtp.is_some() { "yes" } else { "no" }
2028 );
2029
2030 let cap = std::env::var("CMF_MAX_SEQ")
2041 .ok()
2042 .and_then(|v| v.parse::<usize>().ok())
2043 .unwrap_or(if model.header.provenance.as_ref().is_some_and(|p| p.get("o1_turbo").is_some()) {
2045 usize::MAX
2046 } else {
2047 32_768
2048 });
2049 let max_seq_len = arch.max_position_embeddings.min(cap);
2050
2051 let sensenova = crate::sensenova::settings(model).map_err(CmfError::Parse)?;
2055 let layer_map = sensenova
2056 .map(|(span, _)| crate::sensenova::execution_plan(arch.num_layers, span));
2057 let total_layers = layer_map
2058 .as_ref()
2059 .map_or(arch.num_layers * arch.num_loops, Vec::len);
2060
2061 let mut pipeline = Pipeline::new(
2062 tokenizer,
2063 PipelineWeights {
2064 embed_tokens,
2065 layers,
2066 lm_head,
2067 final_norm,
2068 },
2069 arch.hidden_size,
2070 arch.intermediate_size,
2071 arch.num_attention_heads,
2072 arch.num_kv_heads,
2073 arch.head_dim,
2074 total_layers,
2075 arch.num_layers, arch.loop_final_norm,
2077 arch.vocab_size,
2078 arch.rms_norm_eps,
2079 arch.rope_theta as f32,
2080 arch.norm_style,
2081 max_seq_len,
2082 sampler_config,
2083 );
2084 if arch.arch_name == crate::uocr::ARCH {
2086 pipeline.dash = Some(Box::new(crate::uocr::Dash::from_model(model).map_err(CmfError::Parse)?));
2087 }
2088 if let Some((_, mhc)) = sensenova {
2089 pipeline.layer_map = layer_map;
2090 if let Some(ms) = mhc {
2091 pipeline.mhc = Some(Box::new(
2092 crate::sensenova::Mhc::from_model(model, ms, arch.num_layers, arch.hidden_size)
2093 .map_err(CmfError::Parse)?,
2094 ));
2095 }
2096 pipeline.mrope_sections = model
2097 .header
2098 .provenance
2099 .as_ref()
2100 .and_then(|p| p.pointer("/sensenova_loop/config/text_config/rope_parameters/mrope_section"))
2101 .and_then(|v| v.as_array())
2102 .filter(|a| a.len() == 3)
2103 .map(|a| {
2104 let g = |i: usize| a[i].as_u64().unwrap_or(0) as usize;
2105 [g(0), g(1), g(2)]
2106 });
2107 }
2108 let rotary = ((arch.head_dim as f32 * arch.partial_rotary_factor) as usize).max(2);
2109 pipeline.set_rotary(rotary, arch.rope_theta as f32);
2110 pipeline.attention_heads_per_layer = arch.attention_heads_per_layer.clone();
2111 if let Some(yarn) = &arch.yarn {
2112 pipeline.inv_freq = std::sync::Arc::new(crate::attention::yarn_inv_freq(
2113 rotary,
2114 arch.rope_theta as f32,
2115 yarn.factor,
2116 yarn.original_max_position_embeddings,
2117 yarn.beta_fast,
2118 yarn.beta_slow,
2119 ));
2120 pipeline.rope_scale = yarn.attention_factor;
2121 }
2122 pipeline.embed_multiplier = arch.embed_multiplier;
2126 pipeline.logit_multiplier = arch.logit_multiplier;
2127 if let Some(qpas) = arch.query_pre_attn_scalar {
2128 pipeline.attn_scale = 1.0 / (qpas as f32).sqrt();
2129 }
2130 if let (Some(w), Some(p)) = (arch.sliding_window, arch.sliding_window_pattern) {
2131 pipeline.swa = Some((w, p));
2132 if let Some(base) = arch.rope_local_base_freq {
2133 pipeline.inv_freq_local = Some(std::sync::Arc::new(
2134 crate::attention::rope_inv_freq(rotary, base as f32),
2135 ));
2136 }
2137 }
2138 let explicit_sliding: Vec<bool> = arch
2139 .layer_types
2140 .iter()
2141 .map(|t| matches!(t, cortiq_core::LayerType::SlidingAttention))
2142 .collect();
2143 if explicit_sliding.iter().any(|&v| v) {
2144 pipeline.sliding_layers = Some(explicit_sliding);
2145 if let Some(w) = arch.sliding_window {
2146 pipeline.swa = Some((w, usize::MAX));
2147 }
2148 let local_rotary = ((arch.head_dim as f32
2149 * arch
2150 .local_partial_rotary_factor
2151 .unwrap_or(arch.partial_rotary_factor))
2152 as usize)
2153 .max(2);
2154 pipeline.rotary_dim_local = Some(local_rotary);
2155 if let Some(base) = arch.rope_local_base_freq {
2156 pipeline.inv_freq_local = Some(std::sync::Arc::new(
2157 crate::attention::rope_inv_freq(local_rotary, base as f32),
2158 ));
2159 }
2160 }
2161 if let (Some(ghd), Some(gkv)) = (arch.global_head_dim, arch.num_global_kv_heads) {
2165 pipeline.global_attn = Some((ghd, gkv));
2166 let prf = arch.global_partial_rotary_factor.unwrap_or(1.0);
2167 let half = ghd / 2;
2168 let ra = (((prf * ghd as f32) as usize) / 2).min(half);
2169 let mut f = vec![0.0f32; half];
2170 for (i, slot) in f.iter_mut().enumerate().take(ra) {
2171 *slot = 1.0 / (arch.rope_theta as f32).powf(2.0 * i as f32 / ghd as f32);
2172 }
2173 pipeline.inv_freq_global = Some(std::sync::Arc::new(f));
2174 let global_at = |li: usize| -> bool {
2179 match &pipeline.sliding_layers {
2180 Some(map) => !map.get(li).copied().unwrap_or(false),
2181 None => pipeline
2182 .swa
2183 .map(|(_, p)| p > 0 && p != usize::MAX && (li + 1) % p == 0)
2184 .unwrap_or(false),
2185 }
2186 };
2187 for li in 0..arch.num_layers {
2188 if global_at(li) {
2189 pipeline.kv_cache.layers[li] = crate::kv_cache::LayerKvCache::new(gkv, ghd);
2190 }
2191 }
2192 }
2193 if let (Some(prf), None) = (arch.global_partial_rotary_factor, arch.global_head_dim) {
2198 if arch.yarn.is_some() || pipeline.rotary_dim != arch.head_dim {
2199 return Err(CmfError::Parse(format!(
2200 "global_partial_rotary_factor {prf} needs partial_rotary_factor 1.0 and no \
2201 YaRN profile (rotary width {} of head {})",
2202 pipeline.rotary_dim, arch.head_dim
2203 )));
2204 }
2205 let mut f = crate::attention::rope_inv_freq(arch.head_dim, arch.rope_theta as f32);
2206 let ra = (((prf * arch.head_dim as f32) as usize) / 2).min(f.len());
2207 for slot in f.iter_mut().skip(ra) {
2208 *slot = 0.0;
2209 }
2210 pipeline.inv_freq = std::sync::Arc::new(f);
2211 }
2212 if let Some(mla) = arch.mla.as_ref() {
2215 let hd = mla.qk_rope_head_dim + mla.qk_nope_head_dim;
2216 pipeline.head_dim = hd;
2217 pipeline.num_kv_heads = arch.num_attention_heads;
2218 pipeline.rotary_dim = mla.qk_rope_head_dim;
2219 let half = mla.qk_rope_head_dim / 2;
2220 let mut f = vec![0.0f32; half];
2221 for (i, slot) in f.iter_mut().enumerate() {
2222 *slot = 1.0
2223 / (arch.rope_theta as f32).powf(2.0 * i as f32 / mla.qk_rope_head_dim as f32);
2224 }
2225 pipeline.inv_freq = std::sync::Arc::new(f);
2226 for li in 0..arch.num_layers {
2227 pipeline.kv_cache.layers[li] =
2228 crate::kv_cache::LayerKvCache::new(arch.num_attention_heads, hd);
2229 }
2230 }
2231 if !owns_its_layers {
2236 pipeline
2237 .set_attn_geometry(arch.kv_heads_per_layer.clone(), arch.v_head_dim)
2238 .map_err(|e| CmfError::Parse(format!("attention geometry: {e}")))?;
2239 } else if arch.kv_heads_per_layer.is_some() || arch.v_head_dim.is_some() {
2240 return Err(CmfError::Parse(format!(
2241 "{}: kv_heads_per_layer / v_head_dim are not supported by its own layer stack",
2242 arch.arch_name
2243 )));
2244 }
2245 if !owns_its_layers {
2252 for li in 0..arch.num_layers {
2253 let name = [
2254 format!("model.layers.{li}.self_attn.sinks"),
2255 format!("model.layers.{li}.self_attn.attention_sink_bias"),
2256 format!("model.layers.{li}.self_attn.softmax_offset"),
2260 ]
2261 .into_iter()
2262 .find(|n| model.tensor(n).is_some());
2263 if let Some(name) = name {
2264 let sinks = load_f32(model, &name, ov).map_err(err)?;
2265 pipeline
2266 .set_layer_sinks(li, sinks)
2267 .map_err(|e| CmfError::Parse(format!("{name}: {e}")))?;
2268 }
2269 }
2270 }
2271 if arch.arch_name == "flint" || arch.arch_name == "ricursive" {
2275 pipeline.no_rope_layers = arch.no_rope_layers.clone();
2276 }
2277 if !owns_its_layers {
2282 let mut blend = vec![1.0f32; arch.num_layers];
2283 let mut any = false;
2284 for (li, b) in blend.iter_mut().enumerate() {
2285 let name = format!("model.layers.{li}.self_attn.value_res_lambda");
2286 if model.tensor(&name).is_some() {
2287 let lam = load_f32(model, &name, ov).map_err(err)?;
2288 if lam.len() != 1 {
2289 return Err(CmfError::Parse(format!("{name}: one scalar expected, got {}", lam.len())));
2290 }
2291 *b = 1.0 / (1.0 + (-lam[0]).exp());
2292 any = true;
2293 }
2294 }
2295 if any {
2296 pipeline.value_blend = Some(blend);
2297 }
2298 }
2299 if pipeline.kv_heads_per_layer.is_some()
2300 || pipeline.v_head_dim.is_some()
2301 || pipeline.kv_cache.layers.iter().any(|l| l.sinks.is_some())
2302 {
2303 let per_token: usize = pipeline
2304 .kv_cache
2305 .layers
2306 .iter()
2307 .map(|l| 2 * l.num_kv_heads * l.head_dim * std::mem::size_of::<f32>())
2308 .sum();
2309 tracing::info!(
2310 "attention geometry: kv heads per layer {:?}, v_head_dim {:?}, {} sink layer(s); \
2311 f32 KV {} B/token (V padded to head_dim), cap {} tokens",
2312 pipeline.kv_heads_per_layer,
2313 pipeline.v_head_dim,
2314 pipeline
2315 .kv_cache
2316 .layers
2317 .iter()
2318 .filter(|l| l.sinks.is_some())
2319 .count(),
2320 per_token,
2321 pipeline.kv_cache.max_seq_len
2322 );
2323 }
2324 if let Some(fac) = &arch.rope_freq_factors {
2328 let mut f = pipeline.inv_freq.as_ref().clone();
2329 for (i, v) in f.iter_mut().enumerate() {
2330 if let Some(&d) = fac.get(i) {
2331 *v /= d as f32;
2332 }
2333 }
2334 pipeline.inv_freq = std::sync::Arc::new(f);
2335 }
2336 if let Some(selected) = &phase_delta_layers {
2337 for &li in selected {
2338 pipeline.kv_cache.layers[li].set_linear_wire_allowed(false);
2339 }
2340 }
2341 pipeline.attn_v_norm = arch.attn_v_norm;
2342 pipeline.qk_norm_after_rope = arch.qk_norm_after_rope;
2343 pipeline.proj_gate_sigmoid = arch.arch_name == "spark2_5" || arch.arch_name == "lfm3";
2346 pipeline.final_softcap = arch.final_logit_softcapping.map(|c| c as f32);
2347 if let Some(ncl) = arch.head_clusters {
2349 let cm = load_f32(model, "lm_head.clusters.weight", ov).map_err(err)?;
2350 if cm.len() != ncl * arch.hidden_size {
2351 return Err(CmfError::Parse(format!(
2352 "lm_head.clusters.weight: {} != {ncl}×{}",
2353 cm.len(),
2354 arch.hidden_size
2355 )));
2356 }
2357 pipeline.head_clusters = Some(std::sync::Arc::new(cm));
2358 }
2359 pipeline.attn_softcap = arch.attn_logit_softcapping.unwrap_or(0.0) as f32;
2360 pipeline.vmf_cfg = vmf_cfg;
2361 pipeline.gdn_cfg = gdn_cfg;
2362 pipeline.kda_cfg = kda_cfg;
2363 if arch.block_diffusion.is_some() {
2364 let c = crate::pipeline::clover::load(model, &arch, gdn_cfg.as_ref(), ov)?;
2365 pipeline.clover = Some(std::sync::Arc::new(c));
2366 }
2367 if arch.qwen4_exp.is_some() {
2368 let (globals, layers, cfg, state) = crate::qwen4_exp::load(model, &arch)?;
2369 pipeline.qwen4_exp = Some(Box::new((globals, layers, cfg, state)));
2370 }
2371 if arch.g3n.is_some() && arch.arch_name.starts_with("gemma4") {
2376 use crate::g4e::{G4eGlobals, G4eLayer};
2377 let gc = arch.g3n.as_ref().expect("checked above");
2378 let t = |name: &str| load_matrix(model, name, force_f32, ov);
2379 let f = |name: &str| load_f32(model, name, ov).map_err(err);
2380 let first_shared = arch.num_layers.saturating_sub(gc.num_kv_shared_layers);
2381 let sliding_of = |li: usize| {
2382 matches!(
2383 arch.layer_types.get(li),
2384 Some(cortiq_core::LayerType::SlidingAttention)
2385 )
2386 };
2387 let hd_g = arch.global_head_dim.unwrap_or(arch.head_dim);
2388 let mut g4e_layers = Vec::with_capacity(arch.num_layers);
2389 for li in 0..arch.num_layers {
2390 let pfx = format!("model.layers.{li}.");
2391 let shared = li >= first_shared && first_shared > 0;
2392 let share_src = if shared {
2396 let want = sliding_of(li);
2397 (0..first_shared).rev().find(|&j| sliding_of(j) == want)
2398 } else {
2399 None
2400 };
2401 let sliding = sliding_of(li);
2402 let scalar_name = format!("{pfx}layer_scalar");
2403 g4e_layers.push(G4eLayer {
2404 input_norm: f(&format!("{pfx}input_layernorm.weight"))?,
2405 attn_out_norm: f(&format!("{pfx}post_attention_layernorm.weight"))?,
2406 pre_ffn_norm: f(&format!("{pfx}pre_feedforward_layernorm.weight"))?,
2407 post_ffw_norm: f(&format!("{pfx}post_feedforward_layernorm.weight"))?,
2408 wq: t(&format!("{pfx}self_attn.q_proj.weight"))?,
2409 wk: if shared {
2410 None
2411 } else {
2412 Some(t(&format!("{pfx}self_attn.k_proj.weight"))?)
2413 },
2414 wv: if shared {
2415 None
2416 } else {
2417 Some(t(&format!("{pfx}self_attn.v_proj.weight"))?)
2418 },
2419 wo: t(&format!("{pfx}self_attn.o_proj.weight"))?,
2420 q_norm: f(&format!("{pfx}self_attn.q_norm.weight"))?,
2421 k_norm: if shared {
2422 None
2423 } else {
2424 Some(f(&format!("{pfx}self_attn.k_norm.weight"))?)
2425 },
2426 kv_share_src: share_src,
2427 sliding,
2428 hd: if sliding { arch.head_dim } else { hd_g },
2429 gate: t(&format!("{pfx}mlp.gate_proj.weight"))?,
2430 up: t(&format!("{pfx}mlp.up_proj.weight"))?,
2431 down: t(&format!("{pfx}mlp.down_proj.weight"))?,
2432 ple_gate: t(&format!("{pfx}per_layer_input_gate.weight"))?,
2433 ple_proj: t(&format!("{pfx}per_layer_projection.weight"))?,
2434 post_ple_norm: f(&format!("{pfx}post_per_layer_input_norm.weight"))?,
2435 layer_scalar: if model.tensor(&scalar_name).is_some() {
2436 f(&scalar_name)?.first().copied()
2437 } else {
2438 None
2439 },
2440 });
2441 }
2442 let ghd = hd_g;
2447 let prf = arch.global_partial_rotary_factor.unwrap_or(1.0);
2448 let half = ghd / 2;
2449 let ra = (((prf * ghd as f32) as usize) / 2).min(half);
2450 let mut inv_freq_global = vec![0.0f32; half];
2451 for (i, slot) in inv_freq_global.iter_mut().enumerate().take(ra) {
2452 *slot = 1.0 / (arch.rope_theta as f32).powf(2.0 * i as f32 / ghd as f32);
2453 }
2454 let globals = G4eGlobals {
2455 ple_embed: t("model.embed_tokens_per_layer.weight")?,
2456 ple_model_proj: t("model.per_layer_model_projection.weight")?,
2457 ple_norm: f("model.per_layer_projection_norm.weight")?,
2458 ple_vocab: gc.ple_vocab,
2459 ple_dim: gc.ple_dim,
2460 num_layers: arch.num_layers,
2461 hidden: arch.hidden_size,
2462 rms_eps: arch.rms_norm_eps,
2463 inv_freq_local: crate::attention::rope_inv_freq(
2464 arch.head_dim,
2465 arch.rope_local_base_freq.unwrap_or(10_000.0) as f32,
2466 ),
2467 inv_freq_global,
2468 window: arch.sliding_window.unwrap_or(512),
2469 nh: arch.num_attention_heads,
2470 nkv: arch.num_kv_heads,
2471 };
2472 pipeline.g4e = Some(Box::new((globals, g4e_layers)));
2473 }
2474 if let Some(gc) = arch
2475 .g3n
2476 .as_ref()
2477 .filter(|_| !arch.arch_name.starts_with("gemma4"))
2478 {
2479 use crate::g3n::{G3nAltUp, G3nGlobals, G3nLaurel, G3nLayer};
2480 anyhow_like(gc.altup_num_inputs == crate::g3n::ALTUP_N).map_err(|_| {
2481 CmfError::Parse(format!(
2482 "g3n: altup_num_inputs {} != supported {}",
2483 gc.altup_num_inputs,
2484 crate::g3n::ALTUP_N
2485 ))
2486 })?;
2487 let t = |name: &str| load_matrix(model, name, force_f32, ov);
2488 let f = |name: &str| load_f32(model, name, ov).map_err(err);
2489 let mut altup_proj = Vec::new();
2490 let mut altup_unembed = Vec::new();
2491 for i in 0..crate::g3n::ALTUP_N - 1 {
2492 altup_proj.push(t(&format!("model.altup_projections.{i}.weight"))?);
2493 altup_unembed.push(t(&format!("model.altup_unembed_projections.{i}.weight"))?);
2494 }
2495 let first_shared = arch.num_layers.saturating_sub(gc.num_kv_shared_layers);
2496 let sliding_of = |li: usize| {
2497 matches!(
2498 arch.layer_types.get(li),
2499 Some(cortiq_core::LayerType::SlidingAttention)
2500 )
2501 };
2502 let mut g3n_layers = Vec::with_capacity(arch.num_layers);
2503 for li in 0..arch.num_layers {
2504 let pfx = format!("model.layers.{li}.");
2505 let shared = li >= first_shared && first_shared > 0;
2506 let share_src = if shared {
2507 let want = sliding_of(li);
2508 (0..first_shared).rev().find(|&j| sliding_of(j) == want)
2509 } else {
2510 None
2511 };
2512 g3n_layers.push(G3nLayer {
2513 altup: G3nAltUp {
2514 router_norm: f(&format!("{pfx}altup.router_norm.weight"))?,
2515 modality_router: t(&format!("{pfx}altup.modality_router.weight"))?,
2516 prediction_coefs: t(&format!("{pfx}altup.prediction_coefs.weight"))?,
2517 correction_coefs: t(&format!("{pfx}altup.correction_coefs.weight"))?,
2518 correct_output_scale: f(&format!("{pfx}altup.correct_output_scale"))?,
2519 },
2520 laurel: G3nLaurel {
2521 left: t(&format!("{pfx}laurel.linear_left.weight"))?,
2522 right: t(&format!("{pfx}laurel.linear_right.weight"))?,
2523 post_norm: f(&format!("{pfx}laurel.post_laurel_norm.weight"))?,
2524 },
2525 input_norm: f(&format!("{pfx}input_layernorm.weight"))?,
2526 post_attn_norm: f(&format!("{pfx}post_attention_layernorm.weight"))?,
2527 pre_ffw_norm: f(&format!("{pfx}pre_feedforward_layernorm.weight"))?,
2528 post_ffw_norm: f(&format!("{pfx}post_feedforward_layernorm.weight"))?,
2529 wq: t(&format!("{pfx}self_attn.q_proj.weight"))?,
2530 wk: if shared {
2531 None
2532 } else {
2533 Some(t(&format!("{pfx}self_attn.k_proj.weight"))?)
2534 },
2535 wv: if shared {
2536 None
2537 } else {
2538 Some(t(&format!("{pfx}self_attn.v_proj.weight"))?)
2539 },
2540 wo: t(&format!("{pfx}self_attn.o_proj.weight"))?,
2541 q_norm: f(&format!("{pfx}self_attn.q_norm.weight"))?,
2542 k_norm: if shared {
2543 None
2544 } else {
2545 Some(f(&format!("{pfx}self_attn.k_norm.weight"))?)
2546 },
2547 kv_share_src: share_src,
2548 sliding: sliding_of(li),
2549 gate: t(&format!("{pfx}mlp.gate_proj.weight"))?,
2550 up: t(&format!("{pfx}mlp.up_proj.weight"))?,
2551 down: t(&format!("{pfx}mlp.down_proj.weight"))?,
2552 sparsity: gc.activation_sparsity.get(li).copied().unwrap_or(0.0),
2553 ple_gate: t(&format!("{pfx}per_layer_input_gate.weight"))?,
2554 ple_proj: t(&format!("{pfx}per_layer_projection.weight"))?,
2555 post_ple_norm: f(&format!("{pfx}post_per_layer_input_norm.weight"))?,
2556 });
2557 }
2558 let hd = arch.head_dim;
2559 let globals = G3nGlobals {
2560 altup_proj,
2561 altup_unembed,
2562 ple_embed: t("model.embed_tokens_per_layer.weight")?,
2563 ple_model_proj: t("model.per_layer_model_projection.weight")?,
2564 ple_norm: f("model.per_layer_projection_norm.weight")?,
2565 ple_vocab: gc.ple_vocab,
2566 ple_dim: gc.ple_dim,
2567 num_layers: arch.num_layers,
2568 hidden: arch.hidden_size,
2569 rms_eps: arch.rms_norm_eps,
2570 inv_freq_local: crate::attention::rope_inv_freq(
2571 hd,
2572 arch.rope_local_base_freq.unwrap_or(10_000.0) as f32,
2573 ),
2574 inv_freq_global: crate::attention::rope_inv_freq(hd, arch.rope_theta as f32),
2575 window: arch.sliding_window.unwrap_or(512),
2576 };
2577 pipeline.g3n = Some(Box::new((globals, g3n_layers)));
2578 }
2579 if arch.dory.is_some() {
2581 let m = crate::dory::Dory::from_model(model).map_err(CmfError::Parse)?;
2582 pipeline.dory = Some(Box::new((m, crate::dory::DoryState::default())));
2583 }
2584 if arch.arch_name == "deepseek_v41" {
2589 let source = arch.deepseek_v41.as_ref().ok_or_else(|| {
2590 CmfError::Parse("deepseek_v41: missing preserved source config".into())
2591 })?;
2592 let (cfg, n_layers) = crate::dsv41::Dsv41Cfg::from_source(source, &arch)
2593 .map_err(|e| CmfError::Parse(format!("deepseek_v41: {e}")))?;
2594 let token_map = crate::dsv41::token_map_from_model(model, cfg.vocab);
2595 let (g, dl, hash) = crate::dsv41::load(model, &cfg, n_layers, token_map)
2596 .map_err(|e| CmfError::Parse(format!("deepseek_v41: {e}")))?;
2597 let st = crate::dsv41::Dsv41State::new(&cfg, hash);
2598 if let Ok(vision_cfg) = crate::dsv41_vision::VisionConfig::from_source(source) {
2603 if vision_cfg.vision_enabled()
2604 && model.tensor("vision.patch_embed.proj.weight").is_some()
2605 {
2606 pipeline.dsv41_vision = Some(
2607 crate::dsv41_vision::VisionModel::from_model(model, vision_cfg)
2608 .map_err(|e| CmfError::Parse(format!("deepseek_v41 vision: {e}")))?,
2609 );
2610 }
2611 }
2612 tracing::info!(
2613 "deepseek_v41: loaded {} layers, {} KV sources, {} index sources, {} Engram layers; experts remain mmap-backed",
2614 dl.len(),
2615 cfg.kv_sources.len(),
2616 cfg.index_sources.len(),
2617 cfg.engram_layers.len()
2618 );
2619 pipeline.dsv41 = Some(Box::new((g, dl, cfg, st)));
2620 }
2621 if arch.arch_name == "deepseek_v4" {
2626 let moe = arch
2627 .moe
2628 .as_ref()
2629 .ok_or_else(|| CmfError::Parse("deepseek_v4: no moe config".into()))?;
2630 let cfg = crate::dsv4::Dsv4Cfg {
2631 dim: arch.hidden_size,
2632 n_heads: arch.num_attention_heads,
2633 head_dim: arch.head_dim,
2634 rope_head_dim: if arch.partial_rotary_factor < 1.0 {
2641 (((arch.head_dim as f32 * arch.partial_rotary_factor) as usize) & !1)
2642 .clamp(2, arch.head_dim)
2643 } else {
2644 64.min(arch.head_dim)
2645 },
2646 q_lora_rank: 0,
2650 o_lora_rank: 0,
2651 o_groups: 8,
2658 hc_mult: 4,
2659 hc_sinkhorn_iters: 20,
2660 hc_eps: 1e-6,
2661 norm_eps: arch.rms_norm_eps as f32,
2662 n_routed_experts: moe.num_experts,
2663 top_k: moe.top_k,
2664 moe_inter: moe.moe_intermediate_size,
2665 route_scale: moe.routed_scaling_factor.unwrap_or(1.0),
2666 swiglu_limit: 10.0,
2672 window: arch.sliding_window.unwrap_or(128),
2673 index_topk: 512,
2674 vocab: arch.vocab_size,
2675 };
2676 let (g, dl) = crate::dsv4::load(model, &cfg, arch.num_layers)
2677 .map_err(|e| CmfError::Parse(format!("deepseek_v4: {e}")))?;
2678 let mut cfg = cfg;
2683 if let Some(l0) = dl.first() {
2684 cfg.q_lora_rank = l0.wq_a.rows();
2685 let attn_width = arch.num_attention_heads * arch.head_dim;
2686 if l0.wo_a.cols() > 0 && attn_width % l0.wo_a.cols() == 0 {
2687 cfg.o_groups = (attn_width / l0.wo_a.cols()).max(1);
2688 }
2689 cfg.o_lora_rank = l0.wo_b.cols() / cfg.o_groups.max(1);
2690 cfg.hc_mult = (l0.hc_attn_fn.len() / l0.hc_attn_base.len().max(1)) / cfg.dim.max(1);
2691 if cfg.hc_mult == 0 {
2692 cfg.hc_mult = 4;
2693 }
2694 }
2695 let (yf, yo, ybf, ybs) = match &arch.yarn {
2708 Some(y) => (
2709 y.factor,
2710 y.original_max_position_embeddings,
2711 y.beta_fast,
2712 y.beta_slow,
2713 ),
2714 None => {
2715 tracing::warn!(
2716 "deepseek_v4: the header carries no YaRN profile — \
2717 falling back to the release's (factor 16, original \
2718 65536, beta 32/1). Re-converting with a build that \
2719 reads rope_scaling.type would make this exact."
2720 );
2721 (16.0, 65536, 32.0, 1.0)
2722 }
2723 };
2724 pipeline.inv_freq = std::sync::Arc::new(crate::attention::yarn_inv_freq(
2725 cfg.rope_head_dim,
2726 arch.rope_theta as f32,
2727 yf,
2728 yo,
2729 ybf,
2730 ybs,
2731 ));
2732 if let Ok(stats) = std::env::var("CMF_MOE_PIN") {
2737 let cover = std::env::var("CMF_MOE_PIN_COVER")
2738 .ok()
2739 .and_then(|v| v.parse::<f64>().ok())
2740 .filter(|&c| c > 0.0 && c <= 1.0)
2741 .unwrap_or(0.95);
2742 let hot = crate::pin::hot_experts(&stats, cover);
2743 let mut names: Vec<String> = Vec::new();
2744 for e in &model.tensors {
2745 let is_expert = e.name.contains(".mlp.experts.");
2746 if !is_expert {
2747 names.push(e.name.clone()); }
2749 }
2750 let mut kept_experts = 0usize;
2751 if let Some(hot) = &hot {
2752 for (li, experts) in hot {
2753 for e in experts {
2754 for w in ["gate_proj", "up_proj", "down_proj"] {
2755 names.push(format!("model.layers.{li}.mlp.experts.{e}.{w}.weight"));
2756 }
2757 kept_experts += 1;
2758 }
2759 }
2760 }
2761 let r = crate::pin::pin_tensors(model, &names);
2762 tracing::info!(
2763 "pinned {:.1} GB ({} tensors, hot experts {kept_experts}, coverage {cover}); limit {}",
2764 r.bytes as f64 / 1e9,
2765 r.tensors,
2766 r.limit
2767 .map(|l| format!("{:.1} GB", l as f64 / 1e9))
2768 .unwrap_or_else(|| "unknown".into())
2769 );
2770 if r.skipped > 0 {
2771 tracing::warn!("tensors left unpinned: {}", r.skipped);
2772 }
2773 }
2774 let st = crate::dsv4::Dsv4State::new(arch.num_layers);
2775 let depth = std::env::var("CMF_DSV4_MTP_DEPTH")
2779 .ok()
2780 .and_then(|v| v.parse::<usize>().ok())
2781 .unwrap_or(3);
2782 pipeline.dsv4_mtp = crate::dsv4::load_mtp(model, &cfg, depth);
2783 crate::dsv4::dspark_reserve_note(&pipeline.dsv4_mtp, &cfg, &dl);
2785 pipeline.dsv4 = Some(Box::new((g, dl, cfg, st)));
2786 }
2787 pipeline.short_conv_cfg = short_conv_cfg;
2788 pipeline.mtp = mtp;
2789 if std::env::var("CMF_DFLASH").as_deref() == Ok("1") {
2791 match crate::dflash::Drafter::for_target(model) {
2792 Some(Ok(d)) => {
2793 tracing::info!("DFlash drafter: {} layers on the trunk's layers {:?}, blocks of {}", d.g.layers, d.g.taps, d.g.block);
2794 #[cfg(feature = "gpu")]
2795 let _ = d.dev_ready();
2796 pipeline.dflash = Some(Box::new(d));
2797 pipeline.dflash_solo = pipeline.mtp.is_none();
2798 }
2799 Some(Err(e)) => tracing::warn!("DFlash sidecar ignored: {e}"),
2800 None => tracing::warn!("CMF_DFLASH=1, and {} is not there", crate::dflash::sidecar_path(&model.path).display()),
2801 }
2802 }
2803 if arch.arch_name == "mimo_v2" {
2804 pipeline.mimo_mtp = crate::pipeline::mimo_mtp::load_for(model, &arch)?;
2805 }
2806 pipeline.swa_trim = (matches!(arch.arch_name.as_str(), "spark2_5" | "lfm3")
2816 && pipeline.swa.is_some()
2817 && pipeline.mtp.is_none()
2818 && pipeline.mimo_mtp.is_none()
2819 && pipeline.dsv4_mtp.is_empty()
2820 && std::env::var_os("CMF_ATTN_DUMP").is_none()
2821 && std::env::var("CMF_SWA_TRIM").as_deref() != Ok("0"))
2822 .then_some((64, 64));
2823 #[cfg(target_os = "macos")]
2826 if arch.arch_name == "spark2_5" {
2827 crate::gpu_metal::set_kv_f16_default(true);
2828 }
2829 pipeline.chunk_min_rows = std::env::var("CMF_CHUNK_MIN_B")
2834 .ok()
2835 .and_then(|v| v.parse().ok())
2836 .unwrap_or(if arch.arch_name == "spark2_5" { 8 } else { 32 });
2837 pipeline.install_dynamic_routing(model, false);
2838 match ov {
2842 Overlay::One(sid) => {
2843 pipeline.dyn_active = model.header.skills.iter().position(|s| &s.id == sid);
2844 }
2845 Overlay::Blend(_) => pipeline.dyn_blend_loaded = true,
2846 Overlay::None => {}
2847 }
2848 if let Some(c) = &model.header.calibration {
2851 pipeline.set_calib_temp(c.temperature);
2852 }
2853 let identity = arch
2856 .linear_core_identity()
2857 .and_then(|v| serde_json::to_vec(&v).ok())
2858 .map(|b| cortiq_core::hash64(&b))
2859 .unwrap_or(0);
2860 pipeline.install_wire_identity(identity);
2861 if let Some(ac) = &arch.anchor_core {
2865 pipeline.install_bounded(ac).map_err(CmfError::Parse)?;
2866 let why = pipeline.o1_refusal().unwrap_or_default();
2867 let mut knobs: Vec<String> = std::env::vars()
2868 .filter(|(k, v)| {
2869 (k == "CMF_O1" && !(v == "off" || v == "0")) || k.starts_with("CMF_O1_")
2870 })
2871 .map(|(k, _)| k)
2872 .collect();
2873 knobs.sort();
2874 if !knobs.is_empty() {
2875 return Err(CmfError::Parse(format!("{why} (set: {})", knobs.join(", "))));
2876 }
2877 if model
2878 .header
2879 .provenance
2880 .as_ref()
2881 .and_then(|p| p.get("o1_attn"))
2882 .is_some()
2883 {
2884 return Err(CmfError::Parse(format!(
2885 "{why} (the header carries a provenance.o1_attn hint)"
2886 )));
2887 }
2888 return Ok(pipeline);
2889 }
2890 let genome_hint = model.header.genome.is_some()
2900 && model
2901 .header
2902 .provenance
2903 .as_ref()
2904 .and_then(|p| p.get("o1_attn"))
2905 .is_some();
2906 if genome_hint {
2907 return Err(CmfError::Parse(
2908 "genome file with a provenance.o1_attn hint: the trunk's attention operator is \
2909 set by the genome's arch only — refusing"
2910 .into(),
2911 ));
2912 }
2913 let o1_env = crate::nystrom::o1_from_env();
2914 if matches!(o1_env, crate::nystrom::O1Env::Unset) && pipeline.qwen4_exp.is_none() {
2918 let hint = model.header.provenance.as_ref().and_then(|p| p.get("o1_turbo"));
2919 if let Some(h) = hint {
2920 let field = |k: &str| h.get(k).and_then(|v| v.as_u64()).map(|v| v as usize);
2921 let weights = load_f32(model, "o1.exact.weights", &Overlay::None).unwrap_or_default();
2922 if let (Some(cfg), Some(seal), Some(rows)) = (crate::nystrom::O1Cfg::from_json(h), field("seal"), field("exact_rows")) {
2923 pipeline.o1_turbo = Some(crate::pipeline::O1Turbo {
2924 cfg,
2925 seal: crate::pipeline::o1_seal_for_card(seal, model.tensors.iter().map(|e| e.nbytes).sum()),
2926 exact: crate::dory::ExactRows { weights: std::sync::Arc::new(weights), budget: rows },
2927 });
2928 }
2929 }
2930 }
2931 let o1 = match o1_env {
2932 crate::nystrom::O1Env::Off => None,
2933 crate::nystrom::O1Env::On(cfg) => Some(cfg),
2934 crate::nystrom::O1Env::Unset => model
2935 .header
2936 .provenance
2937 .as_ref()
2938 .and_then(|p| p.get("o1_attn"))
2939 .and_then(crate::nystrom::O1Cfg::from_json),
2940 };
2941 if o1.is_some() {
2942 if pipeline.attn_softcap > 0.0 {
2943 return Err(CmfError::Parse(
2944 "--o1 with attention-logit soft-capping (Gemma-2) is not supported: \
2945 the streaming operator has no capped-score form"
2946 .into(),
2947 ));
2948 }
2949 pipeline.set_o1(o1);
2950 }
2951 Ok(pipeline)
2952 }
2953
2954 pub(crate) fn install_dynamic_routing(&mut self, model: &Arc<CmfModel>, force_f32: bool) {
2959 self.model = Some(model.clone());
2960 self.dyn_force_f32 = force_f32;
2961 let mut per_skill = Vec::with_capacity(model.header.skills.len());
2962 for sk in &model.header.skills {
2963 if sk.kind.as_deref() == Some(cortiq_core::knowledge::skill_kind::LOOKUP) {
2966 per_skill.push(None);
2967 continue;
2968 }
2969 let mut ffn_layers = std::collections::BTreeSet::new();
2970 let mut non_ffn = false;
2971 let prefix = format!("skill.{}.", sk.id);
2972 for t in model.skill_tensors(&sk.id) {
2973 let rel = &t.name[prefix.len()..]; let toks: Vec<&str> = rel.split('.').collect();
2975 if toks.len() >= 5 && toks[0] == "model" && toks[1] == "layers" && toks[3] == "mlp"
2976 {
2977 if let Ok(li) = toks[2].parse::<usize>() {
2978 ffn_layers.insert(li);
2979 continue;
2980 }
2981 }
2982 non_ffn = true; }
2984 if non_ffn {
2985 tracing::warn!(
2986 "skill '{}' replaces non-FFN tensors — excluded from dynamic \
2987 routing (static overlay still works)",
2988 sk.id
2989 );
2990 per_skill.push(None);
2991 } else {
2992 per_skill.push(Some(ffn_layers.into_iter().collect::<Vec<_>>()));
2993 }
2994 }
2995 self.dyn_skill_layers = per_skill;
2996 }
2997
2998 pub fn set_active_skill(&mut self, idx: Option<usize>) -> Result<(), CmfError> {
3013 if self.dyn_active == idx {
3014 return Ok(());
3015 }
3016 let model = self.model.clone().ok_or_else(|| {
3017 CmfError::Parse("dynamic routing needs a model-backed pipeline".into())
3018 })?;
3019 let mut union: std::collections::BTreeSet<usize> = std::collections::BTreeSet::new();
3020 if let Some(old) = self.dyn_active {
3021 if let Some(Some(ls)) = self.dyn_skill_layers.get(old) {
3022 union.extend(ls.iter().copied());
3023 }
3024 }
3025 let new_id: Option<String> = match idx {
3026 Some(n) => match self.dyn_skill_layers.get(n) {
3027 Some(Some(ls)) => {
3028 union.extend(ls.iter().copied());
3029 Some(model.header.skills[n].id.clone())
3030 }
3031 _ => {
3032 return Err(CmfError::Parse(format!(
3033 "skill index {n} not dynamic-eligible"
3034 )));
3035 }
3036 },
3037 None => None,
3038 };
3039 let ov = match &new_id {
3040 Some(s) => Overlay::One(s),
3041 None => Overlay::None,
3042 };
3043 let arch = model.arch();
3044 let mut rebuilt = Vec::with_capacity(union.len());
3045 for li in union {
3046 rebuilt.push((
3047 li,
3048 build_layer_ffn(&model, arch, li, self.dyn_force_f32, &ov)?,
3049 ));
3050 }
3051 for (li, ffn) in rebuilt {
3052 self.weights.layers[li].ffn = ffn;
3053 }
3054 self.invalidate_for_weight_change();
3056 self.dyn_active = idx;
3057 Ok(())
3058 }
3059}
3060
3061#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
3066pub struct PerSequenceState {
3067 pub bounded_bytes: usize,
3068 pub recurrent_bytes: usize,
3069 pub growing_layers: usize,
3070}
3071
3072pub fn per_sequence_state_bytes(model: &CmfModel) -> Result<PerSequenceState, String> {
3073 let arch = model.arch();
3074 let f = std::mem::size_of::<f32>();
3075 let mut st = PerSequenceState::default();
3076 let need = |v: Option<usize>, name: &str| {
3077 v.ok_or_else(|| format!("linear core needs arch.{name}"))
3078 };
3079 for li in 0..arch.num_layers {
3080 match arch.layer_types.get(li) {
3081 Some(LayerType::BoundedAttention) => {
3082 let ac = arch
3083 .anchor_core
3084 .as_ref()
3085 .ok_or("BoundedAttention layer without anchor_core")?;
3086 st.bounded_bytes += ac.ring_state_elems(arch.num_kv_heads, arch.head_dim) * f;
3087 }
3088 Some(LayerType::LinearAttention) => {
3089 let lc = arch
3090 .linear_core
3091 .as_ref()
3092 .ok_or("LinearAttention layer without linear_core")?;
3093 let elems = match lc.kind.as_str() {
3094 "gated_delta_net" => GdnCfg {
3095 num_v_heads: lc.num_heads,
3096 num_k_heads: need(arch.linear_num_key_heads, "linear_num_key_heads")?,
3097 key_head_dim: need(arch.linear_key_head_dim, "linear_key_head_dim")?,
3098 value_head_dim: lc.value_head_dim,
3099 conv_kernel: need(arch.linear_conv_kernel_dim, "linear_conv_kernel_dim")?,
3100 hidden_size: arch.hidden_size,
3101 rms_eps: arch.rms_norm_eps,
3102 output_gate_sigmoid: false,
3103 }
3104 .state_len(),
3105 _ => {
3106 let nphase = need(lc.nphase, "linear_core.nphase")?;
3107 let mut n = lc.num_heads * 2 * nphase * lc.value_head_dim;
3108 if let Some(c) = model.tensor(&format!("model.layers.{li}.vmf_attn.conv1d.weight")) {
3109 if c.shape.len() == 3 && c.shape[2] >= 2 {
3110 n += (c.shape[2] - 1) * arch.hidden_size;
3111 }
3112 }
3113 n
3114 }
3115 };
3116 st.recurrent_bytes += elems * f;
3117 }
3118 Some(LayerType::ShortConv) => {
3119 let k = need(arch.linear_conv_kernel_dim, "linear_conv_kernel_dim")?;
3120 st.recurrent_bytes += ShortConvCfg {
3121 hidden_size: arch.hidden_size,
3122 kernel: k,
3123 }
3124 .state_len()
3125 * f;
3126 }
3127 Some(LayerType::Kda) => {
3128 st.recurrent_bytes += crate::linear_core::KdaCfg {
3129 num_heads: need(arch.linear_num_key_heads, "linear_num_key_heads")?,
3130 head_k_dim: need(arch.linear_key_head_dim, "linear_key_head_dim")?,
3131 head_v_dim: need(arch.linear_value_head_dim, "linear_value_head_dim")?,
3132 conv_kernel: need(arch.linear_conv_kernel_dim, "linear_conv_kernel_dim")?,
3133 hidden_size: arch.hidden_size,
3134 rms_eps: arch.rms_norm_eps,
3135 }
3136 .state_len()
3137 * f;
3138 }
3139 _ => st.growing_layers += 1,
3140 }
3141 }
3142 Ok(st)
3143}
3144
3145#[cfg(test)]
3146mod tests {
3147 use super::*;
3148
3149 fn dense_mtp_ffn(width: usize) -> FfnKind {
3150 const HIDDEN: usize = 4;
3151 let matrix = |rows, cols| QTensor::from_f32(vec![0.0; rows * cols], rows, cols);
3152 FfnKind::Dense(DenseFfn {
3153 gate_proj: matrix(width, HIDDEN),
3154 up_proj: matrix(width, HIDDEN),
3155 down_proj: matrix(HIDDEN, width),
3156 act: crate::pipeline::Act::Silu,
3157 down_t: None,
3158 segs: Vec::new(),
3159 })
3160 }
3161
3162 fn mtp_config(widths: Vec<usize>) -> cortiq_core::MtpConfig {
3163 cortiq_core::MtpConfig {
3164 num_layers: 1,
3165 ffn_intermediate_sizes: Some(widths),
3166 share_lm_head: true,
3167 share_embed: true,
3168 }
3169 }
3170
3171 #[test]
3172 fn generic_mtp_declared_width_is_checked_against_its_local_ffn() {
3173 assert!(validate_generic_mtp_ffn_width(&mtp_config(vec![6]), &dense_mtp_ffn(6)).is_ok());
3174
3175 let err = validate_generic_mtp_ffn_width(&mtp_config(vec![5]), &dense_mtp_ffn(6))
3176 .unwrap_err()
3177 .to_string();
3178 assert!(err.contains("ffn_intermediate_sizes[0]=5"), "{err}");
3179 assert!(err.contains("has 6 rows"), "{err}");
3180
3181 let err = validate_generic_mtp_ffn_width(&mtp_config(vec![6, 6]), &dense_mtp_ffn(6))
3182 .unwrap_err()
3183 .to_string();
3184 assert!(err.contains("has 2 entries, expected 1"), "{err}");
3185 }
3186}