use model_io::{ArchConfig, ModelFamily, ResidentIndex};
use crate::real_forward::RealForwardError;
use crate::real_forward_types::MAX_PREFILL_BATCH;
const BF16_ONE: u16 = 0x3F80;
pub(crate) struct RealDeepseek2State {
pub(crate) rms_eps: f32,
pub(crate) router_ones: gpu::MetalBuffer,
pub(crate) heads: u32,
pub(crate) nope: u32,
pub(crate) rope_dim: u32,
pub(crate) kv_lora: u32,
pub(crate) v_dim: u32,
pub(crate) head_dim: u32,
pub(crate) cache_row: u32,
pub(crate) rope_mscale: f32,
pub(crate) rope_frequencies: gpu::MetalBuffer,
pub(crate) q: gpu::MetalBuffer,
pub(crate) q_abs: gpu::MetalBuffer,
pub(crate) attn_c: gpu::MetalBuffer,
pub(crate) mla_out: gpu::MetalBuffer,
pub(crate) ffn_a: gpu::MetalBuffer,
pub(crate) ffn_b: gpu::MetalBuffer,
pub(crate) shared_out: gpu::MetalBuffer,
pub(crate) router_logits_f32: gpu::MetalBuffer,
pub(crate) moe_x: gpu::MetalBuffer,
pub(crate) h2: gpu::MetalBuffer,
pub(crate) lead: usize,
pub(crate) dense_inter: usize,
pub(crate) shared_inter: usize,
}
impl RealDeepseek2State {
pub(crate) fn build(
context: &mut gpu::MetalContext,
index: &ResidentIndex,
arch: &ArchConfig,
) -> Result<Self, RealForwardError> {
let unsupported = |detail: String| Err(RealForwardError::Unsupported(detail));
if arch.family != ModelFamily::Deepseek2 {
return unsupported("deepseek2 state built for another family".into());
}
if arch.ffn_sandwich_norms
|| arch.router_scaled
|| arch.embedding_scaled_by_sqrt_hidden
|| arch.attn_output_gate
|| arch.shared_expert_gated
|| arch.attention_k_eq_v
{
return unsupported(
"the deepseek2 flow takes none of ffnSandwichNorms / routerScaled / \
embeddingScaledBySqrtHidden / attnOutputGate / sharedExpertGated / \
attentionKEqV"
.to_string(),
);
}
if arch.final_logit_softcap != 0.0 {
return unsupported("the deepseek2 architecture has no final logit softcap".into());
}
if arch.full_attention_layer_mask.iter().any(|&m| m != 5) {
return unsupported(
"every deepseek2 layer is MLA (mask 5); a mixed mask is not wired".to_string(),
);
}
let mla = arch.mla;
if !mla.is_active() {
return unsupported("deepseek2 install declares no MLA geometry".into());
}
if mla.q_lora_rank != 0 {
return unsupported(format!(
"q low-rank {} is the full V2/V3 branch, which this port does not execute",
mla.q_lora_rank
));
}
let heads = arch.num_heads as u32;
let nope = mla.nope_head_dim as u32;
let rope_dim = mla.rope_head_dim as u32;
let kv_lora = mla.kv_lora_rank as u32;
let v_dim = mla.v_head_dim as u32;
let head_dim = mla.key_head_dim() as u32;
let cache_row = mla.cache_row_dim() as u32;
let q_rows = arch.num_heads * mla.key_head_dim();
let kv_b_rows = arch.num_heads * (mla.nope_head_dim + mla.v_head_dim);
let o_rows = arch.num_heads * mla.v_head_dim;
if q_rows != heads as i64 * head_dim as i64
|| kv_b_rows != arch.num_heads * (mla.nope_head_dim + mla.v_head_dim)
|| o_rows != arch.num_heads * mla.v_head_dim
{
return unsupported(
"deepseek2 head geometry disagrees with itself (heads x (nope + rope) / \
(nope + v) / v)"
.to_string(),
);
}
if kv_lora != 512 {
return unsupported(format!(
"the MLA attention kernel fixes its accumulator at kv_lora 512; this \
install declares {kv_lora}"
));
}
if nope % 32 != 0 || kv_lora % 32 != 0 || v_dim % 8 != 0 || rope_dim % 2 != 0 {
return unsupported(
"deepseek2 MLA dims must keep nope/kv_lora on 32-block and v_dim on \
8-row boundaries, and rope even"
.to_string(),
);
}
let top_k = arch.top_k_experts as usize;
if top_k == 0 || arch.num_experts == 0 {
return unsupported("deepseek2 installs are MoE; experts must be positive".into());
}
if top_k > gpu::PHASE2_FIXED_SLOTS {
return unsupported(format!(
"top_k {top_k} exceeds the {}-slot GGUF MoE kernels",
gpu::PHASE2_FIXED_SLOTS
));
}
let lead = arch.num_dense_leading_layers.max(0) as usize;
if lead >= arch.num_layers as usize {
return unsupported("dense lead leaves no MoE layers".into());
}
if arch.dense_lead_intermediate_size <= 0 {
return unsupported(
"deepseek2 declares a dense lead count but no dense lead FFN width".into(),
);
}
let _ = arch.rope_scaling.yarn_mscale(); let rope_mscale: f32 = 1.0;
let yarn = compute::yarn_frequencies(
rope_dim as usize,
arch.full_rope_theta as f32,
arch.rope_scaling.factor as f32,
arch.rope_scaling.original_context,
arch.rope_scaling.beta_fast as f32,
arch.rope_scaling.beta_slow as f32,
);
let freq_bytes: Vec<u8> = yarn
.frequencies
.iter()
.flat_map(|f| f.to_le_bytes())
.collect();
let rope_frequencies = context.new_output_buffer(freq_bytes.len().max(4) as u64);
gpu::write_buffer_bytes(&rope_frequencies, 0, &freq_bytes);
let ones: Vec<u8> = (0..arch.hidden_size as usize)
.flat_map(|_| BF16_ONE.to_le_bytes())
.collect();
let router_ones = context.new_output_buffer(ones.len() as u64);
gpu::write_buffer_bytes(&router_ones, 0, &ones);
for (layer, suffix) in [
(0usize, "mlp.gate_proj.weight"),
(0usize, "mlp.up_proj.weight"),
(0usize, "mlp.down_proj.weight"),
(lead, "mlp.gate.weight"),
(lead, "mlp.shared_expert.gate_proj.weight"),
(lead, "mlp.shared_expert.up_proj.weight"),
(lead, "mlp.shared_expert.down_proj.weight"),
] {
let name = crate::families::deepseek2::layer_tensor(layer, suffix);
if !index.entries.contains_key(&name) {
return Err(RealForwardError::MissingTensor(name));
}
}
let hidden = arch.hidden_size as usize;
let bf16 = |n: usize| (n * 2) as u64;
Ok(Self {
rms_eps: 1e-6,
router_ones,
heads,
nope,
rope_dim,
kv_lora,
v_dim,
head_dim,
cache_row,
rope_mscale,
rope_frequencies,
q: context.new_output_buffer(bf16(heads as usize * head_dim as usize)),
q_abs: context.new_output_buffer(bf16(heads as usize * cache_row as usize)),
attn_c: context.new_output_buffer(bf16(heads as usize * kv_lora as usize)),
mla_out: context.new_output_buffer(bf16(heads as usize * v_dim as usize)),
ffn_a: context.new_output_buffer(bf16(
arch.dense_lead_intermediate_size
.max(arch.intermediate_size) as usize,
)),
ffn_b: context.new_output_buffer(bf16(
arch.dense_lead_intermediate_size
.max(arch.intermediate_size) as usize,
)),
shared_out: context.new_output_buffer(bf16(hidden)),
router_logits_f32: context
.new_output_buffer((MAX_PREFILL_BATCH * arch.num_experts as usize * 4) as u64),
moe_x: context.new_output_buffer(bf16(MAX_PREFILL_BATCH * hidden)),
h2: context.new_output_buffer(bf16(hidden)),
lead,
dense_inter: arch.dense_lead_intermediate_size as usize,
shared_inter: arch.intermediate_size as usize,
})
}
}