use anyhow::{bail, Result};
use mlx_native::gguf::GgufFile;
use crate::inference::glp::bind::BoundGlp;
use crate::inference::glp::reader::{GlpHookPoint, GlpVector};
use super::deepseek4::Deepseek4Family;
use super::qwen35::Qwen35Family;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CaptureSite {
ResidualStreamPostLayer,
FfnOutPreResidual,
}
impl CaptureSite {
pub fn as_spec_str(self) -> &'static str {
match self {
Self::ResidualStreamPostLayer => "residual_stream_post_layer",
Self::FfnOutPreResidual => "ffn_out_pre_residual",
}
}
pub fn as_hook_point(self) -> GlpHookPoint {
match self {
Self::ResidualStreamPostLayer => GlpHookPoint::ResidualStreamPostLayer,
Self::FfnOutPreResidual => GlpHookPoint::FfnOutPreResidual,
}
}
pub fn parse_cli(value: &str) -> Result<Self> {
match value {
"residual" => Ok(Self::ResidualStreamPostLayer),
"writer" => Ok(Self::FfnOutPreResidual),
other => bail!(
"unknown site {other:?}; expected `residual` or `writer` \
(residual_stream_post_layer / ffn_out_pre_residual)"
),
}
}
}
#[derive(Debug)]
pub enum CapturedState {
Dense(Vec<f32>),
MhcStreams(Vec<f32>),
}
impl CapturedState {
pub fn as_flat(&self) -> &[f32] {
match self {
Self::Dense(values) => values.as_slice(),
Self::MhcStreams(values) => values.as_slice(),
}
}
}
#[derive(Clone, Debug)]
pub struct FamilyFacts {
pub family: &'static str,
pub num_layers: u32,
pub hidden: u32,
pub hc: Option<u32>,
pub capture_sites: &'static [CaptureSite],
pub default_hook: GlpHookPoint,
pub default_derived_at: &'static str,
pub default_layer: usize,
}
impl FamilyFacts {
pub fn capture_streams(&self, site: CaptureSite) -> usize {
match site {
CaptureSite::ResidualStreamPostLayer => {
self.hc.map(|k| k as usize).unwrap_or(1)
}
CaptureSite::FfnOutPreResidual => 1,
}
}
pub fn ensure_capture_site(&self, site: CaptureSite) -> Result<()> {
if self.capture_sites.contains(&site) {
return Ok(());
}
let supported = self
.capture_sites
.iter()
.map(|site| site.as_spec_str())
.collect::<Vec<_>>()
.join(", ");
bail!(
"capture site {} is not implemented for family {} \
(implemented capture sites: {supported}); a site is never \
silently routed through another family's capture",
site.as_spec_str(),
self.family
);
}
}
pub trait CalibrationFamily {
fn facts(&self) -> &FamilyFacts;
fn encode(&mut self, text: &str) -> Result<Vec<u32>>;
fn capture(&mut self, tokens: &[u32], site: CaptureSite, layer: usize)
-> Result<CapturedState>;
fn probe_logits(&mut self, tokens: &[u32]) -> Result<Vec<f32>>;
fn bind_glp(&mut self, vector: GlpVector, alpha: Option<f32>) -> Result<()>;
fn clear_glp(&mut self);
fn generate_openings(&mut self, probes: &[String], max_tokens: usize) -> Result<Vec<String>>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FamilyKind {
Deepseek4,
Qwen35,
}
pub fn family_kind_for_architecture(architecture: &str) -> Result<FamilyKind> {
match architecture {
"deepseek4" => Ok(FamilyKind::Deepseek4),
"qwen35" | "qwen35moe" => Ok(FamilyKind::Qwen35),
other => bail!(
"hf2q calibrate is not supported for `{other}` models; it is \
available for {} — the same family set as `--glp` serving \
(gemma4 has no GLP family by owner decision and is refused at \
both surfaces)",
crate::inference::glp::GLP_SERVING_ARCHITECTURES.join(", ")
),
}
}
pub fn load_family(gguf: &GgufFile) -> Result<Box<dyn CalibrationFamily>> {
let architecture = gguf
.metadata_string("general.architecture")
.filter(|value| !value.is_empty())
.ok_or_else(|| anyhow::anyhow!("GGUF is missing required `general.architecture`"))?;
match family_kind_for_architecture(architecture)? {
FamilyKind::Deepseek4 => Ok(Box::new(Deepseek4Family::load(gguf)?)),
FamilyKind::Qwen35 => Ok(Box::new(Qwen35Family::load(gguf)?)),
}
}
pub fn family_facts(gguf: &GgufFile) -> Result<FamilyFacts> {
let architecture = gguf
.metadata_string("general.architecture")
.filter(|value| !value.is_empty())
.ok_or_else(|| anyhow::anyhow!("GGUF is missing required `general.architecture`"))?;
match family_kind_for_architecture(architecture)? {
FamilyKind::Deepseek4 => Deepseek4Family::facts_from_gguf(gguf),
FamilyKind::Qwen35 => Qwen35Family::facts_from_gguf(gguf),
}
}
pub(crate) const FAMILY_HOOK_SET: [GlpHookPoint; 2] = [
GlpHookPoint::ResidualStreamPostLayer,
GlpHookPoint::FfnOutPreResidual,
];
pub(crate) fn bound_glp_debug_line(bound: &BoundGlp) -> String {
format!(
"[calibrate] debug: bound alpha={} layers={:?}",
bound.alpha,
bound.device_directions.keys().collect::<Vec<_>>()
)
}