use crate::attention::gdn::GatedDeltaNetWeights;
use crate::error::InferenceError;
pub(crate) struct FullAttentionLayerWeights {
pub(crate) q_proj: Vec<f32>, pub(crate) k_proj: Vec<f32>, pub(crate) v_proj: Vec<f32>, pub(crate) o_proj: Vec<f32>, pub(crate) q_norm: Vec<f32>, pub(crate) k_norm: Vec<f32>, }
pub(crate) struct CommonLayerWeights {
pub(crate) input_layernorm: Vec<f32>, pub(crate) post_attention_layernorm: Vec<f32>, pub(crate) ffn: FeedForwardWeights,
}
pub(crate) enum FeedForwardWeights {
Dense(DenseFfnWeights),
Moe(MoeLayerWeights),
}
pub(crate) struct DenseFfnWeights {
pub(crate) gate_proj: Vec<f32>, pub(crate) up_proj: Vec<f32>, pub(crate) down_proj: Vec<f32>, }
pub(crate) enum AttentionWeights {
Linear(GatedDeltaNetWeights),
Full(FullAttentionLayerWeights),
}
pub struct ModelWeights {
pub(crate) embed_tokens: Vec<f32>, pub(crate) lm_head: Option<Vec<f32>>, pub(crate) final_norm: Vec<f32>, pub(crate) layers: Vec<(AttentionWeights, CommonLayerWeights)>,
}
impl ModelWeights {
pub(crate) fn logits_weight(&self) -> &[f32] {
self.lm_head.as_deref().unwrap_or(&self.embed_tokens)
}
}
#[derive(Debug)]
pub(crate) struct MoeRouter {
pub(crate) gate: Vec<f32>, pub(crate) num_experts: usize,
pub(crate) num_experts_per_tok: usize,
pub(crate) hidden_size: usize,
}
impl MoeRouter {
pub(crate) fn new(
gate: Vec<f32>,
num_experts: usize,
num_experts_per_tok: usize,
hidden_size: usize,
) -> Result<Self, InferenceError> {
let expected = num_experts
.checked_mul(hidden_size)
.ok_or_else(|| InferenceError::Inference("MoE router shape overflow".into()))?;
if gate.len() != expected {
return Err(InferenceError::ShapeMismatch {
name: "mlp.gate.weight".to_string(),
expected: vec![num_experts, hidden_size],
actual: vec![gate.len()],
});
}
if num_experts_per_tok == 0 || num_experts_per_tok > num_experts {
return Err(InferenceError::UnsupportedModel(format!(
"invalid MoE top_k={num_experts_per_tok} for num_experts={num_experts}"
)));
}
Ok(Self {
gate,
num_experts,
num_experts_per_tok,
hidden_size,
})
}
}
#[derive(Debug)]
pub(crate) struct RoutedExperts {
pub(crate) gate_up_proj: Vec<f32>, pub(crate) down_proj: Vec<f32>, pub(crate) num_experts: usize,
pub(crate) hidden_size: usize,
pub(crate) intermediate_size: usize,
}
impl RoutedExperts {
pub(crate) fn new(
gate_up_proj: Vec<f32>,
down_proj: Vec<f32>,
num_experts: usize,
hidden_size: usize,
intermediate_size: usize,
) -> Result<Self, InferenceError> {
let expected_gate_up = num_experts
.checked_mul(2 * intermediate_size)
.and_then(|x| x.checked_mul(hidden_size))
.ok_or_else(|| {
InferenceError::Inference("RoutedExperts gate_up_proj shape overflow".into())
})?;
let expected_down = num_experts
.checked_mul(hidden_size)
.and_then(|x| x.checked_mul(intermediate_size))
.ok_or_else(|| {
InferenceError::Inference("RoutedExperts down_proj shape overflow".into())
})?;
if gate_up_proj.len() != expected_gate_up {
return Err(InferenceError::ShapeMismatch {
name: "mlp.experts.gate_up_proj".to_string(),
expected: vec![num_experts, 2 * intermediate_size, hidden_size],
actual: vec![gate_up_proj.len()],
});
}
if down_proj.len() != expected_down {
return Err(InferenceError::ShapeMismatch {
name: "mlp.experts.down_proj".to_string(),
expected: vec![num_experts, hidden_size, intermediate_size],
actual: vec![down_proj.len()],
});
}
Ok(Self {
gate_up_proj,
down_proj,
num_experts,
hidden_size,
intermediate_size,
})
}
}
#[derive(Debug)]
pub(crate) struct SharedExpert {
pub(crate) gate_proj: Vec<f32>, pub(crate) up_proj: Vec<f32>, pub(crate) down_proj: Vec<f32>, pub(crate) shared_expert_gate: Vec<f32>, pub(crate) hidden_size: usize,
pub(crate) intermediate_size: usize,
}
impl SharedExpert {
pub(crate) fn new(
gate_proj: Vec<f32>,
up_proj: Vec<f32>,
down_proj: Vec<f32>,
shared_expert_gate: Vec<f32>,
hidden_size: usize,
intermediate_size: usize,
) -> Result<Self, InferenceError> {
let up_shape = intermediate_size
.checked_mul(hidden_size)
.ok_or_else(|| InferenceError::Inference("shared expert shape overflow".into()))?;
let down_shape = hidden_size
.checked_mul(intermediate_size)
.ok_or_else(|| InferenceError::Inference("shared expert down shape overflow".into()))?;
if gate_proj.len() != up_shape {
return Err(InferenceError::ShapeMismatch {
name: "mlp.shared_expert.gate_proj.weight".to_string(),
expected: vec![intermediate_size, hidden_size],
actual: vec![gate_proj.len()],
});
}
if up_proj.len() != up_shape {
return Err(InferenceError::ShapeMismatch {
name: "mlp.shared_expert.up_proj.weight".to_string(),
expected: vec![intermediate_size, hidden_size],
actual: vec![up_proj.len()],
});
}
if down_proj.len() != down_shape {
return Err(InferenceError::ShapeMismatch {
name: "mlp.shared_expert.down_proj.weight".to_string(),
expected: vec![hidden_size, intermediate_size],
actual: vec![down_proj.len()],
});
}
if shared_expert_gate.len() != hidden_size {
return Err(InferenceError::ShapeMismatch {
name: "mlp.shared_expert_gate.weight".to_string(),
expected: vec![1, hidden_size],
actual: vec![shared_expert_gate.len()],
});
}
Ok(Self {
gate_proj,
up_proj,
down_proj,
shared_expert_gate,
hidden_size,
intermediate_size,
})
}
}
pub(crate) struct MoeLayerWeights {
pub(crate) router: MoeRouter,
pub(crate) experts: RoutedExperts,
pub(crate) shared_expert: SharedExpert,
}