use frink_gguf::TensorSource;
use frink_moe::DenseBias;
use crate::loader::{load_f32_vec, LoadError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Presence {
Required,
Optional,
}
pub const ATTN_OUT_BIAS_CREATORS: &[(&str, Presence)] = &[
("apertus", Presence::Optional),
("bloom", Presence::Required),
("codeshell", Presence::Required),
("deci", Presence::Optional),
("ernie4_5", Presence::Optional),
("gpt-oss", Presence::Required),
("gpt2", Presence::Required),
("granite_swa", Presence::Optional),
("gptneox", Presence::Required),
("granite", Presence::Optional),
("granitehybrid", Presence::Optional),
("granite-hybrid", Presence::Optional),
("jais", Presence::Required),
("mpt", Presence::Optional),
("phi2", Presence::Required),
("granitemoe", Presence::Optional),
("granite-moe", Presence::Optional),
("jais2", Presence::Required),
("llama", Presence::Optional),
("minicpm", Presence::Optional),
("mistral3", Presence::Optional),
("nemotron", Presence::Optional),
("nemotron_h", Presence::Optional),
("nemotron_h_moe", Presence::Optional),
("pangu-embedded", Presence::Required),
("phimoe", Presence::Required),
("starcoder", Presence::Required),
("starcoder2", Presence::Required),
];
pub const FFN_BIAS_CREATORS: &[(&str, Presence, bool)] = &[
("bloom", Presence::Required, false),
("codeshell", Presence::Required, false),
("deci", Presence::Optional, true),
("gpt2", Presence::Required, false),
("gptneox", Presence::Required, false),
("granite", Presence::Optional, true),
("granite_swa", Presence::Optional, true),
("granitehybrid", Presence::Optional, true),
("granite-hybrid", Presence::Optional, true),
("granitemoe", Presence::Optional, true),
("granite-moe", Presence::Optional, true),
("jais", Presence::Required, true),
("jais2", Presence::Required, false),
("llama", Presence::Optional, true),
("minicpm", Presence::Optional, true),
("mistral3", Presence::Optional, true),
("mpt", Presence::Optional, false),
("nemotron", Presence::Optional, false),
("nemotron_h", Presence::Optional, false),
("phi2", Presence::Required, false),
("starcoder", Presence::Required, false),
("starcoder2", Presence::Required, false),
];
pub const OUTPUT_BIAS_CREATORS: &[(&str, Presence)] = &[
("phi2", Presence::Required),
("phimoe", Presence::Required),
("qwen2", Presence::Optional),
];
fn output_presence(arch: &str) -> Option<Presence> {
OUTPUT_BIAS_CREATORS
.iter()
.find(|(n, _)| *n == arch)
.map(|(_, p)| *p)
}
pub fn load_output_bias(
file: &impl TensorSource,
arch: &str,
vocab_size: usize,
) -> Result<Option<Vec<f32>>, LoadError> {
let Some(presence) = output_presence(arch) else {
return Ok(None);
};
load_bias(file, arch, "output.bias", presence, vocab_size)
}
fn attn_out_presence(arch: &str) -> Option<Presence> {
ATTN_OUT_BIAS_CREATORS
.iter()
.find(|(n, _)| *n == arch)
.map(|(_, p)| *p)
}
fn ffn_presence(arch: &str) -> Option<(Presence, bool)> {
FFN_BIAS_CREATORS
.iter()
.find(|(n, _, _)| *n == arch)
.map(|(_, p, g)| (*p, *g))
}
fn load_bias(
file: &impl TensorSource,
arch: &str,
name: &str,
presence: Presence,
len: usize,
) -> Result<Option<Vec<f32>>, LoadError> {
if file.find_tensor(name).is_none() {
return match presence {
Presence::Required => Err(LoadError::Gguf(frink_gguf::GgufError::TensorNotFound(
format!("{name} (REQUIRED by `{arch}`'s graph)"),
))),
Presence::Optional => Ok(None),
};
}
let v = load_f32_vec(file, name)?;
if v.len() != len {
return Err(LoadError::UnsupportedFeature(
arch.to_string(),
format!("{name} has {} entries, expected {len}", v.len()),
));
}
Ok(Some(v))
}
pub fn load_attn_out_bias(
file: &impl TensorSource,
arch: &str,
l: usize,
hidden_dim: usize,
) -> Result<Option<Vec<f32>>, LoadError> {
let Some(presence) = attn_out_presence(arch) else {
return Ok(None);
};
load_bias(
file,
arch,
&format!("blk.{l}.attn_output.bias"),
presence,
hidden_dim,
)
}
pub fn load_dense_ffn_bias(
file: &impl TensorSource,
arch: &str,
l: usize,
hidden_dim: usize,
ffn_dim: usize,
ungated: bool,
) -> Result<Option<DenseBias>, LoadError> {
let Some((presence, has_gate)) = ffn_presence(arch) else {
return Ok(None);
};
let up = load_bias(
file,
arch,
&format!("blk.{l}.ffn_up.bias"),
presence,
ffn_dim,
)?;
let down = load_bias(
file,
arch,
&format!("blk.{l}.ffn_down.bias"),
presence,
hidden_dim,
)?;
let gate = if has_gate && !ungated {
load_bias(
file,
arch,
&format!("blk.{l}.ffn_gate.bias"),
Presence::Optional,
ffn_dim,
)?
} else {
None
};
let bias = DenseBias { gate, up, down };
Ok((!bias.is_empty()).then_some(bias))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_creator_is_a_generic_row() {
let names: Vec<&str> = ATTN_OUT_BIAS_CREATORS
.iter()
.map(|(n, _)| *n)
.chain(FFN_BIAS_CREATORS.iter().map(|(n, _, _)| *n))
.collect();
for name in names {
let profile = crate::capability::resolve_profile(name)
.unwrap_or_else(|| panic!("`{name}` is not a registered architecture"));
assert!(
matches!(
profile.path,
crate::capability::ArchPath::GenericGqa { .. }
| crate::capability::ArchPath::DedicatedOnly { .. }
),
"`{name}` is {:?}",
profile.path
);
}
for name in ["starcoder2", "codeshell", "jais2"] {
assert!(
crate::capability::AUDITED_GENERIC_GQA.contains(&name),
"{name}"
);
assert_eq!(attn_out_presence(name), Some(Presence::Required));
assert!(matches!(
ffn_presence(name),
Some((Presence::Required, false))
));
}
assert_eq!(attn_out_presence("gpt-oss"), Some(Presence::Required));
assert_eq!(attn_out_presence("qwen3"), None);
assert_eq!(ffn_presence("qwen3"), None);
}
}