use std::path::{Path, PathBuf};
pub(crate) mod cache;
mod gguf_repository;
mod product_source_selection;
pub(crate) mod recipes;
#[cfg(test)]
mod source_identity_tests;
use cache::{CacheRequirements, CachedModel};
pub const METAL_FIRST_SUCCESS_MODEL: &str = "qwen3.5:4b-q4_k_m";
pub const CUDA_FIRST_SUCCESS_MODEL: &str = "qwen3.5:4b";
pub fn first_success_model_help(command: &str) -> String {
format!(
"no model selected. Choose one explicitly:\n Metal: ferrum {command} {METAL_FIRST_SUCCESS_MODEL}\n CUDA: ferrum {command} {CUDA_FIRST_SUCCESS_MODEL}\nRun `ferrum doctor` to inspect this binary before downloading a model."
)
}
use std::sync::Arc;
use clap::Args;
use ferrum_interfaces::vnext::{
ModelSourceKind, OriginalModelSource, OriginalModelSources, ProductModelSourceIdentity,
};
use ferrum_models::source::{ModelFormat, ResolvedModelSource};
use ferrum_models::vnext::{
huggingface_snapshot_identity, open_registered_product_sources, ProductionModelSourceBundle,
ProductionWeightArtifact,
};
use ferrum_server::chat_template::ModelChatTemplate;
use ferrum_types::{
EngineConfig, FerrumError, ModelId, ModelSource, Result, RuntimeConfigEntry,
RuntimeConfigSnapshot, RuntimeConfigSource,
};
use sha2::{Digest, Sha256};
use crate::config::CliConfig;
use crate::gpu_mem_autosize::{apply_auto_size_with_profile, AutoSizeProfile};
#[derive(Args, Debug, Clone, Default)]
pub struct ProductSourceArgs {
#[arg(long, value_name = "FILE")]
pub gguf_file: Option<String>,
#[arg(long, value_name = "DIR|HF_REPO@COMMIT")]
pub semantic_source: Option<PathBuf>,
#[arg(long, value_name = "DIR|HF_REPO@COMMIT")]
pub tokenizer_source: Option<PathBuf>,
}
pub fn hf_cache_dir(config: &CliConfig) -> PathBuf {
if let Ok(hf_home) = std::env::var("HF_HOME") {
return PathBuf::from(hf_home);
}
PathBuf::from(shellexpand::tilde(&config.models.download.hf_cache_dir).as_ref())
}
pub fn detect_format(path: &Path) -> ModelFormat {
if path.is_file()
&& path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false)
{
return ModelFormat::GGUF;
}
if path.join("model.safetensors").exists() || path.join("model.safetensors.index.json").exists()
{
ModelFormat::SafeTensors
} else if path.join("pytorch_model.bin").exists() {
ModelFormat::PyTorchBin
} else {
ModelFormat::Unknown
}
}
pub fn looks_like_gguf_path(model: &str) -> bool {
let p = PathBuf::from(model);
p.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false)
&& p.is_file()
}
pub fn public_model_id(source: &ResolvedModelSource) -> String {
if let Some(identity) = huggingface_snapshot_identity(&source.local_path) {
return identity.repository_id;
}
match source.format {
ModelFormat::GGUF => source
.local_path
.file_stem()
.map(|value| value.to_string_lossy().into_owned())
.unwrap_or_else(|| source.original.clone()),
_ if source.local_path == Path::new(&source.original) => source
.local_path
.file_name()
.map(|value| value.to_string_lossy().into_owned())
.unwrap_or_else(|| source.original.clone()),
_ => source.original.clone(),
}
}
pub fn resolve_model_alias(name: &str) -> String {
if let Some(recipe) = recipes::find(name) {
return recipe.requested_model.to_owned();
}
match name.to_lowercase().as_str() {
"tinyllama" | "tiny" => "TinyLlama/TinyLlama-1.1B-Chat-v1.0".to_string(),
"qwen2.5:0.5b" | "qwen:0.5b" => "Qwen/Qwen2.5-0.5B-Instruct".to_string(),
"qwen2.5:1.5b" | "qwen:1.5b" => "Qwen/Qwen2.5-1.5B-Instruct".to_string(),
"qwen2.5:3b" | "qwen:3b" => "Qwen/Qwen2.5-3B-Instruct".to_string(),
"qwen2.5:7b" | "qwen:7b" => "Qwen/Qwen2.5-7B-Instruct".to_string(),
"qwen3:0.6b" => "Qwen/Qwen3-0.6B".to_string(),
"qwen3:1.7b" => "Qwen/Qwen3-1.7B".to_string(),
"qwen3:4b" => "Qwen/Qwen3-4B".to_string(),
"qwen3:14b" => "Qwen/Qwen3-14B".to_string(),
"qwen3:32b" => "Qwen/Qwen3-32B".to_string(),
"qwen3.5:4b" => "Qwen/Qwen3.5-4B".to_string(),
"qwen3-coder:30b" | "qwen3-coder:30b-a3b" => {
"Qwen/Qwen3-Coder-30B-A3B-Instruct".to_string()
}
"qwen3-coder:30b-gptq" => "jart25/Qwen3-Coder-30B-A3B-Instruct-Int4-gptq".to_string(),
"qwen3:14b-gptq" => "JunHowie/Qwen3-14B-GPTQ-Int4".to_string(),
"qwen3:32b-gptq" => "JunHowie/Qwen3-32B-GPTQ-Int4".to_string(),
"deepseek-r1:8b" | "r1:8b" => "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B".to_string(),
"deepseek-r1:14b" | "r1:14b" => "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B".to_string(),
"deepseek-r1:32b" | "r1:32b" => "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B".to_string(),
"deepseek-r1:32b-gptq" => "OPEA/DeepSeek-R1-Distill-Qwen-32B-int4-gptq-sym-inc".to_string(),
"qwen2.5-coder:32b" => "Qwen/Qwen2.5-Coder-32B-Instruct".to_string(),
"qwen2.5-coder:32b-gptq" => "Qwen/Qwen2.5-Coder-32B-Instruct-GPTQ-Int4".to_string(),
"qwen2.5-coder:14b" => "Qwen/Qwen2.5-Coder-14B-Instruct".to_string(),
"gemma3:1b" => "unsloth/gemma-3-1b-it".to_string(),
"gemma3:4b" => "unsloth/gemma-3-4b-it".to_string(),
"gemma3:27b" => "unsloth/gemma-3-27b-it".to_string(),
"gemma3:27b-gptq" => "circulus/gemma-3-27b-it-gptq".to_string(),
"mistral-small:24b" | "mistral-small:3.2" => {
"mistralai/Mistral-Small-3.2-24B-Instruct-2506".to_string()
}
"devstral:24b" | "devstral:2" => "mistralai/Devstral-Small-2-24B-Instruct-2512".to_string(),
"magistral:24b" => "mistralai/Magistral-Small-2509".to_string(),
"qwen2.5:3b-gptq" | "qwen2.5-3b-instruct-gptq-int4" => {
"Qwen/Qwen2.5-3B-Instruct-GPTQ-Int4".to_string()
}
"llama3.2:1b" => "meta-llama/Llama-3.2-1B-Instruct".to_string(),
"llama3.2:3b" => "meta-llama/Llama-3.2-3B-Instruct".to_string(),
"whisper-tiny" | "whisper:tiny" => "openai/whisper-tiny".to_string(),
"whisper-base" | "whisper:base" => "openai/whisper-base".to_string(),
"whisper-small" | "whisper:small" => "openai/whisper-small".to_string(),
"whisper-medium" | "whisper:medium" => "openai/whisper-medium".to_string(),
"whisper-large-v3" | "whisper:large-v3" => "openai/whisper-large-v3".to_string(),
"whisper-turbo" | "whisper:turbo" | "whisper-large-v3-turbo" => {
"openai/whisper-large-v3-turbo".to_string()
}
"qwen3-tts" | "tts" | "tts:0.6b" => "Qwen/Qwen3-TTS-12Hz-0.6B-Base".to_string(),
"tts:1.7b" | "qwen3-tts:1.7b" => "Qwen/Qwen3-TTS-12Hz-1.7B-Base".to_string(),
_ => name.to_string(),
}
}
struct GgufAliasEntry {
aliases: &'static [&'static str],
repo: &'static str,
filename: &'static str,
tokenizer_repo: Option<&'static str>,
}
const GGUF_ALIASES: &[GgufAliasEntry] = &[
GgufAliasEntry {
aliases: &["qwen3.5:4b-gguf", "qwen3.5:4b-q4_k_m"],
repo: "unsloth/Qwen3.5-4B-GGUF",
filename: "Qwen3.5-4B-Q4_K_M.gguf",
tokenizer_repo: Some("Qwen/Qwen3.5-4B"),
},
GgufAliasEntry {
aliases: &["qwen3.5:35b-a3b-gguf", "qwen3.5:35b-a3b-q4_k_s"],
repo: "unsloth/Qwen3.5-35B-A3B-GGUF",
filename: "Qwen3.5-35B-A3B-Q4_K_S.gguf",
tokenizer_repo: Some("Qwen/Qwen3.5-35B-A3B"),
},
GgufAliasEntry {
aliases: &["qwen3:8b-q4_k_m"],
repo: "Qwen/Qwen3-8B-GGUF",
filename: "Qwen3-8B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen3:4b-q4_k_m"],
repo: "Qwen/Qwen3-4B-GGUF",
filename: "Qwen3-4B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen3:1.7b-gguf", "qwen3:1.7b-q8_0"],
repo: "Qwen/Qwen3-1.7B-GGUF",
filename: "Qwen3-1.7B-Q8_0.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen3:0.6b-gguf", "qwen3:0.6b-q8_0"],
repo: "Qwen/Qwen3-0.6B-GGUF",
filename: "Qwen3-0.6B-Q8_0.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen3-moe:30b-a3b-q4_k_m", "qwen3:30b-a3b-q4_k_m"],
repo: "Qwen/Qwen3-30B-A3B-GGUF",
filename: "Qwen3-30B-A3B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["gemma3:1b-q4_k_m"],
repo: "unsloth/gemma-3-1b-it-GGUF",
filename: "gemma-3-1b-it-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/gemma-3-1b-it"),
},
GgufAliasEntry {
aliases: &["gemma3:27b-q4_k_m"],
repo: "unsloth/gemma-3-27b-it-GGUF",
filename: "gemma-3-27b-it-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/gemma-3-27b-it"),
},
GgufAliasEntry {
aliases: &["qwen3:14b-q4_k_m"],
repo: "Qwen/Qwen3-14B-GGUF",
filename: "Qwen3-14B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen3:32b-q4_k_m"],
repo: "Qwen/Qwen3-32B-GGUF",
filename: "Qwen3-32B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen3-coder:30b-q4_k_m", "qwen3-coder:30b-a3b-q4_k_m"],
repo: "unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF",
filename: "Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["deepseek-r1:8b-q4_k_m", "r1:8b-q4_k_m"],
repo: "unsloth/DeepSeek-R1-0528-Qwen3-8B-GGUF",
filename: "DeepSeek-R1-0528-Qwen3-8B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["deepseek-r1:32b-q4_k_m", "r1:32b-q4_k_m"],
repo: "unsloth/DeepSeek-R1-Distill-Qwen-32B-GGUF",
filename: "DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf",
tokenizer_repo: None,
},
GgufAliasEntry {
aliases: &["qwen2.5-coder:32b-q4_k_m"],
repo: "bartowski/Qwen2.5-Coder-32B-Instruct-GGUF",
filename: "Qwen2.5-Coder-32B-Instruct-Q4_K_M.gguf",
tokenizer_repo: Some("Qwen/Qwen2.5-Coder-32B-Instruct"),
},
GgufAliasEntry {
aliases: &["mistral-small:24b-q4_k_m"],
repo: "bartowski/mistralai_Mistral-Small-3.2-24B-Instruct-2506-GGUF",
filename: "mistralai_Mistral-Small-3.2-24B-Instruct-2506-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/Mistral-Small-3.2-24B-Instruct-2506"),
},
GgufAliasEntry {
aliases: &["devstral:24b-q4_k_m"],
repo: "bartowski/mistralai_Devstral-Small-2-24B-Instruct-2512-GGUF",
filename: "mistralai_Devstral-Small-2-24B-Instruct-2512-Q4_K_M.gguf",
tokenizer_repo: Some("mistralai/Devstral-Small-2-24B-Instruct-2512"),
},
GgufAliasEntry {
aliases: &["magistral:24b-q4_k_m"],
repo: "bartowski/mistralai_Magistral-Small-2509-GGUF",
filename: "mistralai_Magistral-Small-2509-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/Magistral-Small-2509"),
},
GgufAliasEntry {
aliases: &["llama3.1:8b-q4_k_m"],
repo: "bartowski/Meta-Llama-3.1-8B-Instruct-GGUF",
filename: "Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/Meta-Llama-3.1-8B-Instruct"),
},
GgufAliasEntry {
aliases: &["llama3.2:3b-q4_k_m"],
repo: "bartowski/Llama-3.2-3B-Instruct-GGUF",
filename: "Llama-3.2-3B-Instruct-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/Llama-3.2-3B-Instruct"),
},
GgufAliasEntry {
aliases: &["llama3.2:1b-q4_k_m"],
repo: "bartowski/Llama-3.2-1B-Instruct-GGUF",
filename: "Llama-3.2-1B-Instruct-Q4_K_M.gguf",
tokenizer_repo: Some("unsloth/Llama-3.2-1B-Instruct"),
},
];
pub fn resolve_gguf_alias(name: &str) -> Option<(String, String)> {
if let Some(recipe) = recipes::find(name) {
return Some((
recipe.requested_model.to_owned(),
recipe.gguf_file.to_owned(),
));
}
let name = name.to_lowercase();
GGUF_ALIASES
.iter()
.find(|entry| entry.aliases.contains(&name.as_str()))
.map(|entry| (entry.repo.to_string(), entry.filename.to_string()))
}
pub fn tokenizer_sibling_repo(gguf_repo: &str) -> Option<String> {
if let Some(entry) = GGUF_ALIASES.iter().find(|entry| entry.repo == gguf_repo) {
if let Some(repo) = entry.tokenizer_repo {
return Some(repo.to_string());
}
}
gguf_repo.strip_suffix("-GGUF").map(str::to_string)
}
pub fn apply_chat_profile_env(snapshot_path: &Path) {
let entries = chat_profile_runtime_entries(
snapshot_path,
&RuntimeConfigSnapshot::capture_current(),
RuntimeConfigSource::Default,
);
crate::runtime_env::materialize_runtime_env_defaults(&entries);
}
pub fn chat_profile_runtime_entries(
snapshot_path: &Path,
current: &RuntimeConfigSnapshot,
source: RuntimeConfigSource,
) -> Vec<RuntimeConfigEntry> {
let is_gguf = snapshot_path.is_file()
&& snapshot_path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false);
let is_moe = detect_moe_arch(snapshot_path);
let model_family = detect_model_family(snapshot_path);
chat_profile_runtime_entries_for_arch(is_gguf, is_moe, model_family.as_deref(), current, source)
}
fn chat_profile_runtime_entries_for_arch(
is_gguf: bool,
is_moe: bool,
model_family: Option<&str>,
current: &RuntimeConfigSnapshot,
source: RuntimeConfigSource,
) -> Vec<RuntimeConfigEntry> {
let mut entries = Vec::new();
push_missing_entry(
&mut entries,
current,
"FERRUM_KV_CAPACITY",
if is_moe { "4096" } else { "8192" },
source,
);
let need_paged = !is_gguf
&& (is_moe
|| model_family.is_some_and(|family| {
family.eq_ignore_ascii_case("qwen3") || family.eq_ignore_ascii_case("qwen3_5")
}));
push_paged_kv_compat_entries(
&mut entries,
current,
if need_paged { "1" } else { "0" },
source,
);
if !is_gguf || is_moe {
for (k, v) in [
("FERRUM_PAGED_MAX_SEQS", if is_moe { "1" } else { "2" }),
("FERRUM_MAX_BATCH", "1"),
] {
push_missing_entry(&mut entries, current, k, v, source);
}
}
if is_moe {
for (k, v) in [
("FERRUM_MOE_BATCHED", "0"),
("FERRUM_MOE_BATCHED_DECODE", "0"),
("FERRUM_MOE_BATCH_THRESHOLD", "2"),
] {
push_missing_entry(&mut entries, current, k, v, source);
}
}
entries
}
fn push_missing_entry(
entries: &mut Vec<RuntimeConfigEntry>,
current: &RuntimeConfigSnapshot,
key: &str,
value: &str,
source: RuntimeConfigSource,
) {
if snapshot_value(current, key).is_none() {
entries.push(RuntimeConfigEntry::new(key, value, source));
}
}
fn push_paged_kv_compat_entries(
entries: &mut Vec<RuntimeConfigEntry>,
current: &RuntimeConfigSnapshot,
value: &str,
source: RuntimeConfigSource,
) {
let effective_value = snapshot_value(current, "FERRUM_PAGED_KV")
.or_else(|| snapshot_value(current, "FERRUM_METAL_PAGED_KV"))
.unwrap_or(value);
push_missing_entry(entries, current, "FERRUM_PAGED_KV", effective_value, source);
push_missing_entry(
entries,
current,
"FERRUM_METAL_PAGED_KV",
effective_value,
source,
);
}
fn snapshot_value<'a>(snapshot: &'a RuntimeConfigSnapshot, key: &str) -> Option<&'a str> {
snapshot
.entries
.iter()
.find(|entry| entry.key == key)
.map(|entry| entry.effective_value.as_str())
}
pub fn detect_moe_arch(path: &Path) -> bool {
use ferrum_quantization::gguf::GgufFile;
if path.is_file()
&& path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false)
{
return GgufFile::open(path)
.ok()
.and_then(|g| g.architecture().ok().map(|s| s.to_string()))
.map(|a| a.to_lowercase().contains("moe"))
.unwrap_or(false);
}
let config_path = path.join("config.json");
let Ok(contents) = std::fs::read_to_string(&config_path) else {
return false;
};
let Ok(json) = serde_json::from_str::<serde_json::Value>(&contents) else {
return false;
};
if let Some(archs) = json.get("architectures").and_then(|v| v.as_array()) {
if archs
.iter()
.any(|a| a.as_str().is_some_and(|s| s.to_lowercase().contains("moe")))
{
return true;
}
}
json.get("model_type")
.and_then(|v| v.as_str())
.is_some_and(|mt| mt.to_lowercase().contains("moe"))
}
pub fn detect_model_family(path: &Path) -> Option<String> {
use ferrum_quantization::gguf::GgufFile;
if path.is_file()
&& path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false)
{
return GgufFile::open(path)
.ok()
.and_then(|g| g.architecture().ok().map(|s| normalize_model_family(s)));
}
let config_path = path.join("config.json");
let contents = std::fs::read_to_string(&config_path).ok()?;
let json = serde_json::from_str::<serde_json::Value>(&contents).ok()?;
if let Some(model_type) = json.get("model_type").and_then(|v| v.as_str()) {
return Some(normalize_model_family(model_type));
}
json.get("architectures")
.and_then(|v| v.as_array())
.and_then(|archs| archs.iter().find_map(|arch| arch.as_str()))
.map(normalize_model_family)
}
fn normalize_model_family(raw: &str) -> String {
let lower = raw.to_ascii_lowercase();
if lower.contains("qwen3_5_moe")
|| lower.contains("qwen3_5moe")
|| lower.contains("qwen35_moe")
|| lower.contains("qwen35moe")
{
"qwen3_5_moe".to_string()
} else if lower.contains("qwen3_5") || lower.contains("qwen35") {
"qwen3_5".to_string()
} else if lower.contains("qwen3_moe")
|| lower.contains("qwen3moe")
|| lower.contains("qwen3_mo")
{
"qwen3_moe".to_string()
} else if lower.contains("qwen3") {
"qwen3".to_string()
} else if lower.contains("qwen2") || lower == "qwen" {
"qwen2".to_string()
} else if lower.contains("mistral") {
"mistral".to_string()
} else if lower.contains("llama") || lower.contains("tinyllama") {
"llama".to_string()
} else {
lower
}
}
pub fn metal_gguf_moe_correctness_entries(
snapshot_path: &Path,
device: &ferrum_types::Device,
current: &RuntimeConfigSnapshot,
source: RuntimeConfigSource,
) -> Vec<RuntimeConfigEntry> {
let is_gguf = snapshot_path.is_file()
&& snapshot_path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false);
if !is_gguf || !device_is_metal(device) || !detect_moe_arch(snapshot_path) {
return Vec::new();
}
let mut entries = Vec::new();
push_missing_entry(&mut entries, current, "FERRUM_MOE_HOST_TOPK", "1", source);
entries
}
pub fn serve_profile_runtime_entries(
snapshot_path: &Path,
device: &ferrum_types::Device,
vnext_plan_owns_context_capacity: bool,
current: &RuntimeConfigSnapshot,
source: RuntimeConfigSource,
) -> Vec<RuntimeConfigEntry> {
let is_gguf = snapshot_path.is_file()
&& snapshot_path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false);
serve_profile_runtime_entries_for_arch(
is_gguf,
detect_moe_arch(snapshot_path),
device_is_metal(device),
!matches!(device, ferrum_types::Device::CPU),
vnext_plan_owns_context_capacity,
current,
source,
)
}
fn device_is_metal(device: &ferrum_types::Device) -> bool {
#[cfg(all(any(target_os = "macos", target_os = "ios"), feature = "metal"))]
{
matches!(device, ferrum_types::Device::Metal)
}
#[cfg(not(all(any(target_os = "macos", target_os = "ios"), feature = "metal")))]
{
let _ = device;
false
}
}
pub fn serve_profile_runtime_entries_for_arch(
is_gguf: bool,
is_moe: bool,
is_metal: bool,
supports_paged_kv: bool,
vnext_plan_owns_context_capacity: bool,
current: &RuntimeConfigSnapshot,
source: RuntimeConfigSource,
) -> Vec<RuntimeConfigEntry> {
if !is_gguf {
return Vec::new();
}
let mut entries = Vec::new();
if !vnext_plan_owns_context_capacity {
let kv_capacity = if is_moe && is_metal {
"1024"
} else if is_moe {
"2048"
} else {
"512"
};
push_missing_entry(
&mut entries,
current,
"FERRUM_KV_CAPACITY",
kv_capacity,
source,
);
}
if !supports_paged_kv {
push_paged_kv_compat_entries(&mut entries, current, "0", source);
return entries;
}
push_paged_kv_compat_entries(&mut entries, current, "1", source);
for (k, v) in [
(
"FERRUM_PAGED_MAX_SEQS",
if is_moe {
"16"
} else if is_metal {
"16"
} else {
"32"
},
),
("FERRUM_MAX_BATCH", "16"),
] {
push_missing_entry(&mut entries, current, k, v, source);
}
if is_moe {
for (k, v) in [
("FERRUM_MAX_BATCHED_TOKENS", "2048"),
("FERRUM_MOE_BATCHED", "1"),
("FERRUM_MOE_BATCHED_DECODE", "1"),
("FERRUM_MOE_BATCH_THRESHOLD", "2"),
] {
push_missing_entry(&mut entries, current, k, v, source);
}
}
entries
}
pub fn load_model_chat_template(snapshot_path: &Path) -> Option<ModelChatTemplate> {
let is_gguf = snapshot_path.is_file()
&& snapshot_path
.extension()
.map(|e| e.eq_ignore_ascii_case("gguf"))
.unwrap_or(false);
if is_gguf {
let gguf = ferrum_quantization::gguf::GgufFile::open(snapshot_path).ok()?;
let template = gguf.metadata_string("tokenizer.chat_template").ok()?;
return Some(ModelChatTemplate::new(
template.to_string(),
format!("{}:tokenizer.chat_template", snapshot_path.display()),
));
}
if !snapshot_path.is_dir() {
return None;
}
let jinja_path = snapshot_path.join("chat_template.jinja");
if let Ok(template) = std::fs::read_to_string(&jinja_path) {
if !template.trim().is_empty() {
return Some(ModelChatTemplate::new(
template,
jinja_path.display().to_string(),
));
}
}
let json_path = snapshot_path.join("chat_template.json");
if let Some(template) = read_template_json(&json_path) {
return Some(template);
}
let tokenizer_config_path = snapshot_path.join("tokenizer_config.json");
read_tokenizer_config_template(&tokenizer_config_path)
}
pub fn load_product_chat_template(
sources: &ProductionModelSourceBundle,
) -> Option<ModelChatTemplate> {
if let Some(bytes) = sources.chat_template_jinja() {
let template = std::str::from_utf8(bytes).ok()?;
if !template.trim().is_empty() {
return Some(ModelChatTemplate::new(
template.to_owned(),
sources
.tokenizer_root()
.join("chat_template.jinja")
.display()
.to_string(),
));
}
}
if let Some(bytes) = sources.chat_template_json() {
let origin = sources
.tokenizer_root()
.join("chat_template.json")
.display()
.to_string();
if let Some(template) = template_json_bytes(bytes, origin) {
return Some(template);
}
}
sources.tokenizer_config_json().and_then(|bytes| {
tokenizer_config_template_bytes(
bytes,
sources
.tokenizer_root()
.join("tokenizer_config.json")
.display()
.to_string(),
)
})
}
fn load_product_chat_template_source(
sources: &ProductionModelSourceBundle,
source_file: &str,
) -> Option<ModelChatTemplate> {
let origin = sources.tokenizer_root().join(source_file);
match source_file {
"tokenizer_config.json" => sources
.tokenizer_config_json()
.and_then(|bytes| tokenizer_config_template_bytes(bytes, origin.display().to_string())),
"chat_template.json" => sources
.chat_template_json()
.and_then(|bytes| template_json_bytes(bytes, origin.display().to_string())),
"chat_template.jinja" => sources.chat_template_jinja().and_then(|bytes| {
let template = std::str::from_utf8(bytes).ok()?;
(!template.trim().is_empty())
.then(|| ModelChatTemplate::new(template.to_owned(), origin.display().to_string()))
}),
_ => None,
}
}
pub fn load_defined_product_chat_template(
prepared: &ferrum_models::vnext::DefinedProductionModel,
) -> Result<ModelChatTemplate> {
let metadata = &prepared.definition().metadata().template;
let mut selected = load_product_chat_template_source(prepared.sources(), &metadata.source_file)
.ok_or_else(|| {
FerrumError::model(format!(
"typed product chat template source is unavailable: {}",
metadata.source_file
))
})?;
if selected.template != metadata.template {
return Err(FerrumError::model(
"typed product chat template bytes differ from the prepared family",
));
}
selected.set_output_protocol(prepared.descriptor().output_protocol());
selected.reasoning_effort_support = prepared.descriptor().reasoning_effort_support().clone();
Ok(selected)
}
fn read_template_json(path: &Path) -> Option<ModelChatTemplate> {
let bytes = std::fs::read(path).ok()?;
template_json_bytes(&bytes, path.display().to_string())
}
fn template_json_bytes(bytes: &[u8], origin: String) -> Option<ModelChatTemplate> {
if bytes.iter().all(u8::is_ascii_whitespace) {
return None;
}
match serde_json::from_slice::<serde_json::Value>(bytes).ok() {
Some(serde_json::Value::String(template)) => Some(ModelChatTemplate::new(template, origin)),
Some(value) => template_value(&value).map(|template| {
let mut t = ModelChatTemplate::new(template, origin);
t.bos_token = token_value(&value, "bos_token");
t.eos_token = token_value(&value, "eos_token");
t
}),
None => std::str::from_utf8(bytes)
.ok()
.map(|text| ModelChatTemplate::new(text.to_owned(), origin)),
}
}
fn read_tokenizer_config_template(path: &Path) -> Option<ModelChatTemplate> {
let bytes = std::fs::read(path).ok()?;
tokenizer_config_template_bytes(&bytes, path.display().to_string())
}
fn tokenizer_config_template_bytes(bytes: &[u8], origin: String) -> Option<ModelChatTemplate> {
let value = serde_json::from_slice::<serde_json::Value>(bytes).ok()?;
let template = template_value(&value)?;
let mut t = ModelChatTemplate::new(template, origin);
t.bos_token = token_value(&value, "bos_token");
t.eos_token = token_value(&value, "eos_token");
Some(t)
}
fn template_value(value: &serde_json::Value) -> Option<String> {
match value.get("chat_template")? {
serde_json::Value::String(s) => Some(s.clone()),
serde_json::Value::Array(items) => items
.iter()
.find(|item| item.get("name").and_then(|v| v.as_str()) == Some("default"))
.or_else(|| items.first())
.and_then(|item| item.get("template").and_then(|v| v.as_str()))
.map(ToString::to_string),
serde_json::Value::Object(obj) => obj
.get("template")
.and_then(|v| v.as_str())
.map(ToString::to_string),
_ => None,
}
}
fn token_value(value: &serde_json::Value, key: &str) -> Option<String> {
match value.get(key)? {
serde_json::Value::String(s) => Some(s.clone()),
serde_json::Value::Object(obj) => obj
.get("content")
.and_then(|v| v.as_str())
.map(ToString::to_string),
_ => None,
}
}
#[derive(Debug, PartialEq, Eq)]
struct PinnedHfRepository<'a> {
repo_id: &'a str,
revision: String,
}
fn parse_pinned_hf_repository(model: &str) -> Result<Option<PinnedHfRepository<'_>>> {
let Some((repo_id, revision)) = model.split_once('@') else {
return Ok(None);
};
if resolve_gguf_alias(repo_id).is_some() {
return Err(FerrumError::unsupported(
"GGUF alias@commit is not supported: its weight and metadata repositories require independent revisions; use explicit local sources",
));
}
let valid_component = |part: &str| {
!part.is_empty()
&& !part.starts_with(['.', '-'])
&& !part.ends_with(['.', '-'])
&& !part.contains("..")
&& !part.contains("--")
&& part
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || b"._-".contains(&byte))
};
let valid_repo = repo_id
.split_once('/')
.is_some_and(|(owner, name)| valid_component(owner) && valid_component(name));
if !valid_repo || revision.len() != 40 || !revision.bytes().all(|b| b.is_ascii_hexdigit()) {
return Err(FerrumError::config(
"pinned model must be HF_REPO@<40-hex-commit> with an explicit owner/repository; aliases, tags and branch names are not supported",
));
}
Ok(Some(PinnedHfRepository {
repo_id,
revision: revision.to_ascii_lowercase(),
}))
}
impl PinnedHfRepository<'_> {
fn cached(
&self,
cache_dir: &Path,
requested_model: &str,
requirements: CacheRequirements,
) -> Result<CachedModel> {
let local_path = cache_dir
.join("hub")
.join(format!("models--{}", self.repo_id.replace('/', "--")))
.join("snapshots")
.join(&self.revision);
cache::inspect_snapshot(&local_path, requested_model, requirements)
}
fn verify_snapshot(&self, path: &Path) -> Result<()> {
let identity = huggingface_snapshot_identity(path);
if !identity.is_some_and(|identity| {
identity.repository_id == self.repo_id
&& identity.revision.eq_ignore_ascii_case(&self.revision)
}) {
return Err(FerrumError::model(format!(
"resolved snapshot does not match requested repository {} at commit {}: {}",
self.repo_id,
self.revision,
path.display(),
)));
}
Ok(())
}
}
pub fn find_cached_model(cache_dir: &Path, model_id: &str) -> Option<ResolvedModelSource> {
cache::inspect_cached_model(cache_dir, model_id, CacheRequirements::Product)
.ok()?
.into_source()
}
pub fn find_cached_gguf(cache_dir: &Path, repo: &str, filename: &str) -> Option<PathBuf> {
let repo_dir = cache_dir
.join("hub")
.join(format!("models--{}", repo.replace('/', "--")));
cache::snapshot_candidates(&repo_dir)
.ok()?
.into_iter()
.map(|snapshot| snapshot.join(filename))
.find(|candidate| {
matches!(
ferrum_models::source::inspect_cached_weights(candidate),
Ok(ferrum_models::source::CachedWeights::Ready(
ModelFormat::GGUF
))
)
})
}
const PRODUCT_SOURCE_FILES: [&str; 7] = [
"config.json",
"tokenizer.json",
"tokenizer_config.json",
"special_tokens_map.json",
"chat_template.json",
"chat_template.jinja",
"generation_config.json",
];
fn is_complete_product_metadata_snapshot(path: &Path) -> Result<bool> {
let missing =
ferrum_models::hf_download::inspect_cached_metadata_selection(path, &PRODUCT_SOURCE_FILES)?;
for filename in ["config.json", "tokenizer.json"] {
let file = path.join(filename);
match std::fs::metadata(&file) {
Ok(metadata) if metadata.is_file() && metadata.len() > 0 => {}
Ok(metadata) if !metadata.is_file() => {
return Err(FerrumError::model(format!(
"Cached model metadata {} is not a file",
file.display()
)));
}
Ok(_) => return Ok(false),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),
Err(error) => return Err(error.into()),
}
}
Ok(missing.is_none())
}
fn find_cached_product_metadata(cache_dir: &Path, repo: &str) -> Result<Option<PathBuf>> {
let repo_dir = cache_dir
.join("hub")
.join(format!("models--{}", repo.replace('/', "--")));
let mut invalid = None;
for candidate in cache::snapshot_candidates(&repo_dir)? {
match is_complete_product_metadata_snapshot(&candidate) {
Ok(true) => return Ok(Some(candidate)),
Ok(false) => {}
Err(error) => {
if invalid.is_none() {
invalid = Some(error);
}
}
}
}
match invalid {
Some(error) => Err(error),
None => Ok(None),
}
}
fn repository_source(repo: impl Into<String>) -> OriginalModelSource {
OriginalModelSource {
kind: ModelSourceKind::Repository,
location: repo.into(),
requested_revision: None,
}
}
fn original_product_source(
source: &ModelSource,
resolved_path: &Path,
) -> Result<OriginalModelSource> {
match source {
ModelSource::Local(location) => Ok(OriginalModelSource {
kind: if Path::new(location).is_dir() {
ModelSourceKind::LocalDirectory
} else if resolved_path.is_file() {
ModelSourceKind::LocalFile
} else {
ModelSourceKind::LocalDirectory
},
location: location.clone(),
requested_revision: None,
}),
ModelSource::HuggingFace {
repo_id, revision, ..
} => Ok(OriginalModelSource {
kind: ModelSourceKind::Repository,
location: repo_id.clone(),
requested_revision: revision.clone(),
}),
ModelSource::Url { .. } | ModelSource::S3 { .. } => Err(FerrumError::unsupported(
"typed product source bundles do not yet resolve URL or S3 sources",
)),
}
}
fn gguf_metadata_root(path: &Path) -> &Path {
if let Some(identity) = huggingface_snapshot_identity(path) {
if let Some(root) = path.ancestors().find(|root| {
root.file_name().and_then(|name| name.to_str()) == Some(identity.revision.as_str())
&& root
.parent()
.and_then(Path::file_name)
.and_then(|name| name.to_str())
== Some("snapshots")
}) {
return root;
}
}
path.parent()
.filter(|parent| !parent.as_os_str().is_empty())
.unwrap_or_else(|| Path::new("."))
}
fn open_colocated_product_sources(
source: &ResolvedModelSource,
original_source: &ModelSource,
) -> Result<Option<Arc<ProductionModelSourceBundle>>> {
let (metadata_root, weights, original_sources) = match source.format {
ModelFormat::SafeTensors if is_complete_product_metadata_snapshot(&source.local_path)? => {
let original = original_product_source(original_source, &source.local_path)?;
(
source.local_path.as_path(),
ProductionWeightArtifact::safetensors_directory(&source.local_path),
OriginalModelSources {
semantic: original.clone(),
tokenizer: original.clone(),
weights: original,
},
)
}
ModelFormat::GGUF => {
let metadata_root = gguf_metadata_root(&source.local_path);
if !is_complete_product_metadata_snapshot(metadata_root)? {
return Ok(None);
}
let weight_original = original_product_source(original_source, &source.local_path)?;
let metadata_original = if matches!(original_source, ModelSource::HuggingFace { .. }) {
weight_original.clone()
} else {
OriginalModelSource {
kind: ModelSourceKind::LocalDirectory,
location: metadata_root.display().to_string(),
requested_revision: None,
}
};
(
metadata_root,
ProductionWeightArtifact::gguf_file(&source.local_path),
OriginalModelSources {
semantic: metadata_original.clone(),
tokenizer: metadata_original,
weights: weight_original,
},
)
}
_ => return Ok(None),
};
open_registered_product_sources(metadata_root, metadata_root, weights, original_sources)
.map(Arc::new)
.map(Some)
}
fn direct_gguf_requires_typed_product_sources(path: &Path) -> Result<bool> {
let metadata = gguf_repository::read_metadata(path)?;
Ok(
ferrum_models::vnext::gguf_architecture_requires_typed_product_sources(
&metadata.architecture,
),
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DownloadPolicy {
AutoDownload,
NoDownload,
}
#[derive(Debug, Clone, Copy)]
enum DownloadArtifacts {
Repository,
RootSafetensors,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ProductSourceComposition<'a> {
ResolveColocated,
ResolveWithExplicitTokenizer(&'a Path),
DeferUntilExplicitSemantic,
}
pub struct Resolved {
source: ResolvedModelSource,
requested_model: String,
public_model_id: String,
original_source: ModelSource,
model_sources: Option<Arc<ProductionModelSourceBundle>>,
autosized: bool,
}
pub struct ProductEngineInput {
pub source: ResolvedModelSource,
pub requested_model: String,
pub public_model_id: String,
pub engine_config: EngineConfig,
pub model_sources: Option<Arc<ProductionModelSourceBundle>>,
pub autosized: bool,
}
pub fn define_registered_product_model(
sources: Option<&Arc<ProductionModelSourceBundle>>,
policy: &ferrum_types::NumericalExecutionPolicy,
kv_dtype: ferrum_types::KvCacheDtype,
) -> Result<Option<Arc<ferrum_models::vnext::DefinedProductionModel>>> {
if let Some(sources) = sources {
if let ferrum_models::vnext::ProductionModelRegistration::Registered(registration) =
ferrum_models::vnext::resolve_registered_model_from_sources(sources)?
{
let defined = registration.define_from_sources(Arc::clone(sources))?;
defined
.definition()
.numerical_profiles()
.candidates(
policy,
ferrum_types::KvStorageFormat::try_from(kv_dtype)
.map_err(FerrumError::config)?,
)
.map_err(|error| FerrumError::config(error.to_string()))?;
return Ok(Some(Arc::new(defined)));
}
}
if let ferrum_types::NumericalExecutionPolicy::Require(profile) = policy {
return Err(FerrumError::unsupported(format!(
"numerical profile {profile} requires a registered vNext model package"
)));
}
Ok(None)
}
pub fn product_source_identity(
prepared: Option<&ferrum_models::vnext::DefinedProductionModel>,
sources: Option<&ProductionModelSourceBundle>,
requested_model: &str,
resolved_model: &str,
selected_template: Option<&ModelChatTemplate>,
) -> Result<Option<ProductModelSourceIdentity>> {
if let Some(prepared) = prepared {
return defined_product_source_identity(
prepared,
requested_model,
resolved_model,
selected_template,
)
.map(Some);
}
let (Some(sources), Some(selected)) = (sources, selected_template) else {
return Ok(None);
};
let source_file = Path::new(&selected.source)
.file_name()
.and_then(|value| value.to_str())
.ok_or_else(|| FerrumError::model("selected chat template has no source filename"))?;
let retained = load_product_chat_template_source(sources, source_file).ok_or_else(|| {
FerrumError::model("selected chat template is absent from the product source lease")
})?;
if selected.source != retained.source || selected.template != retained.template {
return Err(FerrumError::model(
"selected runtime chat template differs from the retained product source",
));
}
sources
.product_source_identity(
requested_model,
resolved_model,
source_file,
&selected.template,
)
.map(Some)
}
pub fn defined_product_source_identity(
prepared: &ferrum_models::vnext::DefinedProductionModel,
requested_model: &str,
resolved_model: &str,
selected_template: Option<&ModelChatTemplate>,
) -> Result<ProductModelSourceIdentity> {
let identity = prepared.product_source_identity(requested_model, resolved_model)?;
let selected_template = selected_template.ok_or_else(|| {
FerrumError::model("typed product model has no selected runtime chat template")
})?;
let selected_sha256 = format!(
"{:x}",
Sha256::digest(selected_template.template.as_bytes())
);
if identity.template.content_sha256.as_deref() != Some(selected_sha256.as_str()) {
return Err(FerrumError::model(
"selected runtime chat template differs from the prepared typed family",
));
}
let selected_file = Path::new(&selected_template.source)
.file_name()
.and_then(|value| value.to_str());
if selected_file != Some(identity.template.source_file.as_str()) {
return Err(FerrumError::model(format!(
"selected runtime chat template source differs from typed identity: {}",
selected_template.source
)));
}
Ok(identity)
}
impl Resolved {
pub fn local_path(&self) -> &Path {
&self.source.local_path
}
pub fn into_product_engine_input(self) -> ProductEngineInput {
let mut engine_config = EngineConfig::default();
engine_config.model.model_id = ModelId::new(self.public_model_id.clone());
engine_config.model.source = Some(self.original_source);
ProductEngineInput {
source: self.source,
requested_model: self.requested_model,
public_model_id: self.public_model_id,
engine_config,
model_sources: self.model_sources,
autosized: self.autosized,
}
}
}
pub async fn resolve_model_source(
model: &str,
cache_dir: &Path,
download: DownloadPolicy,
autosize: Option<(AutoSizeProfile, f32)>,
) -> Result<Resolved> {
if recipes::find(model).is_some() {
return resolve_model_source_with_product_sources(
model,
cache_dir,
download,
autosize,
&ProductSourceArgs::default(),
)
.await;
}
resolve_model_source_internal(
model,
cache_dir,
download,
autosize,
ProductSourceComposition::ResolveColocated,
DownloadArtifacts::Repository,
)
.await
}
async fn resolve_model_source_internal(
model: &str,
cache_dir: &Path,
download: DownloadPolicy,
autosize: Option<(AutoSizeProfile, f32)>,
source_composition: ProductSourceComposition<'_>,
download_artifacts: DownloadArtifacts,
) -> Result<Resolved> {
let defer_colocated_product_sources =
source_composition == ProductSourceComposition::DeferUntilExplicitSemantic;
let defer_repository_product_sources =
source_composition != ProductSourceComposition::ResolveColocated;
let cache_requirements = match source_composition {
ProductSourceComposition::DeferUntilExplicitSemantic => CacheRequirements::WeightsOnly,
ProductSourceComposition::ResolveWithExplicitTokenizer(_) => {
CacheRequirements::ExplicitTokenizer
}
ProductSourceComposition::ResolveColocated => match download_artifacts {
DownloadArtifacts::RootSafetensors => CacheRequirements::Product,
DownloadArtifacts::Repository => CacheRequirements::Repository,
},
};
let tokenizer_override = match source_composition {
ProductSourceComposition::ResolveWithExplicitTokenizer(path) => Some(path),
_ => None,
};
if let Some((repo, filename)) = resolve_gguf_alias(model) {
let token = (download == DownloadPolicy::AutoDownload)
.then(|| {
std::env::var("HF_TOKEN")
.or_else(|_| std::env::var("HUGGING_FACE_HUB_TOKEN"))
.ok()
})
.flatten();
let (local_path, weights_from_cache) = match find_cached_gguf(cache_dir, &repo, &filename) {
Some(path) => (path, true),
None if download == DownloadPolicy::AutoDownload => {
let downloader =
ferrum_models::HfDownloader::new(cache_dir.to_path_buf(), token.clone())?;
(
downloader.download_gguf(&repo, None, &filename).await?,
false,
)
}
None => {
return Err(FerrumError::model(format!(
"GGUF alias '{model}' is not cached and DownloadPolicy::NoDownload is set"
)))
}
};
let (model_sources, metadata_from_cache) = if defer_colocated_product_sources {
(None, true)
} else {
gguf_repository::resolve_metadata(
model,
repository_source(&repo),
&local_path,
cache_dir,
download,
tokenizer_override,
)
.await?
};
return Ok(finalize_resolution(
ResolvedModelSource {
original: model.to_string(),
local_path,
format: ModelFormat::GGUF,
from_cache: weights_from_cache && metadata_from_cache,
},
ModelSource::HuggingFace {
repo_id: repo,
revision: None,
cache_dir: Some(cache_dir.display().to_string()),
},
model_sources,
autosize,
));
}
let direct = PathBuf::from(model);
let local_gguf = if looks_like_gguf_path(model) {
Some(direct.clone())
} else if direct.is_dir() && detect_format(&direct) == ModelFormat::Unknown {
match cache::inspect_snapshot(&direct, model, cache_requirements)? {
CachedModel::Ready(source) if source.format == ModelFormat::GGUF => {
Some(source.local_path)
}
_ => None,
}
} else {
None
};
if let Some(local_path) = local_gguf {
let source = ResolvedModelSource {
original: model.to_string(),
local_path,
format: ModelFormat::GGUF,
from_cache: false,
};
let original_source = ModelSource::Local(model.to_owned());
let model_sources = if defer_repository_product_sources {
None
} else {
open_colocated_product_sources(&source, &original_source)?
};
if !defer_repository_product_sources
&& model_sources.is_none()
&& direct_gguf_requires_typed_product_sources(&source.local_path)?
{
return Err(FerrumError::unsupported(format!(
"GGUF architecture in '{}' has migrated to the typed vNext product runtime; use a curated GGUF alias or place config.json and tokenizer.json beside the file",
source.local_path.display()
)));
}
return Ok(finalize_resolution(
source,
original_source,
model_sources,
autosize,
));
}
if direct.is_dir() {
let format = detect_format(&direct);
if format != ModelFormat::Unknown {
let source = ResolvedModelSource {
original: model.to_string(),
local_path: direct,
format,
from_cache: false,
};
let original_source = ModelSource::Local(model.to_owned());
let model_sources = if defer_repository_product_sources {
None
} else {
open_colocated_product_sources(&source, &original_source)?
};
return Ok(finalize_resolution(
source,
original_source,
model_sources,
autosize,
));
}
return Err(FerrumError::model(format!(
"local model directory '{}' has no supported weights",
direct.display()
)));
}
if let Some((repository, quant)) = gguf_repository::selection::quantized_repository(model)? {
let mut resolved = gguf_repository::selection::resolve_quantization(
&repository,
quant,
cache_dir,
download,
autosize,
source_composition,
)
.await?;
resolved.requested_model = model.to_owned();
return Ok(resolved);
}
let pinned = parse_pinned_hf_repository(model)?;
let model_id = pinned
.as_ref()
.map(|pin| pin.repo_id.to_owned())
.unwrap_or_else(|| resolve_model_alias(model));
let revision = pinned.as_ref().map(|pin| pin.revision.as_str());
let cached = match &pinned {
Some(pin) => pin.cached(cache_dir, model, cache_requirements)?,
None => cache::inspect_cached_model(cache_dir, &model_id, cache_requirements)?,
};
let (cached, incomplete_reason) = match cached {
CachedModel::Ready(source) => (Some(source), None),
CachedModel::Missing(reason) => (None, reason),
};
if let Some(mut source) = cached {
if let Some(pin) = &pinned {
pin.verify_snapshot(&source.local_path)?;
}
let original_source = ModelSource::HuggingFace {
repo_id: model_id,
revision: revision.map(str::to_owned),
cache_dir: Some(cache_dir.display().to_string()),
};
let model_sources = gguf_repository::compose_repository(
&mut source,
&original_source,
model,
cache_dir,
download,
source_composition,
)
.await?;
return Ok(finalize_resolution(
source,
original_source,
model_sources,
autosize,
));
}
if download != DownloadPolicy::AutoDownload {
if let Some(reason) = incomplete_reason {
return Err(FerrumError::model(format!(
"model '{}' has an incomplete cache and DownloadPolicy::NoDownload is set: {reason}",
if pinned.is_some() { model } else { &model_id }
)));
}
return Err(FerrumError::model(format!(
"model '{}' not found locally and DownloadPolicy::NoDownload set",
if pinned.is_some() { model } else { &model_id }
)));
}
if let Some(reason) = incomplete_reason {
eprintln!("Cached model needs download recovery: {reason}");
}
let token = std::env::var("HF_TOKEN")
.or_else(|_| std::env::var("HUGGING_FACE_HUB_TOKEN"))
.ok();
let downloader = ferrum_models::HfDownloader::new(cache_dir.to_path_buf(), token)?;
let snapshot_path = match download_artifacts {
DownloadArtifacts::Repository => downloader.download(&model_id, revision).await?,
DownloadArtifacts::RootSafetensors => {
downloader.download_safetensors(&model_id, revision).await?
}
};
if let Some(pin) = &pinned {
pin.verify_snapshot(&snapshot_path)?;
}
let mut source = match cache::inspect_snapshot(
&snapshot_path,
if pinned.is_some() { model } else { &model_id },
cache_requirements,
)? {
CachedModel::Ready(source) => source,
CachedModel::Missing(reason) => {
return Err(FerrumError::model(format!(
"downloaded model is incomplete: {}",
reason.as_deref().unwrap_or("no supported weights found")
)));
}
};
source.from_cache = false;
let original_source = ModelSource::HuggingFace {
repo_id: model_id,
revision: revision.map(str::to_owned),
cache_dir: Some(cache_dir.display().to_string()),
};
let model_sources = gguf_repository::compose_repository(
&mut source,
&original_source,
model,
cache_dir,
download,
source_composition,
)
.await?;
Ok(finalize_resolution(
source,
original_source,
model_sources,
autosize,
))
}
pub async fn resolve_model_source_with_product_sources(
model: &str,
cache_dir: &Path,
download: DownloadPolicy,
autosize: Option<(AutoSizeProfile, f32)>,
source_args: &ProductSourceArgs,
) -> Result<Resolved> {
let requested_model = model;
let recipe = recipes::find(model);
let recipe_args = recipe.map(|recipe| recipe.source_args(source_args));
let source_args = recipe_args.as_ref().unwrap_or(source_args);
let model = recipe.map_or(model, |recipe| recipe.requested_model);
let selected = product_source_selection::resolve(source_args, cache_dir, download).await?;
let source_args = &selected.arguments;
if let Some(filename) = &source_args.gguf_file {
let mut resolved = gguf_repository::selection::resolve(
model,
filename,
cache_dir,
download,
autosize,
source_args,
)
.await?;
resolved.requested_model = requested_model.to_owned();
if recipe.is_some() {
resolved.source.original = requested_model.to_owned();
}
return apply_explicit_product_sources(resolved, &selected);
}
let mut resolved = resolve_model_source_internal(
model,
cache_dir,
download,
autosize,
if source_args.semantic_source.is_some() {
ProductSourceComposition::DeferUntilExplicitSemantic
} else if let Some(tokenizer) = &source_args.tokenizer_source {
ProductSourceComposition::ResolveWithExplicitTokenizer(tokenizer)
} else {
ProductSourceComposition::ResolveColocated
},
DownloadArtifacts::RootSafetensors,
)
.await?;
resolved.requested_model = model.to_owned();
apply_explicit_product_sources(resolved, &selected)
}
fn apply_explicit_product_sources(
mut resolved: Resolved,
selected: &product_source_selection::SelectedSources,
) -> Result<Resolved> {
let source_args = &selected.arguments;
if source_args.semantic_source.is_none() && source_args.tokenizer_source.is_none() {
return Ok(resolved);
}
let existing = resolved.model_sources.as_deref();
let weight_root = match resolved.source.format {
ModelFormat::GGUF => resolved
.source
.local_path
.parent()
.unwrap_or_else(|| Path::new(".")),
_ => resolved.source.local_path.as_path(),
};
let semantic_root = source_args
.semantic_source
.as_deref()
.or_else(|| existing.map(ProductionModelSourceBundle::semantic_root))
.unwrap_or(weight_root);
let tokenizer_root = source_args
.tokenizer_source
.as_deref()
.or_else(|| source_args.semantic_source.as_ref().map(|_| semantic_root))
.or_else(|| existing.map(ProductionModelSourceBundle::tokenizer_root))
.unwrap_or(semantic_root);
let weights = existing
.map(|sources| sources.weights().clone())
.unwrap_or_else(|| match resolved.source.format {
ModelFormat::GGUF => ProductionWeightArtifact::gguf_file(&resolved.source.local_path),
_ => ProductionWeightArtifact::safetensors_directory(&resolved.source.local_path),
});
let explicit_original = |path: &Path| OriginalModelSource {
kind: if path.is_file() {
ModelSourceKind::LocalFile
} else {
ModelSourceKind::LocalDirectory
},
location: path.display().to_string(),
requested_revision: None,
};
let semantic_original = match selected
.semantic_original
.clone()
.or_else(|| existing.map(|sources| sources.original_sources().semantic.clone()))
{
Some(original) => original,
None if matches!(&resolved.original_source, ModelSource::HuggingFace { .. }) => {
original_product_source(&resolved.original_source, semantic_root)?
}
None => explicit_original(semantic_root),
};
let tokenizer_original = selected
.tokenizer_original
.clone()
.or_else(|| {
source_args
.semantic_source
.as_ref()
.map(|_| semantic_original.clone())
})
.or_else(|| existing.map(|sources| sources.original_sources().tokenizer.clone()))
.unwrap_or_else(|| explicit_original(tokenizer_root));
let weight_original = existing
.map(|sources| sources.original_sources().weights.clone())
.unwrap_or_else(|| {
original_product_source(&resolved.original_source, &resolved.source.local_path)
.unwrap_or_else(|_| explicit_original(weights.path()))
});
resolved.model_sources = Some(Arc::new(open_registered_product_sources(
semantic_root,
tokenizer_root,
weights,
OriginalModelSources {
semantic: semantic_original,
tokenizer: tokenizer_original,
weights: weight_original,
},
)?));
resolved.source.from_cache &= !selected.downloaded;
Ok(resolved)
}
fn finalize_resolution(
source: ResolvedModelSource,
original_source: ModelSource,
model_sources: Option<Arc<ProductionModelSourceBundle>>,
autosize: Option<(AutoSizeProfile, f32)>,
) -> Resolved {
let autosized = if let Some((profile, gpu_util)) = autosize {
apply_auto_size_with_profile(&source.local_path, gpu_util, profile);
if profile == AutoSizeProfile::Chat {
apply_chat_profile_env(&source.local_path);
}
true
} else {
false
};
let requested_model = source.original.clone();
let public_model_id = public_model_id(&source);
Resolved {
source,
requested_model,
public_model_id,
original_source,
model_sources,
autosized,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{SystemTime, UNIX_EPOCH};
fn temp_model_dir(name: &str, config_json: &str) -> PathBuf {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!(
"ferrum-source-resolver-{name}-{}-{nonce}",
std::process::id()
));
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("config.json"), config_json).unwrap();
std::fs::write(dir.join("tokenizer.json"), br#"{"version":"1.0"}"#).unwrap();
dir
}
pub(super) fn qwen35_semantic_config(moe: bool) -> String {
let mut text = serde_json::json!({
"model_type": if moe { "qwen3_5_moe_text" } else { "qwen3_5_text" },
"hidden_size": 16,
"num_hidden_layers": 2,
"layer_types": ["linear_attention", "full_attention"],
"linear_num_key_heads": 1,
"linear_num_value_heads": 1,
"linear_key_head_dim": 4,
"linear_value_head_dim": 4,
"linear_conv_kernel_dim": 2,
"mamba_ssm_dtype": "float32",
"head_dim": 4,
"num_attention_heads": 1,
"num_key_value_heads": 1,
"max_position_embeddings": 128,
"vocab_size": 32,
"rms_norm_eps": 0.000001,
"rope_parameters": {
"rope_theta": 10000.0,
"partial_rotary_factor": 1.0,
"mrope_interleaved": false
}
});
let text = text.as_object_mut().unwrap();
if moe {
text.insert("num_experts".to_owned(), serde_json::json!(4));
text.insert("num_experts_per_tok".to_owned(), serde_json::json!(2));
text.insert("moe_intermediate_size".to_owned(), serde_json::json!(8));
text.insert(
"shared_expert_intermediate_size".to_owned(),
serde_json::json!(8),
);
} else {
text.insert("intermediate_size".to_owned(), serde_json::json!(32));
}
serde_json::json!({
"architectures": [if moe {
"Qwen3_5MoeForConditionalGeneration"
} else {
"Qwen3_5ForConditionalGeneration"
}],
"model_type": if moe { "qwen3_5_moe" } else { "qwen3_5" },
"text_config": text,
"tie_word_embeddings": false
})
.to_string()
}
fn value(entries: &[RuntimeConfigEntry], key: &str) -> Option<String> {
entries
.iter()
.find(|entry| entry.key == key)
.map(|entry| entry.effective_value.clone())
}
#[tokio::test]
async fn explicit_semantic_preflight_rejects_before_weight_binding() {
let weights = temp_model_dir(
"preflight-weights",
r#"{"architectures":["Qwen3_5MoeForConditionalGeneration"]}"#,
);
std::fs::write(weights.join("model.safetensors"), []).unwrap();
let semantic = temp_model_dir(
"preflight-semantic",
r#"{
"architectures":["Qwen3_5MoeForConditionalGeneration"],
"model_type":"qwen3_5_moe",
"text_config":{"model_type":"unsupported_nested_layout"}
}"#,
);
let args = ProductSourceArgs {
gguf_file: None,
semantic_source: Some(semantic.clone()),
tokenizer_source: None,
};
let error = resolve_model_source_with_product_sources(
weights.to_str().unwrap(),
&weights.join("unused-cache"),
DownloadPolicy::NoDownload,
None,
&args,
)
.await
.err()
.expect("invalid semantic layout must fail before weight binding")
.to_string();
assert!(
error.contains("unsupported Qwen3.5 text model_type"),
"{error}"
);
assert!(
!error.contains("source manifest file is missing or empty"),
"{error}"
);
let _ = std::fs::remove_dir_all(weights);
let _ = std::fs::remove_dir_all(semantic);
}
#[test]
fn hf_and_gguf_aliases_are_disjoint() {
for entry in GGUF_ALIASES {
for alias in entry.aliases {
assert_eq!(
resolve_model_alias(alias),
*alias,
"alias '{alias}' resolves to both an HF repository and a GGUF file"
);
}
}
assert_eq!(resolve_model_alias("qwen3:1.7b"), "Qwen/Qwen3-1.7B");
assert_eq!(resolve_model_alias("qwen3.5:4b"), "Qwen/Qwen3.5-4B");
assert!(resolve_gguf_alias("qwen3:1.7b").is_none());
assert!(resolve_gguf_alias("qwen3:1.7b-gguf").is_some());
assert_eq!(
resolve_gguf_alias("qwen3.5:4b-q4_k_m"),
Some((
"unsloth/Qwen3.5-4B-GGUF".to_string(),
"Qwen3.5-4B-Q4_K_M.gguf".to_string()
))
);
assert_eq!(
resolve_gguf_alias("qwen3.5:35b-a3b-q4_k_s"),
Some((
"unsloth/Qwen3.5-35B-A3B-GGUF".to_string(),
"Qwen3.5-35B-A3B-Q4_K_S.gguf".to_string()
))
);
}
#[tokio::test]
async fn resolves_local_model_directory_with_stable_product_id() {
let config = qwen35_semantic_config(false);
let dir = temp_model_dir("local-product-id", &config);
std::fs::write(dir.join("model.safetensors"), b"fixture-weights").unwrap();
let resolved = resolve_model_source(
dir.to_str().unwrap(),
&dir.join("unused-cache"),
DownloadPolicy::NoDownload,
None,
)
.await
.unwrap();
let product = resolved.into_product_engine_input();
assert_eq!(product.source.local_path, dir);
assert_eq!(product.source.format, ModelFormat::SafeTensors);
assert!(!product.source.from_cache);
assert!(!product.autosized);
let sources = product.model_sources.as_ref().unwrap();
assert_eq!(sources.semantic_root(), dir.canonicalize().unwrap());
assert_eq!(sources.tokenizer_root(), dir.canonicalize().unwrap());
assert!(matches!(
product.engine_config.model.source.as_ref().unwrap(),
ModelSource::Local(path) if path == dir.to_str().unwrap()
));
assert_eq!(
product.engine_config.model.model_id.as_str(),
dir.file_name().unwrap().to_string_lossy()
);
let _ = std::fs::remove_dir_all(dir);
}
#[tokio::test]
async fn explicit_semantic_source_replaces_metadata_roles_not_weights() {
let weights = temp_model_dir(
"explicit-role-weights",
r#"{"architectures":["Qwen3_5MoeForConditionalGeneration"],"quantization_config":{"quant_method":"gptq"}}"#,
);
std::fs::write(weights.join("model.safetensors"), b"fixture-weights").unwrap();
let semantic_config = qwen35_semantic_config(true);
let semantic = temp_model_dir("explicit-role-semantic", &semantic_config);
std::fs::write(
semantic.join("tokenizer_config.json"),
br#"{"chat_template":"fixture"}"#,
)
.unwrap();
let args = ProductSourceArgs {
gguf_file: None,
semantic_source: Some(semantic.clone()),
tokenizer_source: None,
};
let resolved = resolve_model_source_with_product_sources(
weights.to_str().unwrap(),
&weights.join("unused-cache"),
DownloadPolicy::NoDownload,
None,
&args,
)
.await
.unwrap();
let product = resolved.into_product_engine_input();
let sources = product.model_sources.unwrap();
assert_eq!(product.requested_model, weights.display().to_string());
assert_eq!(sources.semantic_root(), semantic.canonicalize().unwrap());
assert_eq!(sources.tokenizer_root(), semantic.canonicalize().unwrap());
assert_eq!(sources.weights().path(), weights.canonicalize().unwrap());
assert!(sources
.fingerprint(
ferrum_interfaces::vnext::ModelArtifactSourceRole::Weights,
"config.json",
)
.is_some());
let _ = std::fs::remove_dir_all(weights);
let _ = std::fs::remove_dir_all(semantic);
}
fn cached_hf_fixture(cache: &Path, repo: &str, revision: &str) -> PathBuf {
let snapshot = cache
.join("hub")
.join(format!("models--{}", repo.replace('/', "--")))
.join("snapshots")
.join(revision);
std::fs::create_dir_all(&snapshot).unwrap();
for (name, bytes) in [
("model.safetensors", b"fixture-weights".as_slice()),
("config.json", br#"{"architectures":["Qwen3ForCausalLM"]}"#),
("tokenizer.json", br#"{"version":"1.0"}"#),
(
"tokenizer_config.json",
br#"{"chat_template":"fixture-template"}"#,
),
] {
std::fs::write(snapshot.join(name), bytes).unwrap();
}
snapshot
}
#[test]
fn incomplete_indexed_cache_is_not_a_hit() {
let cache = tempfile::tempdir().unwrap();
let repo = "Owner/Interrupted";
let revision = "a".repeat(40);
let snapshot = cached_hf_fixture(cache.path(), repo, &revision);
std::fs::remove_file(snapshot.join("model.safetensors")).unwrap();
std::fs::write(
snapshot.join("model.safetensors.index.json"),
br#"{"weight_map":{"first":"part-1.safetensors","last":"part-2.safetensors"}}"#,
)
.unwrap();
std::fs::write(snapshot.join("part-1.safetensors"), b"first shard").unwrap();
assert!(find_cached_model(cache.path(), repo).is_none());
let refs = snapshot.parent().unwrap().parent().unwrap().join("refs");
std::fs::create_dir_all(&refs).unwrap();
std::fs::write(refs.join("main"), &revision).unwrap();
assert!(find_cached_model(cache.path(), repo).is_none());
std::fs::write(snapshot.join("part-2.safetensors"), b"last shard").unwrap();
assert_eq!(
find_cached_model(cache.path(), repo).unwrap().local_path,
snapshot
);
}
#[tokio::test]
async fn incomplete_pinned_cache_reports_missing_shard_without_falling_back() {
let cache = tempfile::tempdir().unwrap();
let revision = "a".repeat(40);
let repo = "Owner/Interrupted";
let requested = format!("{repo}@{revision}");
let snapshot = cached_hf_fixture(cache.path(), repo, &revision);
cached_hf_fixture(cache.path(), repo, &"b".repeat(40));
std::fs::remove_file(snapshot.join("model.safetensors")).unwrap();
std::fs::write(
snapshot.join("model.safetensors.index.json"),
br#"{"weight_map":{"weight":"missing.safetensors"}}"#,
)
.unwrap();
let error =
resolve_model_source(&requested, cache.path(), DownloadPolicy::NoDownload, None)
.await
.err()
.expect("a pinned incomplete snapshot cannot satisfy an offline request")
.to_string();
assert!(error.contains("missing.safetensors"), "{error}");
assert!(error.contains("NoDownload"), "{error}");
}
#[test]
fn pinned_hf_specifier_requires_explicit_repository_and_full_commit() {
let sha = "ABCDEF01".repeat(5);
let spec = format!("Owner/Model.GPTQ_Int4@{sha}");
let pin = parse_pinned_hf_repository(&spec).unwrap().unwrap();
assert_eq!(pin.repo_id, "Owner/Model.GPTQ_Int4");
assert_eq!(pin.revision, sha.to_ascii_lowercase());
for invalid in [
"Owner/Model@main".to_owned(),
"Owner/Model@".to_owned(),
format!("Owner/Model@{}", "a".repeat(39)),
format!("Owner/Model@{}", "g".repeat(40)),
format!("Owner/Model@{sha}@{sha}"),
format!("Owner@{sha}"),
format!("/Model@{sha}"),
format!("Owner/Model/extra@{sha}"),
format!("../Model@{sha}"),
format!("Owner/../Model@{sha}"),
format!("https://huggingface.co/Owner/Model@{sha}"),
format!("Owner/Model @{sha}"),
format!("qwen3:1.7b@{sha}"),
] {
assert!(parse_pinned_hf_repository(&invalid).is_err(), "{invalid}");
}
let gguf = format!("qwen3:4b-q4_k_m@{sha}");
let error = parse_pinned_hf_repository(&gguf).unwrap_err().to_string();
assert!(error.contains("independent revisions"), "{error}");
for unpinned in ["Qwen/Qwen3-1.7B", "qwen3:1.7b", "qwen3:4b-q4_k_m"] {
assert!(parse_pinned_hf_repository(unpinned).unwrap().is_none());
}
}
#[tokio::test]
async fn pinned_hf_cache_ignores_main_and_preserves_product_source_identity() {
let cache = tempfile::tempdir().unwrap();
let repo = "Owner/Model";
let revision = "a".repeat(40);
let other_revision = "b".repeat(40);
let requested = format!("{repo}@{revision}");
let selected = cached_hf_fixture(cache.path(), repo, &revision);
let other = cached_hf_fixture(cache.path(), repo, &other_revision);
let refs = cache.path().join("hub/models--Owner--Model/refs");
std::fs::create_dir_all(&refs).unwrap();
std::fs::write(refs.join("main"), &other_revision).unwrap();
for product_entrypoint in [false, true] {
let resolved = if product_entrypoint {
resolve_model_source_with_product_sources(
&requested,
cache.path(),
DownloadPolicy::NoDownload,
None,
&ProductSourceArgs::default(),
)
.await
} else {
resolve_model_source(&requested, cache.path(), DownloadPolicy::NoDownload, None)
.await
}
.unwrap();
let product = resolved.into_product_engine_input();
assert_eq!(product.source.local_path, selected);
assert!(product.source.from_cache);
assert_eq!(product.requested_model, requested);
assert_eq!(product.public_model_id, repo);
assert!(
matches!(product.engine_config.model.source.as_ref().unwrap(),
ModelSource::HuggingFace { repo_id, revision: Some(actual), .. }
if repo_id == repo && actual == &revision)
);
let sources = product.model_sources.as_ref().unwrap();
for original in [
&sources.original_sources().semantic,
&sources.original_sources().tokenizer,
&sources.original_sources().weights,
] {
assert_eq!(original.location, repo);
assert_eq!(
original.requested_revision.as_deref(),
Some(revision.as_str())
);
}
assert_eq!(sources.semantic_root(), selected.canonicalize().unwrap());
assert_eq!(sources.tokenizer_root(), selected.canonicalize().unwrap());
assert_eq!(sources.weights().path(), selected.canonicalize().unwrap());
let identity = sources
.product_source_identity(
&requested,
repo,
"tokenizer_config.json",
"fixture-template",
)
.unwrap();
ferrum_bench_core::release_regression::model_sources::verify_pinned_source(
&requested,
&serde_json::to_value(identity).unwrap(),
)
.expect("the actual product source identity must satisfy the release source verifier");
}
let unpinned = resolve_model_source(repo, cache.path(), DownloadPolicy::NoDownload, None)
.await
.unwrap();
assert_eq!(unpinned.source.local_path, other);
assert!(matches!(
unpinned.original_source,
ModelSource::HuggingFace { revision: None, .. }
));
}
#[tokio::test]
async fn missing_pinned_hf_snapshot_never_falls_back_to_another_revision() {
let cache = tempfile::tempdir().unwrap();
let revision = "a".repeat(40);
let other = "b".repeat(40);
cached_hf_fixture(cache.path(), "Owner/Model", &other);
let refs = cache.path().join("hub/models--Owner--Model/refs");
std::fs::create_dir_all(&refs).unwrap();
std::fs::write(refs.join("main"), other).unwrap();
let requested = format!("Owner/Model@{revision}");
for with_main_ref in [true, false] {
if !with_main_ref {
std::fs::remove_file(refs.join("main")).unwrap();
}
let error =
resolve_model_source(&requested, cache.path(), DownloadPolicy::NoDownload, None)
.await
.err()
.expect("missing pinned snapshot must fail")
.to_string();
assert!(error.contains(&requested), "{error}");
assert!(error.contains("NoDownload"), "{error}");
}
}
#[test]
fn pinned_hf_download_result_must_match_repository_and_commit() {
let revision = "a".repeat(40);
let requested = format!("Owner/Model@{revision}");
let pin = parse_pinned_hf_repository(&requested).unwrap().unwrap();
let root = Path::new("/cache/hub/models--Owner--Model/snapshots");
pin.verify_snapshot(&root.join(&revision)).unwrap();
for wrong in [
root.join("b".repeat(40)),
PathBuf::from(format!(
"/cache/hub/models--Other--Model/snapshots/{revision}"
)),
PathBuf::from(format!("/cache/{revision}")),
] {
assert!(pin.verify_snapshot(&wrong).is_err(), "{}", wrong.display());
}
}
#[tokio::test]
async fn existing_local_sources_with_at_sign_keep_path_precedence() {
let root = tempfile::tempdir().unwrap();
let local = root.path().join("model@main");
std::fs::create_dir_all(&local).unwrap();
std::fs::write(local.join("model.safetensors"), b"fixture").unwrap();
let gguf = root.path().join("weights@main.gguf");
gguf_repository::tests::write_metadata_fixture(&gguf, "qwen3", &[]);
for path in [&local, &gguf] {
let resolved = resolve_model_source(
path.to_str().unwrap(),
&root.path().join("unused"),
DownloadPolicy::NoDownload,
None,
)
.await
.unwrap();
assert_eq!(&resolved.source.local_path, path);
assert_eq!(resolved.requested_model, path.to_str().unwrap());
assert!(matches!(resolved.original_source, ModelSource::Local(_)));
}
}
#[test]
fn direct_huggingface_snapshot_uses_stable_repository_public_id() {
let revision = "a".repeat(40);
let source = ResolvedModelSource {
original: "/cache/models--Qwen--Qwen3.5-35B-A3B-GPTQ-Int4/snapshots/local".to_owned(),
local_path: PathBuf::from(format!(
"/cache/models--Qwen--Qwen3.5-35B-A3B-GPTQ-Int4/snapshots/{revision}"
)),
format: ModelFormat::SafeTensors,
from_cache: false,
};
assert_eq!(public_model_id(&source), "Qwen/Qwen3.5-35B-A3B-GPTQ-Int4");
let gguf_source = ResolvedModelSource {
original: "/cache/models--unsloth--Qwen3.5-35B-A3B-GGUF/snapshots/local/model.gguf"
.to_owned(),
local_path: PathBuf::from(format!(
"/cache/models--unsloth--Qwen3.5-35B-A3B-GGUF/snapshots/{revision}/model.gguf"
)),
format: ModelFormat::GGUF,
from_cache: false,
};
assert_eq!(
public_model_id(&gguf_source),
"unsloth/Qwen3.5-35B-A3B-GGUF"
);
}
#[tokio::test]
async fn resolves_direct_gguf_package_with_file_stem_product_id() {
let config = qwen35_semantic_config(false);
let dir = temp_model_dir("direct-gguf-package", &config);
let gguf = dir.join("Qwen3.5-4B-Instruct-Q4_K_M.gguf");
std::fs::write(&gguf, b"fixture-gguf").unwrap();
for requested_path in [&gguf, &dir] {
let resolved = resolve_model_source(
requested_path.to_str().unwrap(),
&dir.join("unused-cache"),
DownloadPolicy::NoDownload,
None,
)
.await
.unwrap();
let product = resolved.into_product_engine_input();
assert_eq!(product.source.local_path, gguf);
assert_eq!(product.source.format, ModelFormat::GGUF);
assert!(!product.source.from_cache);
let sources = product.model_sources.as_ref().unwrap();
assert_eq!(sources.semantic_root(), dir.canonicalize().unwrap());
assert_eq!(sources.tokenizer_root(), dir.canonicalize().unwrap());
assert_eq!(sources.weights().path(), gguf.canonicalize().unwrap());
assert_eq!(
sources.original_sources().weights.kind,
if requested_path.is_dir() {
ModelSourceKind::LocalDirectory
} else {
ModelSourceKind::LocalFile
}
);
assert!(matches!(
ferrum_models::vnext::resolve_registered_model_from_sources(sources).unwrap(),
ferrum_models::vnext::ProductionModelRegistration::Registered(_)
));
assert!(matches!(
product.engine_config.model.source.as_ref().unwrap(),
ModelSource::Local(path) if path == requested_path.to_str().unwrap()
));
assert_eq!(
product.engine_config.model.model_id.as_str(),
"Qwen3.5-4B-Instruct-Q4_K_M"
);
}
let _ = std::fs::remove_dir_all(dir);
}
#[tokio::test]
async fn unresolved_direct_gguf_keeps_legacy_compatibility_for_unmigrated_architectures() {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!(
"ferrum-source-resolver-untyped-gguf-{}-{nonce}",
std::process::id()
));
std::fs::create_dir_all(&dir).unwrap();
let gguf = dir.join("legacy-model.gguf");
gguf_repository::tests::write_metadata_fixture(&gguf, "qwen3", &[]);
let resolved = resolve_model_source(
gguf.to_str().unwrap(),
&dir.join("unused-cache"),
DownloadPolicy::NoDownload,
None,
)
.await
.unwrap();
let product = resolved.into_product_engine_input();
assert!(product.model_sources.is_none());
assert_eq!(product.source.local_path, gguf);
let _ = std::fs::remove_dir_all(dir);
}
#[tokio::test]
async fn resolves_cached_gguf_alias_without_entrypoint_short_circuit() {
let cache = temp_model_dir("cached-gguf-alias", r#"{}"#);
let (repo, filename) = resolve_gguf_alias("qwen3:4b-q4_k_m").unwrap();
let repo_dir = cache
.join("hub")
.join(format!("models--{}", repo.replace('/', "--")));
let revision = "fixture-revision";
let snapshot = repo_dir.join("snapshots").join(revision);
std::fs::create_dir_all(&snapshot).unwrap();
std::fs::create_dir_all(repo_dir.join("refs")).unwrap();
std::fs::write(repo_dir.join("refs/main"), revision).unwrap();
let gguf = snapshot.join(&filename);
gguf_repository::tests::write_metadata_fixture(&gguf, "qwen3", &[]);
let metadata_repo = tokenizer_sibling_repo(&repo).unwrap();
let metadata_repo_dir = cache
.join("hub")
.join(format!("models--{}", metadata_repo.replace('/', "--")));
let metadata_revision = "metadata-fixture-revision";
let metadata_snapshot = metadata_repo_dir.join("snapshots").join(metadata_revision);
std::fs::create_dir_all(&metadata_snapshot).unwrap();
std::fs::create_dir_all(metadata_repo_dir.join("refs")).unwrap();
std::fs::write(metadata_repo_dir.join("refs/main"), metadata_revision).unwrap();
std::fs::write(
metadata_snapshot.join("config.json"),
br#"{"architectures":["Qwen3ForCausalLM"]}"#,
)
.unwrap();
std::fs::write(
metadata_snapshot.join("tokenizer.json"),
br#"{"version":"1.0"}"#,
)
.unwrap();
std::fs::write(
metadata_snapshot.join("tokenizer_config.json"),
br#"{"chat_template":"fixture-template"}"#,
)
.unwrap();
let resolved =
resolve_model_source("qwen3:4b-q4_k_m", &cache, DownloadPolicy::NoDownload, None)
.await
.unwrap();
let product = resolved.into_product_engine_input();
assert_eq!(product.source.local_path, gguf);
assert_eq!(product.source.format, ModelFormat::GGUF);
assert!(product.source.from_cache);
let sources = product.model_sources.as_ref().unwrap();
assert_eq!(
sources.semantic_root(),
metadata_snapshot.canonicalize().unwrap()
);
assert_eq!(
sources.tokenizer_root(),
metadata_snapshot.canonicalize().unwrap()
);
assert_eq!(sources.weights().path(), gguf.canonicalize().unwrap());
assert_eq!(sources.original_sources().semantic.location, metadata_repo);
assert_eq!(sources.original_sources().weights.location, repo);
assert!(!snapshot.join("tokenizer.json").exists());
assert!(matches!(
product.engine_config.model.source.as_ref().unwrap(),
ModelSource::HuggingFace { repo_id, revision: None, cache_dir: Some(root) }
if repo_id == &repo && root == &cache.display().to_string()
));
assert_eq!(
product.engine_config.model.model_id.as_str(),
Path::new(&filename).file_stem().unwrap().to_string_lossy()
);
let _ = std::fs::remove_dir_all(cache);
}
#[tokio::test]
async fn cached_typed_gguf_repository_resolves_independent_roles_and_tokenizer_override() {
use candle_core::quantized::gguf_file::{self, Value};
let cache = tempfile::tempdir().unwrap();
let repo = "unsloth/Qwen3.5-4B-GGUF";
let semantic_repo = "Qwen/Qwen3.5-4B";
let revision = "a".repeat(40);
let semantic_revision = "b".repeat(40);
let snapshot = cache
.path()
.join("hub/models--unsloth--Qwen3.5-4B-GGUF/snapshots")
.join(&revision);
let semantic = cache
.path()
.join("hub/models--Qwen--Qwen3.5-4B/snapshots")
.join(&semantic_revision);
std::fs::create_dir_all(&snapshot).unwrap();
std::fs::create_dir_all(&semantic).unwrap();
let gguf = snapshot.join("model.gguf");
let mut file = std::fs::File::create(&gguf).unwrap();
gguf_file::write(
&mut file,
&[("general.architecture", &Value::String("qwen35".into()))],
&[],
)
.unwrap();
drop(file);
std::fs::write(semantic.join("config.json"), qwen35_semantic_config(false)).unwrap();
std::fs::write(semantic.join("tokenizer.json"), br#"{"version":"1.0"}"#).unwrap();
std::fs::write(
semantic.join("tokenizer_config.json"),
br#"{"chat_template":"fixture-template"}"#,
)
.unwrap();
for request in [repo.to_owned(), format!("{repo}@{revision}")] {
let product = resolve_model_source_with_product_sources(
&request,
cache.path(),
DownloadPolicy::NoDownload,
None,
&ProductSourceArgs::default(),
)
.await
.unwrap()
.into_product_engine_input();
let sources = product.model_sources.unwrap();
assert_eq!(sources.weights().path(), gguf.canonicalize().unwrap());
assert_eq!(sources.semantic_root(), semantic.canonicalize().unwrap());
assert_eq!(sources.tokenizer_root(), semantic.canonicalize().unwrap());
assert_eq!(sources.original_sources().semantic.location, semantic_repo);
assert_eq!(sources.original_sources().weights.location, repo);
assert_eq!(
sources
.original_sources()
.weights
.requested_revision
.as_deref(),
request.contains('@').then_some(revision.as_str())
);
}
let tokenizer = cache.path().join("explicit-tokenizer");
std::fs::create_dir(&tokenizer).unwrap();
std::fs::rename(
semantic.join("tokenizer.json"),
tokenizer.join("tokenizer.json"),
)
.unwrap();
std::fs::rename(
semantic.join("tokenizer_config.json"),
tokenizer.join("tokenizer_config.json"),
)
.unwrap();
let product = resolve_model_source_with_product_sources(
repo,
cache.path(),
DownloadPolicy::NoDownload,
None,
&ProductSourceArgs {
gguf_file: None,
semantic_source: None,
tokenizer_source: Some(tokenizer.clone()),
},
)
.await
.unwrap()
.into_product_engine_input();
let sources = product.model_sources.unwrap();
assert_eq!(sources.semantic_root(), semantic.canonicalize().unwrap());
assert_eq!(sources.tokenizer_root(), tokenizer.canonicalize().unwrap());
assert!(!snapshot.join("config.json").exists());
assert!(!semantic.join("tokenizer.json").exists());
}
#[test]
fn serve_profile_defaults_metal_gguf_moe_without_user_env() {
let entries = serve_profile_runtime_entries_for_arch(
true,
true,
true,
true,
false,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("1024")
);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("16")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MAX_BATCH").as_deref(), Some("16"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED").as_deref(), Some("1"));
assert_eq!(
value(&entries, "FERRUM_MOE_BATCHED_DECODE").as_deref(),
Some("1")
);
}
#[test]
fn serve_profile_keeps_multi_seq_default_for_non_metal_moe() {
let entries = serve_profile_runtime_entries_for_arch(
true,
true,
false,
true,
false,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("2048")
);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("16")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MAX_BATCH").as_deref(), Some("16"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED").as_deref(), Some("1"));
assert_eq!(
value(&entries, "FERRUM_MOE_BATCHED_DECODE").as_deref(),
Some("1")
);
}
#[test]
fn serve_profile_defaults_gguf_dense_without_moe_env() {
let entries = serve_profile_runtime_entries_for_arch(
true,
false,
true,
true,
false,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("512")
);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("16")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED"), None);
}
#[test]
fn serve_profile_leaves_context_capacity_to_vnext_plan() {
let entries = serve_profile_runtime_entries_for_arch(
true,
false,
true,
true,
true,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(value(&entries, "FERRUM_KV_CAPACITY"), None);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("16")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MAX_BATCH").as_deref(), Some("16"));
}
#[test]
fn serve_profile_respects_explicit_user_env() {
let current = RuntimeConfigSnapshot::from_entries(vec![RuntimeConfigEntry::new(
"FERRUM_KV_CAPACITY",
"4096",
RuntimeConfigSource::Default,
)]);
let entries = serve_profile_runtime_entries_for_arch(
true,
true,
true,
true,
false,
¤t,
RuntimeConfigSource::Default,
);
assert_eq!(value(&entries, "FERRUM_KV_CAPACITY"), None);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("16")
);
}
#[test]
fn cpu_gguf_serve_profile_does_not_enable_unsupported_paged_operators() {
let directory = tempfile::tempdir().unwrap();
let file = directory.path().join("model.gguf");
gguf_repository::tests::write_metadata_fixture(&file, "qwen3", &[]);
let entries = serve_profile_runtime_entries(
&file,
&ferrum_types::Device::CPU,
false,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("0"));
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("0")
);
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED"), None);
assert_eq!(value(&entries, "FERRUM_PAGED_MAX_SEQS"), None);
}
#[test]
fn chat_profile_defaults_dense_safetensors_as_typed_entries() {
let dir = temp_model_dir(
"dense",
r#"{"architectures":["Qwen3ForCausalLM"],"model_type":"qwen3"}"#,
);
let entries = chat_profile_runtime_entries(
&dir,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("8192")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("2")
);
assert_eq!(value(&entries, "FERRUM_MAX_BATCH").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED"), None);
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn chat_profile_recognizes_qwen35_dense_without_qwen3_fallback() {
let dir = temp_model_dir(
"qwen35_dense",
r#"{"architectures":["Qwen3_5ForConditionalGeneration"],"model_type":"qwen3_5"}"#,
);
assert_eq!(detect_model_family(&dir).as_deref(), Some("qwen3_5"));
assert!(!detect_moe_arch(&dir));
let entries = chat_profile_runtime_entries(
&dir,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED"), None);
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn chat_profile_disables_metal_paged_kv_for_llama_safetensors() {
let dir = temp_model_dir(
"llama",
r#"{"architectures":["LlamaForCausalLM"],"model_type":"llama"}"#,
);
let entries = chat_profile_runtime_entries(
&dir,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("0")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("0"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn chat_profile_disables_metal_paged_kv_for_qwen2_safetensors() {
let dir = temp_model_dir(
"qwen2",
r#"{"architectures":["Qwen2ForCausalLM"],"model_type":"qwen2"}"#,
);
let entries = chat_profile_runtime_entries(
&dir,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("0")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("0"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn chat_profile_defaults_moe_safetensors_as_typed_entries() {
let dir = temp_model_dir(
"moe",
r#"{"architectures":["Qwen3MoeForCausalLM"],"model_type":"qwen3_moe"}"#,
);
let entries = chat_profile_runtime_entries(
&dir,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("4096")
);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("1")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("1")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("1"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED").as_deref(), Some("0"));
assert_eq!(
value(&entries, "FERRUM_MOE_BATCHED_DECODE").as_deref(),
Some("0")
);
assert_eq!(
value(&entries, "FERRUM_MOE_BATCH_THRESHOLD").as_deref(),
Some("2")
);
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn chat_profile_recognizes_qwen35_moe_as_distinct_moe_family() {
let dir = temp_model_dir(
"qwen35_moe",
r#"{"architectures":["Qwen3_5MoeForConditionalGeneration"],"model_type":"qwen3_5_moe"}"#,
);
assert_eq!(detect_model_family(&dir).as_deref(), Some("qwen3_5_moe"));
assert!(detect_moe_arch(&dir));
let entries = chat_profile_runtime_entries(
&dir,
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("4096")
);
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED").as_deref(), Some("0"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn chat_profile_defaults_moe_gguf_as_typed_entries() {
let entries = chat_profile_runtime_entries_for_arch(
true,
true,
Some("qwen3_moe"),
&RuntimeConfigSnapshot::default(),
RuntimeConfigSource::Default,
);
assert_eq!(
value(&entries, "FERRUM_KV_CAPACITY").as_deref(),
Some("4096")
);
assert_eq!(
value(&entries, "FERRUM_PAGED_MAX_SEQS").as_deref(),
Some("1")
);
assert_eq!(
value(&entries, "FERRUM_METAL_PAGED_KV").as_deref(),
Some("0")
);
assert_eq!(value(&entries, "FERRUM_PAGED_KV").as_deref(), Some("0"));
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED").as_deref(), Some("0"));
assert_eq!(
value(&entries, "FERRUM_MOE_BATCHED_DECODE").as_deref(),
Some("0")
);
}
#[test]
fn chat_profile_defaults_preserve_existing_snapshot_values() {
let dir = temp_model_dir(
"override",
r#"{"architectures":["Qwen3MoeForCausalLM"],"model_type":"qwen3_moe"}"#,
);
let current = RuntimeConfigSnapshot::from_entries([
RuntimeConfigEntry::new("FERRUM_KV_CAPACITY", "1234", RuntimeConfigSource::Env),
RuntimeConfigEntry::new("FERRUM_MOE_BATCH_THRESHOLD", "7", RuntimeConfigSource::Env),
]);
let entries = chat_profile_runtime_entries(&dir, ¤t, RuntimeConfigSource::Default);
assert_eq!(value(&entries, "FERRUM_KV_CAPACITY"), None);
assert_eq!(value(&entries, "FERRUM_MOE_BATCH_THRESHOLD"), None);
assert_eq!(value(&entries, "FERRUM_MOE_BATCHED").as_deref(), Some("0"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn load_model_chat_template_reads_tokenizer_config() {
let dir = temp_model_dir(
"template",
r#"{"architectures":["Qwen3ForCausalLM"],"model_type":"qwen3"}"#,
);
std::fs::write(
dir.join("tokenizer_config.json"),
r#"{"chat_template":"{{ messages[0].content }}","bos_token":"<s>","eos_token":"</s>"}"#,
)
.unwrap();
let template = load_model_chat_template(&dir).unwrap();
assert_eq!(template.template, "{{ messages[0].content }}");
assert_eq!(template.bos_token.as_deref(), Some("<s>"));
assert_eq!(template.eos_token.as_deref(), Some("</s>"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn product_chat_template_uses_immutable_source_bytes() {
let dir = temp_model_dir(
"immutable-template",
r#"{"architectures":["Qwen3ForCausalLM"],"model_type":"qwen3"}"#,
);
std::fs::write(dir.join("model.safetensors"), b"fixture-weights").unwrap();
std::fs::write(
dir.join("tokenizer_config.json"),
r#"{"chat_template":"original-template","eos_token":"</s>"}"#,
)
.unwrap();
let bundle = ProductionModelSourceBundle::open_colocated_safetensors(&dir).unwrap();
std::fs::write(
dir.join("tokenizer_config.json"),
r#"{"chat_template":"mutated-template"}"#,
)
.unwrap();
let template = load_product_chat_template(&bundle).unwrap();
assert_eq!(template.template, "original-template");
assert_eq!(template.eos_token.as_deref(), Some("</s>"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn typed_standalone_template_retains_immutable_bytes_for_run_and_serve() {
let dir = temp_model_dir(
"typed-sidecar-template",
r#"{"architectures":["Qwen3MoeForCausalLM"],"model_type":"qwen3_moe"}"#,
);
std::fs::write(dir.join("model.safetensors"), b"fixture-weights").unwrap();
std::fs::write(
dir.join("tokenizer_config.json"),
r#"{"chat_template":null,"eos_token_id":2}"#,
)
.unwrap();
std::fs::write(dir.join("chat_template.jinja"), "{{ messages[0].content }}").unwrap();
let bundle = ProductionModelSourceBundle::open_colocated_safetensors(&dir).unwrap();
std::fs::write(
dir.join("chat_template.jinja"),
"changed after source opened",
)
.unwrap();
let selected = load_product_chat_template_source(&bundle, "chat_template.jinja").unwrap();
assert_eq!(selected.template, "{{ messages[0].content }}");
assert!(selected.source.ends_with("chat_template.jinja"));
assert!(load_product_chat_template_source(&bundle, "tokenizer_config.json").is_none());
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn typed_template_source_ignores_unselected_duplicate() {
let dir = temp_model_dir(
"typed-template-source",
r#"{"architectures":["Qwen3ForCausalLM"],"model_type":"qwen3"}"#,
);
std::fs::write(dir.join("model.safetensors"), b"fixture-weights").unwrap();
std::fs::write(
dir.join("tokenizer_config.json"),
r#"{"chat_template":"typed-template","eos_token":"</s>"}"#,
)
.unwrap();
std::fs::write(dir.join("chat_template.jinja"), "unselected-template").unwrap();
let bundle = ProductionModelSourceBundle::open_colocated_safetensors(&dir).unwrap();
let template = load_product_chat_template_source(&bundle, "tokenizer_config.json").unwrap();
assert_eq!(template.template, "typed-template");
assert_eq!(template.eos_token.as_deref(), Some("</s>"));
assert!(template.source.ends_with("tokenizer_config.json"));
let _ = std::fs::remove_dir_all(dir);
}
}