const DEFAULT_CONTEXT: usize = 128_000;
const MIN_CONTEXT: usize = 4_096;
pub fn context_window_for_model(model: &str) -> usize {
let m = model.to_lowercase();
if m.ends_with("-1m") && m.contains("claude") {
let base = &m[..m.len() - 3]; if base.starts_with("claude-opus-4-6")
|| base.starts_with("claude-sonnet-4-6")
|| base.starts_with("claude-sonnet-4-5")
|| base.starts_with("claude-sonnet-4-2")
|| base.starts_with("claude-sonnet-4")
{
return 1_000_000;
}
tracing::warn!(
"Model '{}' does not support 1M extended context; using 200K.",
model
);
return 200_000;
}
if m.starts_with("claude-opus-4")
|| m.starts_with("claude-sonnet-4")
|| m.starts_with("claude-haiku-4")
{
return 200_000;
}
if m.starts_with("claude-3.5") || m.starts_with("claude-3-5") {
return 200_000;
}
if m.starts_with("claude-3") {
return 200_000;
}
if m.contains("claude") {
tracing::warn!(
"Unknown Claude model '{}' — assuming 200K context. \
Update model_context.rs if this model has a different context window.",
model
);
return 200_000;
}
if m.starts_with("gpt-4o") || m.starts_with("gpt-4.1") || m.starts_with("chatgpt-4o") {
return 128_000;
}
if m.starts_with("gpt-4-turbo") || m.starts_with("gpt-4-1106") || m.starts_with("gpt-4-0125") {
return 128_000;
}
if m.starts_with("gpt-4") {
return 8_192;
}
if m.starts_with("gpt-3.5-turbo-16k") {
return 16_384;
}
if m.starts_with("gpt-3.5") {
return 16_384;
}
if m.starts_with("o1") || m.starts_with("o3") || m.starts_with("o4") {
return 200_000;
}
if m.contains("gemini-2.5") {
return 1_048_576;
}
if m.contains("gemini-2.0") {
return 1_048_576;
}
if m.contains("gemini-1.5-pro") {
return 2_097_152;
}
if m.contains("gemini-1.5-flash") {
return 1_048_576;
}
if m.contains("gemini") {
return 1_048_576;
}
if m.starts_with("grok-3") {
return 131_072;
}
if m.starts_with("grok") {
return 131_072;
}
if m.contains("deepseek") {
return 128_000;
}
if m.contains("mistral-large") {
return 128_000;
}
if m.contains("mistral-medium") {
return 32_000;
}
if m.contains("mistral-small") || m.contains("mistral-7b") {
return 32_000;
}
if m.contains("mixtral") || m.contains("mistral") {
return 32_000;
}
if m.contains("llama-3.3") || m.contains("llama-3.1") {
return 128_000;
}
if m.contains("llama-3") || m.contains("llama3") {
return 8_192;
}
if m.contains("llama") {
return 4_096;
}
if m.contains("qwen-2.5") || m.contains("qwen2.5") {
return 128_000;
}
if m.contains("qwen") {
return 32_000;
}
if m == "auto-detect" {
return MIN_CONTEXT;
}
DEFAULT_CONTEXT
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_claude_models() {
assert_eq!(context_window_for_model("claude-sonnet-4-6"), 200_000);
assert_eq!(context_window_for_model("claude-opus-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
);
assert_eq!(
context_window_for_model("claude-sonnet-4-5-20250929"),
200_000
);
assert_eq!(context_window_for_model("claude-opus-4-20250514"), 200_000);
assert_eq!(
context_window_for_model("claude-sonnet-4-20250514"),
200_000
);
assert_eq!(context_window_for_model("claude-3-opus-20240229"), 200_000);
assert_eq!(context_window_for_model("claude-3-haiku-20240307"), 200_000);
assert_eq!(
context_window_for_model("claude-3-5-sonnet-20240620"),
200_000
);
}
#[test]
fn test_claude_1m_virtual_models() {
assert_eq!(context_window_for_model("claude-sonnet-4-6-1m"), 1_000_000);
assert_eq!(context_window_for_model("claude-opus-4-6-1m"), 1_000_000);
assert_eq!(
context_window_for_model("claude-sonnet-4-5-20250929-1m"),
1_000_000
);
assert_eq!(
context_window_for_model("claude-sonnet-4-20250514-1m"),
1_000_000
);
}
#[test]
fn test_claude_1m_ineligible_models() {
assert_eq!(
context_window_for_model("claude-opus-4-5-20251101-1m"),
200_000
);
assert_eq!(
context_window_for_model("claude-haiku-4-5-20251001-1m"),
200_000
);
assert_eq!(
context_window_for_model("claude-3-opus-20240229-1m"),
200_000
);
}
#[test]
fn test_gpt4o_models() {
assert_eq!(context_window_for_model("gpt-4o"), 128_000);
assert_eq!(context_window_for_model("gpt-4o-mini"), 128_000);
assert_eq!(context_window_for_model("gpt-4.1"), 128_000);
}
#[test]
fn test_gpt4_legacy() {
assert_eq!(context_window_for_model("gpt-4"), 8_192);
assert_eq!(context_window_for_model("gpt-4-0613"), 8_192);
}
#[test]
fn test_gpt4_turbo() {
assert_eq!(context_window_for_model("gpt-4-turbo"), 128_000);
assert_eq!(context_window_for_model("gpt-4-turbo-preview"), 128_000);
}
#[test]
fn test_o_series() {
assert_eq!(context_window_for_model("o1"), 200_000);
assert_eq!(context_window_for_model("o1-preview"), 200_000);
assert_eq!(context_window_for_model("o3-mini"), 200_000);
assert_eq!(context_window_for_model("o4-mini"), 200_000);
}
#[test]
fn test_gemini_models() {
assert_eq!(context_window_for_model("gemini-2.0-flash"), 1_048_576);
assert_eq!(context_window_for_model("gemini-2.5-pro"), 1_048_576);
assert_eq!(context_window_for_model("gemini-1.5-pro"), 2_097_152);
}
#[test]
fn test_deepseek() {
assert_eq!(context_window_for_model("deepseek-chat"), 128_000);
assert_eq!(context_window_for_model("deepseek-coder"), 128_000);
}
#[test]
fn test_llama_models() {
assert_eq!(context_window_for_model("llama-3.3-70b-versatile"), 128_000);
assert_eq!(context_window_for_model("llama-3-8b"), 8_192);
}
#[test]
fn test_auto_detect_is_conservative() {
assert_eq!(context_window_for_model("auto-detect"), MIN_CONTEXT);
}
#[test]
fn test_unknown_model_gets_default() {
assert_eq!(
context_window_for_model("some-random-model"),
DEFAULT_CONTEXT
);
}
#[test]
fn test_case_insensitive() {
assert_eq!(context_window_for_model("Claude-Sonnet-4-6"), 200_000);
assert_eq!(context_window_for_model("GPT-4O"), 128_000);
}
}