use std::fmt;
use super::catalog::{CatalogExpansion, TensorCatalog};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct QualityThresholds {
pub ppl_ratio_dwq46: f64,
pub ppl_ratio_dwq48: f64,
pub max_median_kl: f64,
}
impl QualityThresholds {
pub const ADR_012_DEFAULT: QualityThresholds = QualityThresholds {
ppl_ratio_dwq46: 1.10,
ppl_ratio_dwq48: 1.05,
max_median_kl: 0.02,
};
}
#[derive(Debug, Clone, Copy)]
pub struct EvalCorpus {
pub id: &'static str,
pub token_count: u32,
pub sha256_hex: &'static str,
}
#[derive(Debug, Clone, Copy)]
pub struct ArchEntry {
pub arch: &'static str,
pub hf_architectures: &'static [&'static str],
pub tensor_catalog: &'static TensorCatalog,
pub has_mtp: bool,
pub has_vision: bool,
pub smoke_prompts: &'static [&'static str],
pub ppl_corpus: EvalCorpus,
pub quality_thresholds: QualityThresholds,
pub disk_floor_gb: u32,
pub hf_repos: &'static [&'static str],
pub auto_override: Option<&'static str>,
}
impl ArchEntry {
pub fn expected_tensor_count(&self, exp: CatalogExpansion) -> u64 {
self.tensor_catalog.expected_tensor_count(exp)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ArchError {
UnknownArch {
requested: String,
known: Vec<&'static str>,
},
}
impl fmt::Display for ArchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ArchError::UnknownArch { requested, known } => {
write!(
f,
"unknown arch: {:?}; known arches: {}",
requested,
known.join(", ")
)
}
}
}
}
impl std::error::Error for ArchError {}
#[derive(Debug)]
pub struct ArchRegistry {
entries: &'static [&'static ArchEntry],
}
impl ArchRegistry {
pub fn global() -> &'static ArchRegistry {
&GLOBAL_REGISTRY
}
pub fn get(&self, arch: &str) -> Result<&'static ArchEntry, ArchError> {
for entry in self.entries {
if entry.arch == arch {
return Ok(entry);
}
}
Err(ArchError::UnknownArch {
requested: arch.to_string(),
known: self.known_arches(),
})
}
pub fn known_arches(&self) -> Vec<&'static str> {
let mut v: Vec<&'static str> = self.entries.iter().map(|e| e.arch).collect();
v.sort_unstable();
v
}
pub fn get_by_hf_architecture(&self, hf_arch: &str) -> Result<&'static ArchEntry, ArchError> {
for entry in self.entries {
if entry.hf_architectures.contains(&hf_arch) {
return Ok(entry);
}
}
Err(ArchError::UnknownArch {
requested: hf_arch.to_string(),
known: self.known_arches(),
})
}
pub fn iter(&self) -> impl Iterator<Item = &&'static ArchEntry> {
self.entries.iter()
}
}
pub fn lookup_auto_override(hf_architecture: &str) -> Option<String> {
ArchRegistry::global()
.get_by_hf_architecture(hf_architecture)
.ok()
.and_then(|e| e.auto_override.map(|s| s.to_string()))
}
const GLOBAL_REGISTRY: ArchRegistry = ArchRegistry {
entries: &[
&super::entries::qwen35::ENTRY,
&super::entries::qwen35moe::ENTRY,
],
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn global_registry_has_exactly_qwen35_and_qwen35moe() {
let known = ArchRegistry::global().known_arches();
assert_eq!(known, vec!["qwen35", "qwen35moe"]);
}
#[test]
fn get_returns_entry_for_known_arch() {
let e = ArchRegistry::global().get("qwen35").expect("known");
assert_eq!(e.arch, "qwen35");
let e = ArchRegistry::global().get("qwen35moe").expect("known");
assert_eq!(e.arch, "qwen35moe");
}
#[test]
fn unknown_arch_returns_uniform_structured_error() {
for bogus in &["gemma4", "ministral", "deepseekv3", "bogus", ""] {
let err = ArchRegistry::global().get(bogus).unwrap_err();
match err {
ArchError::UnknownArch { requested, known } => {
assert_eq!(requested, *bogus);
assert_eq!(known, vec!["qwen35", "qwen35moe"]);
}
}
}
}
#[test]
fn hf_architectures_dispatch_preserves_arch_routing() {
let e = ArchRegistry::global()
.get_by_hf_architecture("Qwen3_5ForCausalLM")
.expect("Qwen3_5ForCausalLM → qwen35");
assert_eq!(e.arch, "qwen35");
let e = ArchRegistry::global()
.get_by_hf_architecture("Qwen3_5MoeForCausalLM")
.expect("Qwen3_5MoeForCausalLM → qwen35moe");
assert_eq!(e.arch, "qwen35moe");
}
#[test]
fn hf_architectures_dispatch_resolves_conditional_generation_aliases() {
let e = ArchRegistry::global()
.get_by_hf_architecture("Qwen3_5ForConditionalGeneration")
.expect("Qwen3_5ForConditionalGeneration → qwen35");
assert_eq!(e.arch, "qwen35");
let e = ArchRegistry::global()
.get_by_hf_architecture("Qwen3_5MoeForConditionalGeneration")
.expect("Qwen3_5MoeForConditionalGeneration → qwen35moe");
assert_eq!(e.arch, "qwen35moe");
}
#[test]
fn unknown_hf_architecture_returns_uniform_error() {
let err = ArchRegistry::global()
.get_by_hf_architecture("FakeNonexistentForCausalLM")
.unwrap_err();
match err {
ArchError::UnknownArch { requested, known } => {
assert_eq!(requested, "FakeNonexistentForCausalLM");
assert_eq!(known, vec!["qwen35", "qwen35moe"]);
}
}
}
#[test]
fn quality_thresholds_match_adr012_party_mode() {
assert_eq!(QualityThresholds::ADR_012_DEFAULT.ppl_ratio_dwq46, 1.10);
assert_eq!(QualityThresholds::ADR_012_DEFAULT.ppl_ratio_dwq48, 1.05);
assert_eq!(QualityThresholds::ADR_012_DEFAULT.max_median_kl, 0.02);
}
#[test]
fn arch_error_display_is_actionable() {
let err = ArchError::UnknownArch {
requested: "gemma4".to_string(),
known: vec!["qwen35", "qwen35moe"],
};
let s = format!("{}", err);
assert!(s.contains("unknown arch"));
assert!(s.contains("\"gemma4\""));
assert!(s.contains("qwen35"));
assert!(s.contains("qwen35moe"));
}
#[test]
fn every_catalog_entry_passes_template_invariants() {
use super::super::catalog::LayerScope;
for entry in ArchRegistry::global().iter() {
let mut seen_names = std::collections::HashSet::new();
for cat_entry in entry.tensor_catalog.entries {
assert!(
!cat_entry.name_template.is_empty(),
"{}: catalog entry has empty name_template",
entry.arch
);
assert!(
!cat_entry.citation.is_empty(),
"{}: catalog entry {:?} has empty citation — ADR-012 mantra violation",
entry.arch,
cat_entry.name_template
);
let has_l = cat_entry.name_template.contains("{L}");
let has_x = cat_entry.name_template.contains("{X}");
match cat_entry.scope {
LayerScope::Global => {
assert!(
!has_l && !has_x,
"{}: Global-scope entry {:?} must not contain {{L}}/{{X}}",
entry.arch,
cat_entry.name_template
);
}
LayerScope::AllLayers
| LayerScope::FullAttentionLayersOnly
| LayerScope::LinearAttentionLayersOnly
| LayerScope::MtpLayers
| LayerScope::MoeSharedExpertPerLayer
| LayerScope::MoeRouterPerLayer => {
assert!(
has_l,
"{}: per-layer entry {:?} MUST contain {{L}} \
(else expand_names emits N duplicates)",
entry.arch, cat_entry.name_template
);
assert!(
!has_x,
"{}: non-expert entry {:?} must not contain {{X}}",
entry.arch, cat_entry.name_template
);
}
LayerScope::MoeExpertsPerLayer => {
assert!(
has_l,
"{}: MoeExpertsPerLayer {:?} MUST contain {{L}}",
entry.arch, cat_entry.name_template
);
assert!(
has_x,
"{}: MoeExpertsPerLayer {:?} MUST contain {{X}}",
entry.arch, cat_entry.name_template
);
}
}
assert!(
seen_names.insert(cat_entry.name_template),
"{}: duplicate name_template {:?} in catalog",
entry.arch,
cat_entry.name_template
);
}
}
}
#[test]
fn every_registered_entry_passes_field_invariants() {
for entry in ArchRegistry::global().iter() {
assert!(!entry.arch.is_empty(), "arch string must be non-empty");
assert!(
!entry.hf_architectures.is_empty(),
"{}: hf_architectures must list at least one HF arch",
entry.arch
);
for hf in entry.hf_architectures {
assert!(
!hf.is_empty(),
"{}: hf_architectures must not contain empty strings",
entry.arch
);
}
assert!(
entry.disk_floor_gb > 0,
"{}: disk_floor_gb must be > 0 (else preflight exit 3 never fires)",
entry.arch
);
assert!(
!entry.hf_repos.is_empty(),
"{}: hf_repos must list at least one resolvable repo",
entry.arch
);
for repo in entry.hf_repos {
assert!(
repo.contains('/'),
"{}: hf_repo {:?} must be `owner/name` form",
entry.arch,
repo
);
}
assert!(
!entry.smoke_prompts.is_empty(),
"{}: smoke_prompts must have at least one prompt (llama-cli needs -p)",
entry.arch
);
for prompt in entry.smoke_prompts {
assert!(
!prompt.trim().is_empty(),
"{}: smoke_prompts must not contain whitespace-only prompts",
entry.arch
);
}
assert!(
!entry.tensor_catalog.entries.is_empty(),
"{}: tensor_catalog must have at least one entry",
entry.arch
);
assert!(
entry.quality_thresholds.ppl_ratio_dwq46 >= 1.0,
"{}: ppl_ratio_dwq46 must be >= 1.0",
entry.arch
);
assert!(
entry.quality_thresholds.ppl_ratio_dwq48 >= 1.0,
"{}: ppl_ratio_dwq48 must be >= 1.0",
entry.arch
);
assert!(
entry.quality_thresholds.ppl_ratio_dwq48
<= entry.quality_thresholds.ppl_ratio_dwq46,
"{}: dwq48 threshold must be tighter than dwq46 (8-bit keeps more fidelity)",
entry.arch
);
assert!(
entry.quality_thresholds.max_median_kl > 0.0,
"{}: max_median_kl must be > 0",
entry.arch
);
}
}
}