agent_core/core/
models.rs1pub const KNOWN_MODELS: &[(&str, &str)] = &[
5 ("claude-sonnet-4-6", "Sonnet 4.6 — balanced (default)"),
6 ("claude-fable-5", "Fable 5 — latest"),
7 ("claude-opus-4-7", "Opus 4.7 — most capable"),
8 ("claude-opus-4-6", "Opus 4.6 — previous flagship"),
9 ("claude-haiku-4-5-20251001", "Haiku 4.5 — fast"),
10];
11
12pub fn default_model() -> &'static str {
13 KNOWN_MODELS[0].0
14}
15
16pub fn model_supports_adaptive_thinking(model: &str) -> bool {
28 let m = model.to_ascii_lowercase();
29 if m.contains("opus-4-7") || m.contains("opus-4-8") || m.contains("opus-4-9") {
34 return true;
35 }
36 if m.contains("sonnet-4-7") || m.contains("sonnet-4-8") || m.contains("sonnet-4-9") {
37 return true;
38 }
39 if m.contains("opus-5") || m.contains("sonnet-5") || m.contains("haiku-5") || m.contains("fable-5") {
41 return true;
42 }
43 false
44}
45
46pub fn effort_for_thinking_level(level: &str) -> Option<&'static str> {
53 match level {
54 "low" => Some("low"),
55 "medium" | "med" => Some("medium"),
56 "high" => Some("high"),
57 "xhigh" => Some("xhigh"),
58 "adaptive" => None, _ => Some("high"), }
61}
62
63pub fn thinking_level_for_budget(budget: u32) -> &'static str {
70 match budget {
71 0 => "adaptive",
72 1..=2048 => "low",
73 2049..=4096 => "medium",
74 4097..=16384 => "high",
75 _ => "xhigh",
76 }
77}
78
79pub fn budget_for_thinking_level(level: &str) -> Option<u32> {
84 match level {
85 "adaptive" | "off" => Some(0),
86 "low" => Some(2048),
87 "medium" => Some(4096),
88 "high" => Some(16384),
89 "xhigh" => Some(32768),
90 _ => None,
91 }
92}
93
94pub const DEFAULT_LEGACY_ADAPTIVE_FALLBACK: u32 = 16384;
97
98pub fn model_supports_1m(model: &str) -> bool {
106 let m = model.to_ascii_lowercase();
107 m.contains("opus-4-6") || m.contains("opus-4-7")
109 || m.contains("opus-4-8") || m.contains("opus-4-9")
110 || m.contains("sonnet-4")
111 || m.contains("opus-5") || m.contains("sonnet-5")
112 || m.contains("fable-5")
113}
114
115pub fn context_window_for_model(_model: &str) -> u64 {
129 200_000
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn default_model_is_first_entry() {
138 assert_eq!(default_model(), KNOWN_MODELS[0].0);
139 }
140
141 #[test]
142 fn known_models_has_expected_ids() {
143 let ids: Vec<&str> = KNOWN_MODELS.iter().map(|(id, _)| *id).collect();
144 assert!(ids.contains(&"claude-opus-4-7"));
145 assert!(ids.contains(&"claude-sonnet-4-6"));
146 assert!(ids.contains(&"claude-haiku-4-5-20251001"));
147 }
148
149 #[test]
150 fn descriptions_are_non_empty() {
151 for (_, desc) in KNOWN_MODELS {
152 assert!(!desc.is_empty(), "empty description");
153 }
154 }
155
156 #[test]
157 fn budget_for_thinking_level_round_trips() {
158 for budget in [0u32, 2048, 4096, 16384, 32768] {
159 let level = thinking_level_for_budget(budget);
160 assert_eq!(budget_for_thinking_level(level), Some(budget), "level {}", level);
161 }
162 assert_eq!(budget_for_thinking_level("off"), Some(0));
163 assert_eq!(budget_for_thinking_level("custom(8192)"), None);
164 }
165
166 #[test]
167 fn context_window_default_is_200k_for_all_models() {
168 assert_eq!(context_window_for_model("claude-opus-4-7"), 200_000);
170 assert_eq!(context_window_for_model("claude-opus-4-6"), 200_000);
171 assert_eq!(context_window_for_model("claude-sonnet-4-6"), 200_000);
172 assert_eq!(context_window_for_model("claude-opus-4-5-20251101"), 200_000);
173 assert_eq!(context_window_for_model("claude-haiku-4-5-20251001"), 200_000);
174 }
175
176 #[test]
177 fn model_supports_1m_for_opus_46_and_sonnet_4() {
178 assert!(model_supports_1m("claude-opus-4-6"));
179 assert!(model_supports_1m("claude-opus-4-7"));
180 assert!(model_supports_1m("claude-sonnet-4-6"));
181 assert!(model_supports_1m("claude-sonnet-4-5-20250929"));
182 }
183
184 #[test]
185 fn model_supports_1m_rejects_older_and_haiku() {
186 assert!(!model_supports_1m("claude-opus-4-5"));
187 assert!(!model_supports_1m("claude-haiku-4-5-20251001"));
188 assert!(!model_supports_1m("claude-opus-3-5"));
189 }
190
191 #[test]
192 fn model_supports_1m_assumed_for_5x() {
193 assert!(model_supports_1m("claude-opus-5-0"));
194 assert!(model_supports_1m("claude-sonnet-5-1"));
195 assert!(model_supports_1m("claude-fable-5"));
196 }
197
198 #[test]
199 fn model_supports_1m_case_insensitive() {
200 assert!(model_supports_1m("CLAUDE-OPUS-4-6"));
201 }
202
203 #[test]
204 fn context_window_haiku_is_200k() {
205 assert_eq!(context_window_for_model("claude-haiku-4-5-20251001"), 200_000);
206 }
207
208 #[test]
209 fn context_window_opus3_is_200k() {
210 assert_eq!(context_window_for_model("claude-opus-3-5-20250101"), 200_000);
211 }
212
213 #[test]
214 fn context_window_unknown_defaults_200k() {
215 assert_eq!(context_window_for_model("some-future-model"), 200_000);
216 assert_eq!(context_window_for_model(""), 200_000);
217 }
218
219 #[test]
220 fn adaptive_thinking_required_for_opus_4_7_plus() {
221 assert!(model_supports_adaptive_thinking("claude-opus-4-7"));
222 assert!(model_supports_adaptive_thinking("claude-opus-4-8"));
223 }
224
225 #[test]
226 fn adaptive_thinking_not_for_opus_4_6() {
227 assert!(!model_supports_adaptive_thinking("claude-opus-4-6"));
229 }
230
231 #[test]
232 fn adaptive_thinking_not_for_sonnet_4_6() {
233 assert!(!model_supports_adaptive_thinking("claude-sonnet-4-6"));
234 }
235
236 #[test]
237 fn adaptive_thinking_required_for_sonnet_4_7_plus() {
238 assert!(model_supports_adaptive_thinking("claude-sonnet-4-7"));
239 }
240
241 #[test]
242 fn adaptive_thinking_not_for_older_models() {
243 assert!(!model_supports_adaptive_thinking("claude-opus-4-5"));
244 assert!(!model_supports_adaptive_thinking("claude-sonnet-4-5"));
245 assert!(!model_supports_adaptive_thinking("claude-haiku-4-5-20251001"));
246 assert!(!model_supports_adaptive_thinking("claude-opus-3-5"));
247 }
248
249 #[test]
250 fn adaptive_thinking_assumed_for_5x() {
251 assert!(model_supports_adaptive_thinking("claude-opus-5-0"));
252 assert!(model_supports_adaptive_thinking("claude-sonnet-5-1"));
253 }
254
255 #[test]
256 fn adaptive_thinking_case_insensitive() {
257 assert!(model_supports_adaptive_thinking("CLAUDE-OPUS-4-7"));
258 }
259}