#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ModelCap {
pub name: &'static str,
pub context_window: u32,
pub is_default: bool,
}
const ANTHROPIC: &[ModelCap] = &[ModelCap {
name: "claude-3-5-haiku-latest",
context_window: 200_000,
is_default: true,
}];
const OPENAI: &[ModelCap] = &[ModelCap {
name: "gpt-4o-mini",
context_window: 128_000,
is_default: true,
}];
const GEMINI: &[ModelCap] = &[ModelCap {
name: "gemini-2.5-flash",
context_window: 1_048_576,
is_default: true,
}];
const KIMI: &[ModelCap] = &[
ModelCap {
name: "moonshot-v1-8k",
context_window: 8_192,
is_default: true,
},
ModelCap {
name: "moonshot-v1-32k",
context_window: 32_768,
is_default: false,
},
ModelCap {
name: "moonshot-v1-128k",
context_window: 131_072,
is_default: false,
},
];
const GLM: &[ModelCap] = &[ModelCap {
name: "glm-4-plus",
context_window: 128_000,
is_default: true,
}];
const OPENROUTER: &[ModelCap] = &[ModelCap {
name: "openai/gpt-4o-mini",
context_window: 128_000,
is_default: true,
}];
const OLLAMA: &[ModelCap] = &[ModelCap {
name: "llama3.1:8b",
context_window: 131_072,
is_default: true,
}];
pub fn models_for(backend: &str) -> &'static [ModelCap] {
match backend {
"anthropic" => ANTHROPIC,
"openai" => OPENAI,
"gemini" => GEMINI,
"kimi" => KIMI,
"glm" => GLM,
"openrouter" => OPENROUTER,
"ollama" => OLLAMA,
_ => &[],
}
}
pub fn context_window(backend: &str, model: &str) -> Option<u32> {
models_for(backend)
.iter()
.find(|m| m.name == model)
.map(|m| m.context_window)
}
pub fn max_canonical_context_window() -> u32 {
crate::backends::CANONICAL_PROVIDERS
.iter()
.flat_map(|p| models_for(p))
.map(|m| m.context_window)
.max()
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backends::CANONICAL_PROVIDERS;
#[test]
fn every_canonical_provider_has_a_catalog_with_exactly_one_default() {
for p in CANONICAL_PROVIDERS {
let models = models_for(p);
assert!(!models.is_empty(), "{p} must have a catalog");
let defaults = models.iter().filter(|m| m.is_default).count();
assert_eq!(defaults, 1, "{p} must have EXACTLY one default model");
}
}
#[test]
fn catalog_default_matches_each_backend_default_model() {
use crate::backends::Backend;
let pairs: Vec<(&str, Box<dyn Backend>)> = vec![
("anthropic", Box::new(crate::backends::AnthropicBackend::with_api_key(Some("k".into())))),
("openai", Box::new(crate::backends::OpenAIBackend::with_api_key(Some("k".into())))),
("gemini", Box::new(crate::backends::GeminiBackend::with_api_key(Some("k".into())))),
("kimi", Box::new(crate::backends::KimiBackend::with_api_key(Some("k".into())))),
("glm", Box::new(crate::backends::GLMBackend::with_api_key(Some("k".into())))),
("openrouter", Box::new(crate::backends::OpenRouterBackend::with_api_key(Some("k".into())))),
("ollama", Box::new(crate::backends::OllamaBackend::with_api_key(Some("k".into())))),
];
for (name, backend) in pairs {
let catalog_default = models_for(name)
.iter()
.find(|m| m.is_default)
.map(|m| m.name)
.unwrap_or("<none>");
assert_eq!(
catalog_default,
backend.default_model(),
"{name}: catalog default must equal the live backend default"
);
}
}
#[test]
fn kimi_carries_the_full_moonshot_context_ladder() {
assert_eq!(context_window("kimi", "moonshot-v1-8k"), Some(8_192));
assert_eq!(context_window("kimi", "moonshot-v1-32k"), Some(32_768));
assert_eq!(context_window("kimi", "moonshot-v1-128k"), Some(131_072));
}
#[test]
fn context_window_is_strictly_increasing_within_a_provider_ladder() {
for p in CANONICAL_PROVIDERS {
let windows: Vec<u32> = models_for(p).iter().map(|m| m.context_window).collect();
let mut sorted = windows.clone();
sorted.sort_unstable();
assert_eq!(windows, sorted, "{p} catalog must be smallest-window-first");
}
}
#[test]
fn unknown_backend_has_empty_catalog_and_no_window() {
assert!(models_for("custom-self-hosted").is_empty());
assert_eq!(context_window("custom-self-hosted", "x"), None);
assert_eq!(context_window("kimi", "moonshot-v1-512k"), None);
}
#[test]
fn max_canonical_window_is_the_largest_known() {
assert_eq!(max_canonical_context_window(), 1_048_576);
}
}