pub(crate) const MODELS: &[(&str, &str, &str)] = &[
(localharness::types::DEFAULT_MODEL, "Gemini (default)", "the platform default"),
("claude-haiku-4-5-20251001", "Claude Haiku", "needs the anthropic-feature build"),
("claude-sonnet-4-6", "Claude Sonnet", "needs the anthropic-feature build"),
("claude-opus-4-8", "Claude Opus", "needs the anthropic-feature build"),
("gpt-5-nano", "GPT-5 nano", "needs the openai-feature build"),
("gpt-5-mini", "GPT-5 mini", "needs the openai-feature build"),
("gpt-5.1", "GPT-5.1", "needs the openai-feature build"),
("gpt-5-pro", "GPT-5 pro", "needs the openai-feature build"),
];
pub(crate) fn format_models() -> String {
let mut out = String::from("available --model ids (for `call` / `mcp-call`):\n");
for (id, label, note) in MODELS {
out.push_str(&format!(" {id}\n {label} — {note}\n"));
}
out.push_str(
"\nuse with: localharness call --model <id> <name> \"…\"\n\
claude-* → Anthropic, gpt-* → OpenAI, gemini-* (default) → Gemini; \
the credit proxy routes all three on $LH.\n",
);
out
}
pub(crate) fn models() -> i32 {
print!("{}", format_models());
0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn models_lists_gemini_default_and_claude_and_gpt_ids() {
let out = format_models();
assert!(out.contains(localharness::types::DEFAULT_MODEL));
assert!(out.contains("default"));
assert!(out.contains("claude-haiku-4-5-20251001"));
assert!(out.contains("claude-sonnet-4-6"));
assert!(out.contains("claude-opus-4-8"));
assert!(out.contains("anthropic"));
assert!(out.contains("gpt-5-nano"));
assert!(out.contains("gpt-5.1"));
assert!(out.contains("openai"));
}
#[test]
fn models_match_canonical_constants() {
let ids: Vec<&str> = MODELS.iter().map(|(id, _, _)| *id).collect();
assert!(ids.contains(&localharness::types::DEFAULT_MODEL));
}
#[cfg(feature = "anthropic")]
#[test]
fn anthropic_ids_match_backend_wire_constants() {
let ids: Vec<&str> = MODELS.iter().map(|(id, _, _)| *id).collect();
assert!(ids.contains(&localharness::backends::anthropic::DEFAULT_MODEL));
assert!(ids.contains(&localharness::backends::anthropic::SONNET_MODEL));
assert!(ids.contains(&localharness::backends::anthropic::OPUS_MODEL));
}
#[cfg(feature = "openai")]
#[test]
fn openai_ids_match_backend_wire_constants() {
let ids: Vec<&str> = MODELS.iter().map(|(id, _, _)| *id).collect();
assert!(ids.contains(&localharness::backends::openai::DEFAULT_MODEL));
assert!(ids.contains(&localharness::backends::openai::MINI_MODEL));
assert!(ids.contains(&localharness::backends::openai::PRO_MODEL));
}
fn cli_non_gemini_ids() -> std::collections::BTreeSet<&'static str> {
MODELS
.iter()
.map(|(id, _, _)| *id)
.filter(|id| id.starts_with("claude-") || id.starts_with("gpt-"))
.collect()
}
fn proxy_claude_gpt_keys(rel: &str) -> Option<std::collections::BTreeSet<String>> {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(rel);
let src = std::fs::read_to_string(&path).ok()?;
let mut out = std::collections::BTreeSet::new();
for line in src.lines() {
let Some(rest) = line.trim_start().strip_prefix('\'') else { continue };
let Some(end) = rest.find('\'') else { continue };
let id = &rest[..end];
if rest[end + 1..].trim_start().starts_with(':')
&& (id.starts_with("claude-") || id.starts_with("gpt-"))
{
out.insert(id.to_string());
}
}
Some(out)
}
#[test]
fn proxy_price_table_matches_cli_models() {
let Some(proxy) = proxy_claude_gpt_keys("proxy/api/_prices.ts") else {
eprintln!("skip: proxy/api/_prices.ts not present (packaged crate?)");
return;
};
let proxy_refs: std::collections::BTreeSet<&str> = proxy.iter().map(String::as_str).collect();
assert_eq!(
cli_non_gemini_ids(),
proxy_refs,
"proxy _prices.ts model ids drifted from CLI MODELS: {proxy_refs:?}"
);
}
#[test]
fn proxy_usage_table_matches_cli_models() {
let Some(proxy) = proxy_claude_gpt_keys("proxy/api/_usage.ts") else {
eprintln!("skip: proxy/api/_usage.ts not present (packaged crate?)");
return;
};
let proxy_refs: std::collections::BTreeSet<&str> = proxy.iter().map(String::as_str).collect();
assert_eq!(
cli_non_gemini_ids(),
proxy_refs,
"proxy _usage.ts rate-table ids drifted from CLI MODELS: {proxy_refs:?}"
);
}
}