use std::path::Path;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Backend {
Vllm,
Ollama,
LlamaCpp,
}
impl Backend {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Vllm => "vllm",
Self::Ollama => "ollama",
Self::LlamaCpp => "llama_cpp",
}
}
}
impl std::str::FromStr for Backend {
type Err = String;
fn from_str(s: &str) -> Result<Self, String> {
match s.trim().to_ascii_lowercase().as_str() {
"vllm" => Ok(Self::Vllm),
"ollama" => Ok(Self::Ollama),
"llama_cpp" | "llama.cpp" | "llamacpp" => Ok(Self::LlamaCpp),
other => Err(format!(
"unknown backend `{other}` (expected vllm | ollama | llama_cpp)"
)),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VllmProfile {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub served_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_model_len: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tensor_parallel: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gpu_mem: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_parser: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_call_parser: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enable_auto_tool_choice: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extra: Vec<String>,
}
impl VllmProfile {
#[must_use]
pub(crate) fn merge(self, o: Self) -> Self {
Self {
served_name: o.served_name.or(self.served_name),
max_model_len: o.max_model_len.or(self.max_model_len),
tensor_parallel: o.tensor_parallel.or(self.tensor_parallel),
gpu_mem: o.gpu_mem.or(self.gpu_mem),
reasoning_parser: o.reasoning_parser.or(self.reasoning_parser),
tool_call_parser: o.tool_call_parser.or(self.tool_call_parser),
enable_auto_tool_choice: o.enable_auto_tool_choice.or(self.enable_auto_tool_choice),
extra: if o.extra.is_empty() {
self.extra
} else {
o.extra
},
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FamilyDefaults {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vllm: Option<VllmProfile>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OllamaProfile {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub num_ctx: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub template: Option<String>,
}
impl OllamaProfile {
#[must_use]
fn merge(self, o: Self) -> Self {
Self {
tag: o.tag.or(self.tag),
num_ctx: o.num_ctx.or(self.num_ctx),
template: o.template.or(self.template),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Tuning {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub temperature: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_tokens: Option<u32>,
}
impl Tuning {
#[must_use]
fn merge(self, o: Self) -> Self {
Self {
temperature: o.temperature.or(self.temperature),
top_p: o.top_p.or(self.top_p),
top_k: o.top_k.or(self.top_k),
context_tokens: o.context_tokens.or(self.context_tokens),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReasoningReplayScope {
#[default]
Never,
CurrentUserTurn,
FullHistory,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ChatCompletionsCapability {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cognition: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chat_template_kwargs: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parallel_tool_calls: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bounded_reasoning_continuation: Option<bool>,
}
impl ChatCompletionsCapability {
#[must_use]
fn merge(self, o: Self) -> Self {
Self {
cognition: o.cognition.or(self.cognition),
chat_template_kwargs: o.chat_template_kwargs.or(self.chat_template_kwargs),
parallel_tool_calls: o.parallel_tool_calls.or(self.parallel_tool_calls),
bounded_reasoning_continuation: o
.bounded_reasoning_continuation
.or(self.bounded_reasoning_continuation),
}
}
}
#[path = "model_card_effort.rs"]
mod effort;
pub use effort::{ReasoningEffort, ReasoningEffortLadder, ResponsesCapability};
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Capability {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub emits_leading_reasoning: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub thinking_default: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_content_field: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_replay_scope: Option<ReasoningReplayScope>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chat_completions: Option<ChatCompletionsCapability>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub responses: Option<ResponsesCapability>,
}
impl Capability {
#[must_use]
pub(crate) fn merge(self, o: Self) -> Self {
Self {
emits_leading_reasoning: o.emits_leading_reasoning.or(self.emits_leading_reasoning),
thinking_default: o.thinking_default.or(self.thinking_default),
reasoning_content_field: o.reasoning_content_field.or(self.reasoning_content_field),
reasoning_replay_scope: o.reasoning_replay_scope.or(self.reasoning_replay_scope),
responses: match (self.responses, o.responses) {
(Some(base), Some(overlay)) => Some(base.merge(overlay)),
(base, overlay) => overlay.or(base),
},
chat_completions: match (self.chat_completions, o.chat_completions) {
(Some(base), Some(overlay)) => Some(base.merge(overlay)),
(base, overlay) => overlay.or(base),
},
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ModelCard {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backend: Option<Backend>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub footprint_gib: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gated: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub family: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vllm: Option<VllmProfile>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ollama: Option<OllamaProfile>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tuning: Option<Tuning>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub capability: Option<Capability>,
}
fn merge_opt<T>(base: Option<T>, overlay: Option<T>, f: impl FnOnce(T, T) -> T) -> Option<T> {
match (base, overlay) {
(Some(b), Some(o)) => Some(f(b, o)),
(b, None) => b,
(None, o) => o,
}
}
impl ModelCard {
#[must_use]
pub fn merge(self, overlay: Self) -> Self {
Self {
name: if overlay.name.trim().is_empty() {
self.name
} else {
overlay.name
},
backend: overlay.backend.or(self.backend),
footprint_gib: overlay.footprint_gib.or(self.footprint_gib),
gated: overlay.gated.or(self.gated),
family: overlay.family.or(self.family),
vllm: merge_opt(self.vllm, overlay.vllm, VllmProfile::merge),
ollama: merge_opt(self.ollama, overlay.ollama, OllamaProfile::merge),
tuning: merge_opt(self.tuning, overlay.tuning, Tuning::merge),
capability: merge_opt(self.capability, overlay.capability, Capability::merge),
}
}
pub fn validate(&self) -> Result<(), String> {
if self.name.trim().is_empty() {
return Err("model card: `name` is empty".to_string());
}
if let Some(family) = self.family.as_deref() {
if family.trim().is_empty() {
return Err(format!(
"model card `{}`: `family` is empty — declare a real family \
identity or remove the key",
self.name
));
}
}
match self.backend {
None => Err(format!(
"model card `{}`: no `backend` set (vllm | ollama | llama_cpp)",
self.name
)),
Some(Backend::Vllm) if self.vllm.is_none() => Err(format!(
"model card `{}`: backend=vllm but no [vllm] serving block",
self.name
)),
Some(Backend::Ollama) if self.ollama.is_none() => Err(format!(
"model card `{}`: backend=ollama but no [ollama] serving block",
self.name
)),
Some(_) => Ok(()),
}
}
pub fn to_toml(&self) -> Result<String, String> {
toml::to_string_pretty(self).map_err(|e| format!("card serialize: {e}"))
}
}
pub fn parse_card(contents: &str, ext: &str) -> Result<ModelCard, String> {
match ext.trim_start_matches('.').to_ascii_lowercase().as_str() {
"toml" => toml::from_str(contents).map_err(|e| format!("card TOML: {e}")),
"yaml" | "yml" => serde_yaml::from_str(contents).map_err(|e| format!("card YAML: {e}")),
other => Err(format!(
"card: unknown extension `.{other}` (expected .toml / .yaml / .yml)"
)),
}
}
pub fn load_card_file(path: &Path) -> Result<ModelCard, String> {
let contents =
std::fs::read_to_string(path).map_err(|e| format!("read {}: {e}", path.display()))?;
let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
parse_card(&contents, ext)
}
#[must_use]
pub fn load_dropin_dir(dir: &Path) -> Vec<ModelCard> {
let mut out = Vec::new();
let Ok(rd) = std::fs::read_dir(dir) else {
return out;
};
for entry in rd.flatten() {
let p = entry.path();
let ext = p.extension().and_then(|s| s.to_str()).unwrap_or("");
if matches!(ext, "toml" | "yaml" | "yml") {
if let Ok(card) = load_card_file(&p) {
out.push(card);
}
}
}
out
}
#[deprecated(
note = "resolve card names through card_catalog::ModelCardCatalog::resolve_exact; \
for a pre-merged card, card_catalog::finalize validates too"
)]
#[must_use]
pub fn resolve(builtin: ModelCard, dropins: &[ModelCard], one_off: Option<ModelCard>) -> ModelCard {
let name = builtin.name.clone();
let mut card = dropins
.iter()
.filter(|d| d.name == name)
.cloned()
.fold(builtin, ModelCard::merge);
if let Some(o) = one_off {
card = card.merge(o);
}
apply_family_defaults(&mut card);
card
}
pub(crate) fn apply_family_defaults(card: &mut ModelCard) {
if let Some(family) = card.family.clone() {
if let Some(defaults) = family_defaults(&family) {
card.vllm = merge_opt(defaults.vllm, card.vllm.take(), VllmProfile::merge);
}
}
}
#[must_use]
pub fn family_defaults(family: &str) -> Option<FamilyDefaults> {
const EMBEDDED: &[(&str, &str)] = &[("qwen3", include_str!("cards/families/qwen3.toml"))];
let want = family.trim().to_ascii_lowercase();
EMBEDDED
.iter()
.find(|(name, _)| *name == want)
.map(|(_, toml)| toml::from_str(toml).expect("built-in family-defaults file is valid TOML"))
}
#[must_use]
pub fn builtin_cards() -> Vec<ModelCard> {
builtin_card_entries().into_iter().map(|(_, c)| c).collect()
}
#[must_use]
pub fn builtin_card_entries() -> Vec<(String, ModelCard)> {
const EMBEDDED: &[(&str, &str)] = &[
("ornith-1.0-35b", include_str!("cards/ornith-1.0-35b.toml")),
(
"ornith-1.0-397b",
include_str!("cards/ornith-1.0-397b.toml"),
),
];
EMBEDDED
.iter()
.map(|(key, s)| {
(
(*key).to_string(),
parse_card(s, "toml").expect("built-in card is valid TOML"),
)
})
.collect()
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResolvedCapabilities {
inline: Option<Capability>,
binding: Option<CardBinding>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CardBindingSeed {
pub card: Option<String>,
pub bound_model: Option<String>,
pub bound_destination: crate::config::BackendDestination,
}
impl CardBindingSeed {
#[must_use]
pub fn from_backend(backend: &crate::BackendConfig) -> Self {
Self {
card: backend.card.clone(),
bound_model: backend.effective_model().map(str::to_string),
bound_destination: crate::config::BackendDestination::of(backend),
}
}
}
#[derive(Debug, Clone, PartialEq)]
struct CardBinding {
name: String,
capability: Option<Capability>,
family: Option<String>,
bound_model: Option<String>,
bound_destination: crate::config::BackendDestination,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ServingPrincipal<'a> {
Instance,
MultiplexerModel(&'a str),
SelectedModel(&'a str),
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CardApplicability {
None,
Active {
card: String,
},
InactiveDestination {
card: String,
bound_destination: crate::config::BackendDestination,
active_destination: crate::config::BackendDestination,
},
InactiveModel {
card: String,
bound_model: Option<String>,
active_model: String,
},
Undecided { card: String },
}
#[derive(Debug, Clone, PartialEq)]
pub struct CapabilityDecision {
effective: Option<Capability>,
applicability: CardApplicability,
}
impl CapabilityDecision {
#[must_use]
pub fn effective(&self) -> Option<&Capability> {
self.effective.as_ref()
}
#[must_use]
pub fn applicability(&self) -> &CardApplicability {
&self.applicability
}
#[must_use]
pub fn chat_completions(&self) -> ChatCompletionsCapability {
self.effective
.as_ref()
.and_then(|c| c.chat_completions)
.unwrap_or_default()
}
#[must_use]
pub fn responses(&self) -> ResponsesCapability {
self.effective
.as_ref()
.and_then(|c| c.responses.clone())
.unwrap_or_default()
}
#[must_use]
pub fn reasoning_replay_scope(&self) -> ReasoningReplayScope {
self.effective
.as_ref()
.and_then(|c| c.reasoning_replay_scope)
.unwrap_or_default()
}
#[must_use]
pub fn emits_leading_reasoning(&self) -> bool {
self.effective
.as_ref()
.and_then(|c| c.emits_leading_reasoning)
.unwrap_or(false)
}
}
fn principal_associates(bound_model: Option<&str>, principal: ServingPrincipal<'_>) -> bool {
match principal {
ServingPrincipal::Instance => true,
ServingPrincipal::MultiplexerModel(current) | ServingPrincipal::SelectedModel(current) => {
!current.trim().is_empty() && bound_model == Some(current)
}
_ => false,
}
}
impl ResolvedCapabilities {
pub fn resolve(
backend: &crate::BackendConfig,
seed: &CardBindingSeed,
explicit_config: Option<&Path>,
) -> Result<Self, String> {
let inline = backend.capability.clone();
let Some(name) = seed.card.as_deref().filter(|n| !n.trim().is_empty()) else {
return Ok(Self {
inline,
binding: None,
});
};
let dir = explicit_config
.and_then(|p| p.parent())
.map(|d| d.join("models"))
.or_else(|| crate::Config::user_config_dir().map(|d| d.join("models")));
let catalog = crate::card_catalog::ModelCardCatalog::load(dir.as_deref());
let card = catalog
.resolve_exact(name)
.map_err(|e| format!("backend `{}` names model card `{name}` — {e}", backend.name))?;
let capability = card.capability;
let family = card.family.clone().filter(|f| !f.trim().is_empty());
Ok(Self {
binding: (capability.is_some() || family.is_some()).then(|| CardBinding {
name: name.to_string(),
capability,
family,
bound_model: seed.bound_model.clone().filter(|m| !m.trim().is_empty()),
bound_destination: seed.bound_destination.clone(),
}),
inline,
})
}
#[must_use]
pub fn none() -> Self {
Self {
inline: None,
binding: None,
}
}
#[must_use]
pub fn card(&self) -> Option<&str> {
self.binding.as_ref().map(|b| b.name.as_str())
}
#[must_use]
pub fn family_for_route(
&self,
active_destination: &crate::config::BackendDestination,
principal: ServingPrincipal<'_>,
) -> Option<&str> {
let b = self.binding.as_ref()?;
let family = b.family.as_deref()?;
(b.bound_destination.is_concrete()
&& active_destination.is_concrete()
&& b.bound_destination == *active_destination
&& principal_associates(b.bound_model.as_deref(), principal))
.then_some(family)
}
#[must_use]
pub fn for_route(
&self,
active_destination: &crate::config::BackendDestination,
principal: ServingPrincipal<'_>,
) -> CapabilityDecision {
let Some(b) = &self.binding else {
return CapabilityDecision {
effective: self.inline.clone(),
applicability: CardApplicability::None,
};
};
if !b.bound_destination.is_concrete() || !active_destination.is_concrete() {
return CapabilityDecision {
effective: self.inline.clone(),
applicability: CardApplicability::Undecided {
card: b.name.clone(),
},
};
}
if b.bound_destination != *active_destination {
return CapabilityDecision {
effective: self.inline.clone(),
applicability: CardApplicability::InactiveDestination {
card: b.name.clone(),
bound_destination: b.bound_destination.clone(),
active_destination: active_destination.clone(),
},
};
}
let applies = principal_associates(b.bound_model.as_deref(), principal);
if applies {
let effective = match (b.capability.clone(), self.inline.clone()) {
(Some(cap), Some(inl)) => Some(cap.merge(inl)),
(Some(cap), None) => Some(cap),
(None, inline) => inline,
};
return CapabilityDecision {
effective,
applicability: CardApplicability::Active {
card: b.name.clone(),
},
};
}
let applicability = match principal {
ServingPrincipal::MultiplexerModel(current)
| ServingPrincipal::SelectedModel(current)
if !current.trim().is_empty() =>
{
CardApplicability::InactiveModel {
card: b.name.clone(),
bound_model: b.bound_model.clone(),
active_model: current.to_string(),
}
}
_ => CardApplicability::Undecided {
card: b.name.clone(),
},
};
CapabilityDecision {
effective: self.inline.clone(),
applicability,
}
}
}
#[must_use]
pub fn builtin_card(name: &str) -> Option<ModelCard> {
let want = name.trim().to_ascii_lowercase();
builtin_cards()
.into_iter()
.find(|c| c.name.to_ascii_lowercase() == want)
}
#[must_use]
pub fn no_hardware_leak(card: &ModelCard) -> Vec<String> {
let text = card.to_toml().unwrap_or_default();
let mut hits = Vec::new();
for tok in text.split(|c: char| !(c.is_ascii_digit() || c == '.')) {
if is_private_ipv4(tok) {
hits.push(tok.to_string());
}
}
hits
}
fn is_private_ipv4(s: &str) -> bool {
let octets: Vec<&str> = s.split('.').collect();
if octets.len() != 4 {
return false;
}
let mut o = [0u16; 4];
for (i, part) in octets.iter().enumerate() {
match part.parse::<u16>() {
Ok(v) if v <= 255 && (part.len() == 1 || !part.starts_with('0')) => o[i] = v,
_ => return false,
}
}
o[0] == 10
|| (o[0] == 172 && (16..=31).contains(&o[1]))
|| (o[0] == 192 && o[1] == 168)
|| (o[0] == 100 && (64..=127).contains(&o[1]))
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
fn ornith_toml() -> &'static str {
r#"
name = "Ornith-1.0-35B"
backend = "vllm"
footprint_gib = 35.0
[vllm]
served_name = "Ornith-1.0-35B"
max_model_len = 262144
reasoning_parser = "qwen3"
tool_call_parser = "qwen3_xml"
enable_auto_tool_choice = true
[tuning]
temperature = 0.6
top_p = 0.95
top_k = 20
context_tokens = 262144
[capability]
emits_leading_reasoning = true
thinking_default = true
reasoning_content_field = "reasoning_content"
"#
}
#[test]
fn backend_tokens_round_trip() {
for (tok, b) in [
("vllm", Backend::Vllm),
("ollama", Backend::Ollama),
("llama_cpp", Backend::LlamaCpp),
] {
assert_eq!(Backend::from_str(tok).unwrap(), b);
assert_eq!(b.as_str(), tok);
}
assert_eq!(Backend::from_str(" VLLM ").unwrap(), Backend::Vllm);
assert_eq!(Backend::from_str("llama.cpp").unwrap(), Backend::LlamaCpp);
assert!(Backend::from_str("tgi").is_err());
}
#[test]
fn parses_from_toml_and_yaml_identically() {
let from_toml = parse_card(ornith_toml(), "toml").expect("toml");
let yaml = r#"
name: Ornith-1.0-35B
backend: vllm
footprint_gib: 35.0
vllm:
served_name: Ornith-1.0-35B
max_model_len: 262144
reasoning_parser: qwen3
tool_call_parser: qwen3_xml
enable_auto_tool_choice: true
tuning:
temperature: 0.6
top_p: 0.95
top_k: 20
context_tokens: 262144
capability:
emits_leading_reasoning: true
thinking_default: true
reasoning_content_field: reasoning_content
"#;
let from_yaml = parse_card(yaml, "yml").expect("yaml");
assert_eq!(from_toml, from_yaml, "TOML and YAML must parse identically");
let reparsed = parse_card(&from_toml.to_toml().unwrap(), "toml").unwrap();
assert_eq!(reparsed, from_toml);
}
#[test]
fn capability_parses_turn_scoped_reasoning_replay() {
let card = parse_card(
r#"
name = "reasoning-model"
[capability]
reasoning_replay_scope = "current_user_turn"
"#,
"toml",
)
.expect("reasoning replay scope is a supported capability");
let capability = serde_json::to_value(card.capability.expect("capability present"))
.expect("capability serializes");
assert_eq!(capability["reasoning_replay_scope"], "current_user_turn");
}
#[test]
fn parse_rejects_unknown_extension_and_bad_syntax() {
assert!(parse_card(ornith_toml(), "json").is_err());
assert!(parse_card("name = \"x\"\nbogus_field = 1", "toml").is_err()); }
#[test]
fn overlay_wins_field_by_field_and_deep_merges() {
let base = parse_card(ornith_toml(), "toml").unwrap();
let overlay: ModelCard = toml::from_str(
"name = \"Ornith-1.0-35B\"\n[vllm]\nmax_model_len = 65536\n[tuning]\ntemperature = 1.0",
)
.unwrap();
let merged = base.clone().merge(overlay);
let v = merged.vllm.as_ref().unwrap();
assert_eq!(v.max_model_len, Some(65536));
assert_eq!(merged.tuning.as_ref().unwrap().temperature, Some(1.0));
assert_eq!(v.reasoning_parser.as_deref(), Some("qwen3"));
assert_eq!(v.tool_call_parser.as_deref(), Some("qwen3_xml"));
assert_eq!(merged.tuning.as_ref().unwrap().top_p, Some(0.95));
assert_eq!(
merged.capability.as_ref().unwrap().thinking_default,
Some(true)
);
}
#[test]
fn chat_completions_capability_deep_merges_field_by_field() {
let base: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"llama_cpp\"\n\
[capability.chat_completions]\ncognition = true\n\
chat_template_kwargs = true\nparallel_tool_calls = false\n",
)
.unwrap();
let overlay: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"llama_cpp\"\n\
[capability.chat_completions]\nbounded_reasoning_continuation = true\n",
)
.unwrap();
let merged = base.merge(overlay);
let capability = merged
.capability
.and_then(|capability| capability.chat_completions)
.expect("chat-completions capability survives the merge");
assert_eq!(capability.cognition, Some(true));
assert_eq!(capability.chat_template_kwargs, Some(true));
assert_eq!(capability.parallel_tool_calls, Some(false));
assert_eq!(capability.bounded_reasoning_continuation, Some(true));
}
fn dropin_source(key: &str, card: ModelCard) -> crate::card_catalog::CardSource {
crate::card_catalog::CardSource {
key: key.to_string(),
path: std::path::PathBuf::from(format!("/cards/{key}.toml")),
parsed: Ok(card),
}
}
#[test]
fn family_defaults_returns_known_family_case_insensitively() {
for name in ["qwen3", "QWEN3", "Qwen3"] {
let d = family_defaults(name).unwrap_or_else(|| panic!("{name} should be known"));
let v = d.vllm.unwrap();
assert_eq!(v.reasoning_parser.as_deref(), Some("qwen3"));
assert_eq!(v.tool_call_parser.as_deref(), Some("qwen3_xml"));
assert_eq!(v.enable_auto_tool_choice, Some(true));
}
}
#[test]
fn family_defaults_is_none_for_an_unknown_family() {
assert!(family_defaults("nemotron").is_none());
assert!(family_defaults("bogus").is_none());
}
#[test]
fn finalize_fills_vllm_gaps_from_family_defaults() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"qwen3\"\n\
[vllm]\nserved_name = \"test\"\nmax_model_len = 8192\n",
)
.unwrap();
let resolved = crate::card_catalog::finalize(card).unwrap();
let v = resolved.vllm.unwrap();
assert_eq!(v.served_name.as_deref(), Some("test"));
assert_eq!(v.max_model_len, Some(8192));
assert_eq!(v.reasoning_parser.as_deref(), Some("qwen3"));
assert_eq!(v.tool_call_parser.as_deref(), Some("qwen3_xml"));
assert_eq!(v.enable_auto_tool_choice, Some(true));
}
#[test]
fn finalize_cards_own_vllm_field_wins_over_family_default() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"qwen3\"\n\
[vllm]\ntool_call_parser = \"custom_xml\"\n",
)
.unwrap();
let resolved = crate::card_catalog::finalize(card).unwrap();
let v = resolved.vllm.unwrap();
assert_eq!(
v.tool_call_parser.as_deref(),
Some("custom_xml"),
"the card's own declaration overrides the family default, never the reverse"
);
assert_eq!(
v.reasoning_parser.as_deref(),
Some("qwen3"),
"a field the card didn't set still inherits from the family"
);
}
#[test]
fn finalize_with_no_family_is_unaffected_by_family_defaults() {
let card: ModelCard =
toml::from_str("name = \"test\"\nbackend = \"vllm\"\n[vllm]\nserved_name = \"test\"\n")
.unwrap();
let resolved = crate::card_catalog::finalize(card).unwrap();
assert_eq!(resolved.vllm.unwrap().reasoning_parser, None);
}
#[test]
fn finalize_accepts_an_arbitrary_family_without_a_defaults_profile() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"nemotron\"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
let resolved = crate::card_catalog::finalize(card).expect("identity needs no profile");
assert_eq!(resolved.family.as_deref(), Some("nemotron"));
let vllm = resolved.vllm.expect("own block kept");
assert_eq!(vllm.served_name.as_deref(), Some("test"));
assert_eq!(
vllm.reasoning_parser, None,
"no defaults profile ⇒ no serving defaults applied"
);
}
#[test]
fn finalize_rejects_an_empty_family_identity() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \" \"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
let err = crate::card_catalog::finalize(card).expect_err("empty family");
assert!(err.contains("family"), "{err}");
}
#[test]
fn validate_accepts_an_arbitrary_family_identity() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"qwenn3\"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
card.validate().expect("an explicit family is an identity");
let empty: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"\"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
let err = empty.validate().expect_err("empty family is no identity");
assert!(err.contains("family"), "{err}");
}
#[test]
fn validate_accepts_a_known_family_name() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"qwen3\"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
assert!(card.validate().is_ok());
}
#[test]
fn validate_accepts_a_well_formed_card() {
assert!(parse_card(ornith_toml(), "toml")
.unwrap()
.validate()
.is_ok());
}
#[test]
fn validate_rejects_empty_name_missing_backend_and_missing_block() {
let mut c = parse_card(ornith_toml(), "toml").unwrap();
c.name = " ".into();
assert!(c.validate().is_err(), "empty name");
let no_backend: ModelCard = toml::from_str("name = \"X\"").unwrap();
assert!(no_backend.validate().is_err(), "no backend");
let vllm_no_block: ModelCard = toml::from_str("name = \"X\"\nbackend = \"vllm\"").unwrap();
assert!(vllm_no_block.validate().is_err(), "backend=vllm, no [vllm]");
let ollama_no_block: ModelCard =
toml::from_str("name = \"X\"\nbackend = \"ollama\"").unwrap();
assert!(
ollama_no_block.validate().is_err(),
"backend=ollama, no [ollama]"
);
}
#[test]
fn no_hardware_leak_flags_a_private_ip_and_passes_clean_cards() {
let clean = parse_card(ornith_toml(), "toml").unwrap();
assert!(
no_hardware_leak(&clean).is_empty(),
"identities-only card is clean"
);
let ip = format!("{}.{}.{}.{}", 192, 168, 1, 100);
let leaky: ModelCard = toml::from_str(&format!(
"name = \"X\"\nbackend = \"vllm\"\n[vllm]\nserved_name = \"http://{ip}:8000\""
))
.unwrap();
assert_eq!(no_hardware_leak(&leaky), vec![ip]);
}
#[test]
fn is_private_ipv4_ranges() {
for o in [
[10, 0, 0, 1],
[172, 16, 0, 1],
[172, 31, 255, 255],
[192, 168, 1, 1],
[100, 64, 0, 1],
] {
let ip = format!("{}.{}.{}.{}", o[0], o[1], o[2], o[3]);
assert!(is_private_ipv4(&ip), "{ip} is private");
}
for ip in [
"8.8.8.8",
"172.15.0.1",
"172.32.0.1",
"1.2.3",
"256.1.1.1",
"01.2.3.4",
] {
assert!(!is_private_ipv4(ip), "{ip} is NOT flagged");
}
}
#[test]
fn builtin_cards_parse_validate_and_are_leak_free() {
let cards = builtin_cards();
assert!(!cards.is_empty(), "built-in cards present");
for c in &cards {
c.validate()
.unwrap_or_else(|e| panic!("built-in `{}` invalid: {e}", c.name));
assert!(
no_hardware_leak(c).is_empty(),
"built-in `{}` leaks a private IP",
c.name
);
if let Some(v) = c.vllm.as_ref() {
assert!(v.gpu_mem.is_none(), "{}: gpu_mem is node-specific", c.name);
assert!(
v.tensor_parallel.is_none(),
"{}: tensor_parallel is node-specific",
c.name
);
}
}
}
#[test]
fn ornith_35b_card_has_the_expected_settings() {
let c = crate::card_catalog::ModelCardCatalog::new(builtin_card_entries(), vec![], None)
.resolve_exact("Ornith-1.0-35B")
.expect("Ornith-1.0-35B present");
assert_eq!(c.backend, Some(Backend::Vllm));
assert_eq!(c.gated, None, "the 35B is runnable, not gated");
let v = c.vllm.unwrap();
assert_eq!(v.max_model_len, Some(262_144));
assert_eq!(v.reasoning_parser.as_deref(), Some("qwen3"));
assert_eq!(v.tool_call_parser.as_deref(), Some("qwen3_xml"));
assert_eq!(v.enable_auto_tool_choice, Some(true));
assert!(v.extra.iter().any(|a| a == "--enable-prefix-caching"));
assert!(v.extra.iter().any(|a| a == "--trust-remote-code"));
let t = c.tuning.unwrap();
assert_eq!(t.temperature, Some(0.6));
assert_eq!(t.top_p, Some(0.95));
assert_eq!(t.top_k, Some(20));
assert_eq!(t.context_tokens, Some(262_144));
let cap = c.capability.unwrap();
assert_eq!(cap.emits_leading_reasoning, Some(true));
assert_eq!(cap.thinking_default, Some(true));
assert_eq!(
cap.reasoning_content_field.as_deref(),
Some("reasoning_content")
);
let o = c.ollama.unwrap();
assert_eq!(o.tag.as_deref(), Some("ornith:35b"));
assert!(
o.num_ctx.unwrap() < 262_144,
"ollama num_ctx is capped below the native window (OOM guard)"
);
}
#[test]
fn ornith_397b_is_gated() {
let c = builtin_card("Ornith-1.0-397B").expect("397B present as a gated card");
assert_eq!(c.gated, Some(true), "the 397B must be hardware-gated");
}
#[test]
fn builtin_card_is_overridable_by_name() {
let dropin: ModelCard =
toml::from_str("name = \"Ornith-1.0-35B\"\n[ollama]\nnum_ctx = 65536").unwrap();
let resolved = crate::card_catalog::ModelCardCatalog::new(
builtin_card_entries(),
vec![dropin_source("ornith-1.0-35b", dropin)],
None,
)
.resolve_exact("Ornith-1.0-35B")
.expect("resolves");
assert_eq!(
resolved.ollama.unwrap().num_ctx,
Some(65536),
"drop-in wins"
);
assert_eq!(
resolved.vllm.unwrap().reasoning_parser.as_deref(),
Some("qwen3"),
"un-set fields inherit from the built-in"
);
}
}