pub const KNOWN_MODELS: &[(&str, &str)] = &[
("claude-sonnet-4-6", "Sonnet 4.6 — balanced (default)"),
("claude-opus-4-7", "Opus 4.7 — most capable"),
("claude-opus-4-6", "Opus 4.6 — previous flagship"),
("claude-haiku-4-5-20251001", "Haiku 4.5 — fast"),
];
pub fn default_model() -> &'static str {
KNOWN_MODELS[0].0
}
pub fn model_supports_adaptive_thinking(model: &str) -> bool {
let m = model.to_ascii_lowercase();
if m.contains("opus-4-7") || m.contains("opus-4-8") || m.contains("opus-4-9") {
return true;
}
if m.contains("sonnet-4-7") || m.contains("sonnet-4-8") || m.contains("sonnet-4-9") {
return true;
}
if m.contains("opus-5") || m.contains("sonnet-5") || m.contains("haiku-5") {
return true;
}
false
}
pub fn effort_for_thinking_level(level: &str) -> Option<&'static str> {
match level {
"low" => Some("low"),
"medium" | "med" => Some("medium"),
"high" => Some("high"),
"xhigh" => Some("xhigh"),
"adaptive" => None, _ => Some("high"), }
}
pub fn thinking_level_for_budget(budget: u32) -> &'static str {
match budget {
0 => "adaptive",
1..=2048 => "low",
2049..=4096 => "medium",
4097..=16384 => "high",
_ => "xhigh",
}
}
pub const DEFAULT_LEGACY_ADAPTIVE_FALLBACK: u32 = 16384;
pub fn model_supports_1m(model: &str) -> bool {
let m = model.to_ascii_lowercase();
m.contains("opus-4-6") || m.contains("opus-4-7")
|| m.contains("opus-4-8") || m.contains("opus-4-9")
|| m.contains("sonnet-4")
|| m.contains("opus-5") || m.contains("sonnet-5")
}
pub fn context_window_for_model(_model: &str) -> u64 {
200_000
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_model_is_first_entry() {
assert_eq!(default_model(), KNOWN_MODELS[0].0);
}
#[test]
fn known_models_has_expected_ids() {
let ids: Vec<&str> = KNOWN_MODELS.iter().map(|(id, _)| *id).collect();
assert!(ids.contains(&"claude-opus-4-7"));
assert!(ids.contains(&"claude-sonnet-4-6"));
assert!(ids.contains(&"claude-haiku-4-5-20251001"));
}
#[test]
fn descriptions_are_non_empty() {
for (_, desc) in KNOWN_MODELS {
assert!(!desc.is_empty(), "empty description");
}
}
#[test]
fn context_window_default_is_200k_for_all_models() {
assert_eq!(context_window_for_model("claude-opus-4-7"), 200_000);
assert_eq!(context_window_for_model("claude-opus-4-6"), 200_000);
assert_eq!(context_window_for_model("claude-sonnet-4-6"), 200_000);
assert_eq!(context_window_for_model("claude-opus-4-5-20251101"), 200_000);
assert_eq!(context_window_for_model("claude-haiku-4-5-20251001"), 200_000);
}
#[test]
fn model_supports_1m_for_opus_46_and_sonnet_4() {
assert!(model_supports_1m("claude-opus-4-6"));
assert!(model_supports_1m("claude-opus-4-7"));
assert!(model_supports_1m("claude-sonnet-4-6"));
assert!(model_supports_1m("claude-sonnet-4-5-20250929"));
}
#[test]
fn model_supports_1m_rejects_older_and_haiku() {
assert!(!model_supports_1m("claude-opus-4-5"));
assert!(!model_supports_1m("claude-haiku-4-5-20251001"));
assert!(!model_supports_1m("claude-opus-3-5"));
}
#[test]
fn model_supports_1m_assumed_for_5x() {
assert!(model_supports_1m("claude-opus-5-0"));
assert!(model_supports_1m("claude-sonnet-5-1"));
}
#[test]
fn model_supports_1m_case_insensitive() {
assert!(model_supports_1m("CLAUDE-OPUS-4-6"));
}
#[test]
fn context_window_haiku_is_200k() {
assert_eq!(context_window_for_model("claude-haiku-4-5-20251001"), 200_000);
}
#[test]
fn context_window_opus3_is_200k() {
assert_eq!(context_window_for_model("claude-opus-3-5-20250101"), 200_000);
}
#[test]
fn context_window_unknown_defaults_200k() {
assert_eq!(context_window_for_model("some-future-model"), 200_000);
assert_eq!(context_window_for_model(""), 200_000);
}
#[test]
fn adaptive_thinking_required_for_opus_4_7_plus() {
assert!(model_supports_adaptive_thinking("claude-opus-4-7"));
assert!(model_supports_adaptive_thinking("claude-opus-4-8"));
}
#[test]
fn adaptive_thinking_not_for_opus_4_6() {
assert!(!model_supports_adaptive_thinking("claude-opus-4-6"));
}
#[test]
fn adaptive_thinking_not_for_sonnet_4_6() {
assert!(!model_supports_adaptive_thinking("claude-sonnet-4-6"));
}
#[test]
fn adaptive_thinking_required_for_sonnet_4_7_plus() {
assert!(model_supports_adaptive_thinking("claude-sonnet-4-7"));
}
#[test]
fn adaptive_thinking_not_for_older_models() {
assert!(!model_supports_adaptive_thinking("claude-opus-4-5"));
assert!(!model_supports_adaptive_thinking("claude-sonnet-4-5"));
assert!(!model_supports_adaptive_thinking("claude-haiku-4-5-20251001"));
assert!(!model_supports_adaptive_thinking("claude-opus-3-5"));
}
#[test]
fn adaptive_thinking_assumed_for_5x() {
assert!(model_supports_adaptive_thinking("claude-opus-5-0"));
assert!(model_supports_adaptive_thinking("claude-sonnet-5-1"));
}
#[test]
fn adaptive_thinking_case_insensitive() {
assert!(model_supports_adaptive_thinking("CLAUDE-OPUS-4-7"));
}
}