Skip to main content

vtcode_config/constants/models/
anthropic.rs

1// Claude 5.x series - Latest Anthropic models
2pub const DEFAULT_MODEL: &str = "claude-sonnet-5";
3pub const SUPPORTED_MODELS: &[&str] = &[
4    "claude-sonnet-5-5", // Latest Sonnet: best speed/intelligence balance, 1M context, between_tools is the lowest thinking setting
5    "claude-sonnet-5",   // Previous Sonnet generation with adaptive thinking on by default
6    "claude-haiku-5-5", // High-volume latency-sensitive work: classification, routing, extraction, subagents; adaptive thinking, 1M context, 128K output
7    "claude-fable-5",   // Most capable widely released model
8    "claude-fable-5-1", // Successor to Fable 5, 1M context, cache reads at 1/4 cost
9    "claude-opus-5",    // Opus-tier premium flagship with adaptive thinking, 1M context
10    "claude-opus-5-5",  // Opus-tier successor with adaptive thinking always on, 1M context
11];
12
13// Convenience constants for alias models
14pub const CLAUDE_OPUS_5: &str = "claude-opus-5";
15pub const CLAUDE_OPUS_5_5: &str = "claude-opus-5-5";
16pub const CLAUDE_SONNET_5: &str = "claude-sonnet-5";
17pub const CLAUDE_SONNET_5_5: &str = "claude-sonnet-5-5";
18pub const CLAUDE_HAIKU_5_5: &str = "claude-haiku-5-5";
19pub const CLAUDE_FABLE_5: &str = "claude-fable-5";
20pub const CLAUDE_FABLE_5_1: &str = "claude-fable-5-1";
21
22/// Models that accept the reasoning effort parameter or extended thinking
23pub const REASONING_MODELS: &[&str] = &[
24    CLAUDE_SONNET_5_5,
25    CLAUDE_SONNET_5,
26    CLAUDE_HAIKU_5_5,
27    CLAUDE_FABLE_5,
28    CLAUDE_FABLE_5_1,
29    CLAUDE_OPUS_5,
30    CLAUDE_OPUS_5_5,
31];
32
33/// Minimum advisor model capability: the advisor must be at least Claude Sonnet 5.
34const ADVISOR_MIN_MODEL: &str = CLAUDE_SONNET_5;
35
36/// Returns the base model id with any `-YYYYMMDD` version suffix stripped.
37pub fn normalize_model_id(model: &str) -> &str {
38    if let Some(idx) = model.rfind('-') {
39        let suffix = &model[idx + 1..];
40        if suffix.len() == 8 && suffix.chars().all(|c| c.is_ascii_digit()) {
41            return &model[..idx];
42        }
43    }
44    model
45}
46
47/// Relative capability tier for advisor compatibility checks.
48///
49/// Higher is more capable. Used to enforce that the advisor model is at least as
50/// capable as the executor model. Self-advising models (Fable 5) are
51/// handled separately because they may only advise themselves.
52///
53/// Claude Sonnet 5.5 sits just above Sonnet 5: a Sonnet 5.5 executor rejects a
54/// Sonnet 5 advisor (400), so its tier must stay strictly above Sonnet 5's.
55fn advisor_tier(model: &str) -> Option<u8> {
56    match normalize_model_id(model) {
57        CLAUDE_HAIKU_5_5 => Some(2),
58        CLAUDE_SONNET_5 => Some(3),
59        CLAUDE_SONNET_5_5 => Some(4),
60        CLAUDE_OPUS_5 => Some(6),
61        CLAUDE_OPUS_5_5 => Some(7),
62        CLAUDE_FABLE_5 => Some(8),
63        CLAUDE_FABLE_5_1 => Some(8),
64        _ => None,
65    }
66}
67
68/// Validates that an executor/advisor model pair is permitted by the Anthropic
69/// advisor compatibility table.
70///
71/// Returns `Ok(())` when the pair is valid, or `Err(message)` describing the
72/// unsupported combination (matching the API's `400 invalid_request_error`).
73pub fn validate_advisor_pair(executor: &str, advisor: &str) -> Result<(), String> {
74    let executor_base = normalize_model_id(executor);
75    let advisor_base = normalize_model_id(advisor);
76
77    // Self-advising models can only advise themselves.
78    if advisor_base == CLAUDE_FABLE_5 && executor_base != CLAUDE_FABLE_5 {
79        return Err(format!("advisor model {advisor} may only advise {CLAUDE_FABLE_5}"));
80    }
81    if advisor_base == CLAUDE_FABLE_5_1 && executor_base != CLAUDE_FABLE_5_1 {
82        return Err(format!("advisor model {advisor} may only advise {CLAUDE_FABLE_5_1}"));
83    }
84
85    let Some(executor_tier) = advisor_tier(executor_base) else {
86        return Err(format!("executor model {executor} is not a supported advisor executor"));
87    };
88    let Some(chosen_advisor_tier) = advisor_tier(advisor_base) else {
89        return Err(format!("advisor model {advisor} is not a supported advisor model"));
90    };
91
92    if chosen_advisor_tier < advisor_tier(ADVISOR_MIN_MODEL).unwrap_or(3) {
93        return Err(format!(
94            "advisor model {advisor} is less capable than the minimum allowed advisor ({ADVISOR_MIN_MODEL})"
95        ));
96    }
97
98    if chosen_advisor_tier < executor_tier {
99        return Err(format!("advisor model {advisor} must be at least as capable as the executor model {executor}"));
100    }
101
102    Ok(())
103}
104
105/// Returns a reasonable default advisor model for a given executor model.
106///
107/// Falls back to `claude-opus-5` (the most broadly compatible advisor) when the
108/// executor is unknown or unversioned.
109pub fn default_advisor_model(executor: &str) -> &'static str {
110    match normalize_model_id(executor) {
111        CLAUDE_HAIKU_5_5 => CLAUDE_OPUS_5,
112        CLAUDE_SONNET_5 => CLAUDE_OPUS_5,
113        CLAUDE_SONNET_5_5 => CLAUDE_OPUS_5,
114        CLAUDE_OPUS_5 => CLAUDE_OPUS_5,
115        CLAUDE_OPUS_5_5 => CLAUDE_OPUS_5_5,
116        CLAUDE_FABLE_5 => CLAUDE_FABLE_5,
117        CLAUDE_FABLE_5_1 => CLAUDE_FABLE_5_1,
118        _ => CLAUDE_OPUS_5,
119    }
120}