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, 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>,
}
impl Capability {
#[must_use]
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),
}
}
}
#[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_defaults(family).is_none() {
return Err(format!(
"model card `{}`: family `{family}` has no known defaults \
(check for a typo, or add cards/families/{family}.toml)",
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
}
#[must_use]
pub fn resolve(builtin: ModelCard, dropins: &[ModelCard], one_off: Option<ModelCard>) -> ModelCard {
let mut card = builtin;
if let Some(family) = card.family.clone() {
if let Some(defaults) = family_defaults(&family) {
card.vllm = merge_opt(defaults.vllm, card.vllm, VllmProfile::merge);
}
}
let name = card.name.clone();
for d in dropins.iter().filter(|d| d.name == name) {
card = card.merge(d.clone());
}
if let Some(o) = one_off {
card = card.merge(o);
}
card
}
#[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> {
const EMBEDDED: &[&str] = &[
include_str!("cards/ornith-1.0-35b.toml"),
include_str!("cards/ornith-1.0-397b.toml"),
];
EMBEDDED
.iter()
.map(|s| parse_card(s, "toml").expect("built-in card is valid TOML"))
.collect()
}
#[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 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 resolve_applies_precedence_builtin_dropin_oneoff() {
let builtin = parse_card(ornith_toml(), "toml").unwrap();
let dropin: ModelCard =
toml::from_str("name = \"Ornith-1.0-35B\"\n[vllm]\ngpu_mem = 0.9").unwrap();
let other: ModelCard =
toml::from_str("name = \"Other\"\n[vllm]\nmax_model_len = 1").unwrap();
let one_off: ModelCard =
toml::from_str("name = \"Ornith-1.0-35B\"\n[vllm]\nmax_model_len = 131072").unwrap();
let card = resolve(builtin, &[dropin, other], Some(one_off));
let v = card.vllm.unwrap();
assert_eq!(v.gpu_mem, Some(0.9), "drop-in applied");
assert_eq!(v.max_model_len, Some(131072), "one-off wins over built-in");
assert_eq!(
v.reasoning_parser.as_deref(),
Some("qwen3"),
"base inherited"
);
}
#[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 resolve_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 = resolve(card, &[], None);
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 resolve_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 = resolve(card, &[], None);
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 resolve_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 = resolve(card, &[], None);
assert_eq!(resolved.vllm.unwrap().reasoning_parser, None);
}
#[test]
fn resolve_with_an_unknown_family_leaves_vllm_unchanged() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"bogus\"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
let resolved = resolve(card, &[], None);
assert_eq!(resolved.vllm.unwrap().served_name.as_deref(), Some("test"));
}
#[test]
fn validate_rejects_an_unknown_family_name() {
let card: ModelCard = toml::from_str(
"name = \"test\"\nbackend = \"vllm\"\nfamily = \"qwenn3\"\n\
[vllm]\nserved_name = \"test\"\n",
)
.unwrap();
let err = card
.validate()
.expect_err("unknown family must be rejected");
assert!(err.contains("qwenn3"), "{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 raw = builtin_card("Ornith-1.0-35B").expect("Ornith-1.0-35B present");
let c = resolve(raw, &[], None);
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 base = builtin_card("Ornith-1.0-35B").unwrap();
let dropin: ModelCard =
toml::from_str("name = \"Ornith-1.0-35B\"\n[ollama]\nnum_ctx = 65536").unwrap();
let resolved = resolve(base, std::slice::from_ref(&dropin), None);
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"
);
}
}