1use serde::{Deserialize, Serialize};
2use strum::EnumIter;
3
4#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq)]
6pub enum BedrockAdaptiveThinkingEffort {
7 Low,
8 Medium,
9 #[default]
10 High,
11 Max,
12}
13
14impl BedrockAdaptiveThinkingEffort {
15 pub fn as_str(&self) -> &'static str {
16 match self {
17 Self::Low => "low",
18 Self::Medium => "medium",
19 Self::High => "high",
20 Self::Max => "max",
21 }
22 }
23}
24
25#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
26#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
27pub enum BedrockModelMode {
28 #[default]
29 Default,
30 Thinking {
31 budget_tokens: Option<u64>,
32 },
33 AdaptiveThinking {
34 effort: BedrockAdaptiveThinkingEffort,
35 },
36}
37
38#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
39#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
40pub struct BedrockModelCacheConfiguration {
41 pub max_cache_anchors: usize,
42 pub min_total_token: u64,
43}
44
45#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
46#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
47pub enum Model {
48 // Anthropic models (already included)
49 #[serde(rename = "claude-sonnet-4", alias = "claude-sonnet-4-latest")]
50 ClaudeSonnet4,
51 #[serde(
52 rename = "claude-sonnet-4-thinking",
53 alias = "claude-sonnet-4-thinking-latest"
54 )]
55 ClaudeSonnet4Thinking,
56 #[default]
57 #[serde(rename = "claude-sonnet-4-5", alias = "claude-sonnet-4-5-latest")]
58 ClaudeSonnet4_5,
59 #[serde(
60 rename = "claude-sonnet-4-5-thinking",
61 alias = "claude-sonnet-4-5-thinking-latest"
62 )]
63 ClaudeSonnet4_5Thinking,
64 #[serde(rename = "claude-opus-4", alias = "claude-opus-4-latest")]
65 ClaudeOpus4,
66 #[serde(rename = "claude-opus-4-1", alias = "claude-opus-4-1-latest")]
67 ClaudeOpus4_1,
68 #[serde(
69 rename = "claude-opus-4-thinking",
70 alias = "claude-opus-4-thinking-latest"
71 )]
72 ClaudeOpus4Thinking,
73 #[serde(
74 rename = "claude-opus-4-1-thinking",
75 alias = "claude-opus-4-1-thinking-latest"
76 )]
77 ClaudeOpus4_1Thinking,
78 #[serde(rename = "claude-opus-4-5", alias = "claude-opus-4-5-latest")]
79 ClaudeOpus4_5,
80 #[serde(
81 rename = "claude-opus-4-5-thinking",
82 alias = "claude-opus-4-5-thinking-latest"
83 )]
84 ClaudeOpus4_5Thinking,
85 #[serde(rename = "claude-opus-4-6", alias = "claude-opus-4-6-latest")]
86 ClaudeOpus4_6,
87 #[serde(
88 rename = "claude-opus-4-6-thinking",
89 alias = "claude-opus-4-6-thinking-latest"
90 )]
91 ClaudeOpus4_6Thinking,
92 #[serde(rename = "claude-3-5-sonnet-v2", alias = "claude-3-5-sonnet-latest")]
93 Claude3_5SonnetV2,
94 #[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
95 Claude3_7Sonnet,
96 #[serde(
97 rename = "claude-3-7-sonnet-thinking",
98 alias = "claude-3-7-sonnet-thinking-latest"
99 )]
100 Claude3_7SonnetThinking,
101 #[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
102 Claude3Opus,
103 #[serde(rename = "claude-3-sonnet", alias = "claude-3-sonnet-latest")]
104 Claude3Sonnet,
105 #[serde(rename = "claude-3-5-haiku", alias = "claude-3-5-haiku-latest")]
106 Claude3_5Haiku,
107 #[serde(rename = "claude-haiku-4-5", alias = "claude-haiku-4-5-latest")]
108 ClaudeHaiku4_5,
109 Claude3_5Sonnet,
110 Claude3Haiku,
111 // Amazon Nova Models
112 AmazonNovaLite,
113 AmazonNovaMicro,
114 AmazonNovaPro,
115 AmazonNovaPremier,
116 // AI21 models
117 AI21J2GrandeInstruct,
118 AI21J2JumboInstruct,
119 AI21J2Mid,
120 AI21J2MidV1,
121 AI21J2Ultra,
122 AI21J2UltraV1_8k,
123 AI21J2UltraV1,
124 AI21JambaInstructV1,
125 AI21Jamba15LargeV1,
126 AI21Jamba15MiniV1,
127 // Cohere models
128 CohereCommandTextV14_4k,
129 CohereCommandRV1,
130 CohereCommandRPlusV1,
131 CohereCommandLightTextV14_4k,
132 // DeepSeek
133 DeepSeekR1,
134 // Meta models
135 MetaLlama38BInstructV1,
136 MetaLlama370BInstructV1,
137 MetaLlama318BInstructV1_128k,
138 MetaLlama318BInstructV1,
139 MetaLlama3170BInstructV1_128k,
140 MetaLlama3170BInstructV1,
141 MetaLlama31405BInstructV1,
142 MetaLlama321BInstructV1,
143 MetaLlama323BInstructV1,
144 MetaLlama3211BInstructV1,
145 MetaLlama3290BInstructV1,
146 MetaLlama3370BInstructV1,
147 #[allow(non_camel_case_types)]
148 MetaLlama4Scout17BInstructV1,
149 #[allow(non_camel_case_types)]
150 MetaLlama4Maverick17BInstructV1,
151 // Mistral models
152 MistralMistral7BInstructV0,
153 MistralMixtral8x7BInstructV0,
154 MistralMistralLarge2402V1,
155 MistralMistralSmall2402V1,
156 MistralPixtralLarge2502V1,
157 // Writer models
158 PalmyraWriterX5,
159 PalmyraWriterX4,
160 #[serde(rename = "custom")]
161 Custom {
162 name: String,
163 max_tokens: u64,
164 /// The name displayed in the UI, such as in the assistant panel model dropdown menu.
165 display_name: Option<String>,
166 max_output_tokens: Option<u64>,
167 default_temperature: Option<f32>,
168 cache_configuration: Option<BedrockModelCacheConfiguration>,
169 },
170}
171
172impl Model {
173 pub fn default_fast(region: &str) -> Self {
174 if region.starts_with("us-") {
175 Self::Claude3_5Haiku
176 } else {
177 Self::Claude3Haiku
178 }
179 }
180
181 pub fn from_id(id: &str) -> anyhow::Result<Self> {
182 if id.starts_with("claude-opus-4-6-thinking") {
183 Ok(Self::ClaudeOpus4_6Thinking)
184 } else if id.starts_with("claude-opus-4-6") {
185 Ok(Self::ClaudeOpus4_6)
186 } else if id.starts_with("claude-opus-4-5-thinking") {
187 Ok(Self::ClaudeOpus4_5Thinking)
188 } else if id.starts_with("claude-opus-4-5") {
189 Ok(Self::ClaudeOpus4_5)
190 } else if id.starts_with("claude-opus-4-1-thinking") {
191 Ok(Self::ClaudeOpus4_1Thinking)
192 } else if id.starts_with("claude-opus-4-1") {
193 Ok(Self::ClaudeOpus4_1)
194 } else if id.starts_with("claude-opus-4-thinking") {
195 Ok(Self::ClaudeOpus4Thinking)
196 } else if id.starts_with("claude-opus-4") {
197 Ok(Self::ClaudeOpus4)
198 } else if id.starts_with("claude-3-5-sonnet-v2") {
199 Ok(Self::Claude3_5SonnetV2)
200 } else if id.starts_with("claude-3-opus") {
201 Ok(Self::Claude3Opus)
202 } else if id.starts_with("claude-3-sonnet") {
203 Ok(Self::Claude3Sonnet)
204 } else if id.starts_with("claude-3-5-haiku") {
205 Ok(Self::Claude3_5Haiku)
206 } else if id.starts_with("claude-haiku-4-5") {
207 Ok(Self::ClaudeHaiku4_5)
208 } else if id.starts_with("claude-3-7-sonnet") {
209 Ok(Self::Claude3_7Sonnet)
210 } else if id.starts_with("claude-3-7-sonnet-thinking") {
211 Ok(Self::Claude3_7SonnetThinking)
212 } else if id.starts_with("claude-sonnet-4-5-thinking") {
213 Ok(Self::ClaudeSonnet4_5Thinking)
214 } else if id.starts_with("claude-sonnet-4-5") {
215 Ok(Self::ClaudeSonnet4_5)
216 } else if id.starts_with("claude-sonnet-4-thinking") {
217 Ok(Self::ClaudeSonnet4Thinking)
218 } else if id.starts_with("claude-sonnet-4") {
219 Ok(Self::ClaudeSonnet4)
220 } else {
221 anyhow::bail!("invalid model id {id}");
222 }
223 }
224
225 pub fn id(&self) -> &str {
226 match self {
227 Model::ClaudeSonnet4 => "claude-sonnet-4",
228 Model::ClaudeSonnet4Thinking => "claude-sonnet-4-thinking",
229 Model::ClaudeSonnet4_5 => "claude-sonnet-4-5",
230 Model::ClaudeSonnet4_5Thinking => "claude-sonnet-4-5-thinking",
231 Model::ClaudeOpus4 => "claude-opus-4",
232 Model::ClaudeOpus4_1 => "claude-opus-4-1",
233 Model::ClaudeOpus4Thinking => "claude-opus-4-thinking",
234 Model::ClaudeOpus4_1Thinking => "claude-opus-4-1-thinking",
235 Model::ClaudeOpus4_5 => "claude-opus-4-5",
236 Model::ClaudeOpus4_5Thinking => "claude-opus-4-5-thinking",
237 Model::ClaudeOpus4_6 => "claude-opus-4-6",
238 Model::ClaudeOpus4_6Thinking => "claude-opus-4-6-thinking",
239 Model::Claude3_5SonnetV2 => "claude-3-5-sonnet-v2",
240 Model::Claude3_5Sonnet => "claude-3-5-sonnet",
241 Model::Claude3Opus => "claude-3-opus",
242 Model::Claude3Sonnet => "claude-3-sonnet",
243 Model::Claude3Haiku => "claude-3-haiku",
244 Model::Claude3_5Haiku => "claude-3-5-haiku",
245 Model::ClaudeHaiku4_5 => "claude-haiku-4-5",
246 Model::Claude3_7Sonnet => "claude-3-7-sonnet",
247 Model::Claude3_7SonnetThinking => "claude-3-7-sonnet-thinking",
248 Model::AmazonNovaLite => "amazon-nova-lite",
249 Model::AmazonNovaMicro => "amazon-nova-micro",
250 Model::AmazonNovaPro => "amazon-nova-pro",
251 Model::AmazonNovaPremier => "amazon-nova-premier",
252 Model::DeepSeekR1 => "deepseek-r1",
253 Model::AI21J2GrandeInstruct => "ai21-j2-grande-instruct",
254 Model::AI21J2JumboInstruct => "ai21-j2-jumbo-instruct",
255 Model::AI21J2Mid => "ai21-j2-mid",
256 Model::AI21J2MidV1 => "ai21-j2-mid-v1",
257 Model::AI21J2Ultra => "ai21-j2-ultra",
258 Model::AI21J2UltraV1_8k => "ai21-j2-ultra-v1-8k",
259 Model::AI21J2UltraV1 => "ai21-j2-ultra-v1",
260 Model::AI21JambaInstructV1 => "ai21-jamba-instruct-v1",
261 Model::AI21Jamba15LargeV1 => "ai21-jamba-1-5-large-v1",
262 Model::AI21Jamba15MiniV1 => "ai21-jamba-1-5-mini-v1",
263 Model::CohereCommandTextV14_4k => "cohere-command-text-v14-4k",
264 Model::CohereCommandRV1 => "cohere-command-r-v1",
265 Model::CohereCommandRPlusV1 => "cohere-command-r-plus-v1",
266 Model::CohereCommandLightTextV14_4k => "cohere-command-light-text-v14-4k",
267 Model::MetaLlama38BInstructV1 => "meta-llama3-8b-instruct-v1",
268 Model::MetaLlama370BInstructV1 => "meta-llama3-70b-instruct-v1",
269 Model::MetaLlama318BInstructV1_128k => "meta-llama3-1-8b-instruct-v1-128k",
270 Model::MetaLlama318BInstructV1 => "meta-llama3-1-8b-instruct-v1",
271 Model::MetaLlama3170BInstructV1_128k => "meta-llama3-1-70b-instruct-v1-128k",
272 Model::MetaLlama3170BInstructV1 => "meta-llama3-1-70b-instruct-v1",
273 Model::MetaLlama31405BInstructV1 => "meta-llama3-1-405b-instruct-v1",
274 Model::MetaLlama321BInstructV1 => "meta-llama3-2-1b-instruct-v1",
275 Model::MetaLlama323BInstructV1 => "meta-llama3-2-3b-instruct-v1",
276 Model::MetaLlama3211BInstructV1 => "meta-llama3-2-11b-instruct-v1",
277 Model::MetaLlama3290BInstructV1 => "meta-llama3-2-90b-instruct-v1",
278 Model::MetaLlama3370BInstructV1 => "meta-llama3-3-70b-instruct-v1",
279 Model::MetaLlama4Scout17BInstructV1 => "meta-llama4-scout-17b-instruct-v1",
280 Model::MetaLlama4Maverick17BInstructV1 => "meta-llama4-maverick-17b-instruct-v1",
281 Model::MistralMistral7BInstructV0 => "mistral-7b-instruct-v0",
282 Model::MistralMixtral8x7BInstructV0 => "mistral-mixtral-8x7b-instruct-v0",
283 Model::MistralMistralLarge2402V1 => "mistral-large-2402-v1",
284 Model::MistralMistralSmall2402V1 => "mistral-small-2402-v1",
285 Model::MistralPixtralLarge2502V1 => "mistral-pixtral-large-2502-v1",
286 Model::PalmyraWriterX4 => "palmyra-writer-x4",
287 Model::PalmyraWriterX5 => "palmyra-writer-x5",
288 Self::Custom { name, .. } => name,
289 }
290 }
291
292 pub fn request_id(&self) -> &str {
293 match self {
294 Model::ClaudeSonnet4 | Model::ClaudeSonnet4Thinking => {
295 "anthropic.claude-sonnet-4-20250514-v1:0"
296 }
297 Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking => {
298 "anthropic.claude-sonnet-4-5-20250929-v1:0"
299 }
300 Model::ClaudeOpus4 | Model::ClaudeOpus4Thinking => {
301 "anthropic.claude-opus-4-20250514-v1:0"
302 }
303 Model::ClaudeOpus4_1 | Model::ClaudeOpus4_1Thinking => {
304 "anthropic.claude-opus-4-1-20250805-v1:0"
305 }
306 Model::ClaudeOpus4_5 | Model::ClaudeOpus4_5Thinking => {
307 "anthropic.claude-opus-4-5-20251101-v1:0"
308 }
309 Model::ClaudeOpus4_6 | Model::ClaudeOpus4_6Thinking => "anthropic.claude-opus-4-6-v1",
310 Model::Claude3_5SonnetV2 => "anthropic.claude-3-5-sonnet-20241022-v2:0",
311 Model::Claude3_5Sonnet => "anthropic.claude-3-5-sonnet-20240620-v1:0",
312 Model::Claude3Opus => "anthropic.claude-3-opus-20240229-v1:0",
313 Model::Claude3Sonnet => "anthropic.claude-3-sonnet-20240229-v1:0",
314 Model::Claude3Haiku => "anthropic.claude-3-haiku-20240307-v1:0",
315 Model::Claude3_5Haiku => "anthropic.claude-3-5-haiku-20241022-v1:0",
316 Model::ClaudeHaiku4_5 => "anthropic.claude-haiku-4-5-20251001-v1:0",
317 Model::Claude3_7Sonnet | Model::Claude3_7SonnetThinking => {
318 "anthropic.claude-3-7-sonnet-20250219-v1:0"
319 }
320 Model::AmazonNovaLite => "amazon.nova-lite-v1:0",
321 Model::AmazonNovaMicro => "amazon.nova-micro-v1:0",
322 Model::AmazonNovaPro => "amazon.nova-pro-v1:0",
323 Model::AmazonNovaPremier => "amazon.nova-premier-v1:0",
324 Model::DeepSeekR1 => "deepseek.r1-v1:0",
325 Model::AI21J2GrandeInstruct => "ai21.j2-grande-instruct",
326 Model::AI21J2JumboInstruct => "ai21.j2-jumbo-instruct",
327 Model::AI21J2Mid => "ai21.j2-mid",
328 Model::AI21J2MidV1 => "ai21.j2-mid-v1",
329 Model::AI21J2Ultra => "ai21.j2-ultra",
330 Model::AI21J2UltraV1_8k => "ai21.j2-ultra-v1:0:8k",
331 Model::AI21J2UltraV1 => "ai21.j2-ultra-v1",
332 Model::AI21JambaInstructV1 => "ai21.jamba-instruct-v1:0",
333 Model::AI21Jamba15LargeV1 => "ai21.jamba-1-5-large-v1:0",
334 Model::AI21Jamba15MiniV1 => "ai21.jamba-1-5-mini-v1:0",
335 Model::CohereCommandTextV14_4k => "cohere.command-text-v14:7:4k",
336 Model::CohereCommandRV1 => "cohere.command-r-v1:0",
337 Model::CohereCommandRPlusV1 => "cohere.command-r-plus-v1:0",
338 Model::CohereCommandLightTextV14_4k => "cohere.command-light-text-v14:7:4k",
339 Model::MetaLlama38BInstructV1 => "meta.llama3-8b-instruct-v1:0",
340 Model::MetaLlama370BInstructV1 => "meta.llama3-70b-instruct-v1:0",
341 Model::MetaLlama318BInstructV1_128k => "meta.llama3-1-8b-instruct-v1:0",
342 Model::MetaLlama318BInstructV1 => "meta.llama3-1-8b-instruct-v1:0",
343 Model::MetaLlama3170BInstructV1_128k => "meta.llama3-1-70b-instruct-v1:0",
344 Model::MetaLlama3170BInstructV1 => "meta.llama3-1-70b-instruct-v1:0",
345 Model::MetaLlama31405BInstructV1 => "meta.llama3-1-405b-instruct-v1:0",
346 Model::MetaLlama3211BInstructV1 => "meta.llama3-2-11b-instruct-v1:0",
347 Model::MetaLlama3290BInstructV1 => "meta.llama3-2-90b-instruct-v1:0",
348 Model::MetaLlama321BInstructV1 => "meta.llama3-2-1b-instruct-v1:0",
349 Model::MetaLlama323BInstructV1 => "meta.llama3-2-3b-instruct-v1:0",
350 Model::MetaLlama3370BInstructV1 => "meta.llama3-3-70b-instruct-v1:0",
351 Model::MetaLlama4Scout17BInstructV1 => "meta.llama4-scout-17b-instruct-v1:0",
352 Model::MetaLlama4Maverick17BInstructV1 => "meta.llama4-maverick-17b-instruct-v1:0",
353 Model::MistralMistral7BInstructV0 => "mistral.mistral-7b-instruct-v0:2",
354 Model::MistralMixtral8x7BInstructV0 => "mistral.mixtral-8x7b-instruct-v0:1",
355 Model::MistralMistralLarge2402V1 => "mistral.mistral-large-2402-v1:0",
356 Model::MistralMistralSmall2402V1 => "mistral.mistral-small-2402-v1:0",
357 Model::MistralPixtralLarge2502V1 => "mistral.pixtral-large-2502-v1:0",
358 Model::PalmyraWriterX4 => "writer.palmyra-x4-v1:0",
359 Model::PalmyraWriterX5 => "writer.palmyra-x5-v1:0",
360 Self::Custom { name, .. } => name,
361 }
362 }
363
364 pub fn display_name(&self) -> &str {
365 match self {
366 Self::ClaudeSonnet4 => "Claude Sonnet 4",
367 Self::ClaudeSonnet4Thinking => "Claude Sonnet 4 Thinking",
368 Self::ClaudeSonnet4_5 => "Claude Sonnet 4.5",
369 Self::ClaudeSonnet4_5Thinking => "Claude Sonnet 4.5 Thinking",
370 Self::ClaudeOpus4 => "Claude Opus 4",
371 Self::ClaudeOpus4_1 => "Claude Opus 4.1",
372 Self::ClaudeOpus4Thinking => "Claude Opus 4 Thinking",
373 Self::ClaudeOpus4_1Thinking => "Claude Opus 4.1 Thinking",
374 Self::ClaudeOpus4_5 => "Claude Opus 4.5",
375 Self::ClaudeOpus4_5Thinking => "Claude Opus 4.5 Thinking",
376 Self::ClaudeOpus4_6 => "Claude Opus 4.6",
377 Self::ClaudeOpus4_6Thinking => "Claude Opus 4.6 Thinking",
378 Self::Claude3_5SonnetV2 => "Claude 3.5 Sonnet v2",
379 Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
380 Self::Claude3Opus => "Claude 3 Opus",
381 Self::Claude3Sonnet => "Claude 3 Sonnet",
382 Self::Claude3Haiku => "Claude 3 Haiku",
383 Self::Claude3_5Haiku => "Claude 3.5 Haiku",
384 Self::ClaudeHaiku4_5 => "Claude Haiku 4.5",
385 Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
386 Self::Claude3_7SonnetThinking => "Claude 3.7 Sonnet Thinking",
387 Self::AmazonNovaLite => "Amazon Nova Lite",
388 Self::AmazonNovaMicro => "Amazon Nova Micro",
389 Self::AmazonNovaPro => "Amazon Nova Pro",
390 Self::AmazonNovaPremier => "Amazon Nova Premier",
391 Self::DeepSeekR1 => "DeepSeek R1",
392 Self::AI21J2GrandeInstruct => "AI21 Jurassic2 Grande Instruct",
393 Self::AI21J2JumboInstruct => "AI21 Jurassic2 Jumbo Instruct",
394 Self::AI21J2Mid => "AI21 Jurassic2 Mid",
395 Self::AI21J2MidV1 => "AI21 Jurassic2 Mid V1",
396 Self::AI21J2Ultra => "AI21 Jurassic2 Ultra",
397 Self::AI21J2UltraV1_8k => "AI21 Jurassic2 Ultra V1 8K",
398 Self::AI21J2UltraV1 => "AI21 Jurassic2 Ultra V1",
399 Self::AI21JambaInstructV1 => "AI21 Jamba Instruct",
400 Self::AI21Jamba15LargeV1 => "AI21 Jamba 1.5 Large",
401 Self::AI21Jamba15MiniV1 => "AI21 Jamba 1.5 Mini",
402 Self::CohereCommandTextV14_4k => "Cohere Command Text V14 4K",
403 Self::CohereCommandRV1 => "Cohere Command R V1",
404 Self::CohereCommandRPlusV1 => "Cohere Command R Plus V1",
405 Self::CohereCommandLightTextV14_4k => "Cohere Command Light Text V14 4K",
406 Self::MetaLlama38BInstructV1 => "Meta Llama 3 8B Instruct",
407 Self::MetaLlama370BInstructV1 => "Meta Llama 3 70B Instruct",
408 Self::MetaLlama318BInstructV1_128k => "Meta Llama 3.1 8B Instruct 128K",
409 Self::MetaLlama318BInstructV1 => "Meta Llama 3.1 8B Instruct",
410 Self::MetaLlama3170BInstructV1_128k => "Meta Llama 3.1 70B Instruct 128K",
411 Self::MetaLlama3170BInstructV1 => "Meta Llama 3.1 70B Instruct",
412 Self::MetaLlama31405BInstructV1 => "Meta Llama 3.1 405B Instruct",
413 Self::MetaLlama3211BInstructV1 => "Meta Llama 3.2 11B Instruct",
414 Self::MetaLlama3290BInstructV1 => "Meta Llama 3.2 90B Instruct",
415 Self::MetaLlama321BInstructV1 => "Meta Llama 3.2 1B Instruct",
416 Self::MetaLlama323BInstructV1 => "Meta Llama 3.2 3B Instruct",
417 Self::MetaLlama3370BInstructV1 => "Meta Llama 3.3 70B Instruct",
418 Self::MetaLlama4Scout17BInstructV1 => "Meta Llama 4 Scout 17B Instruct",
419 Self::MetaLlama4Maverick17BInstructV1 => "Meta Llama 4 Maverick 17B Instruct",
420 Self::MistralMistral7BInstructV0 => "Mistral 7B Instruct V0",
421 Self::MistralMixtral8x7BInstructV0 => "Mistral Mixtral 8x7B Instruct V0",
422 Self::MistralMistralLarge2402V1 => "Mistral Large 2402 V1",
423 Self::MistralMistralSmall2402V1 => "Mistral Small 2402 V1",
424 Self::MistralPixtralLarge2502V1 => "Pixtral Large 25.02 V1",
425 Self::PalmyraWriterX5 => "Writer Palmyra X5",
426 Self::PalmyraWriterX4 => "Writer Palmyra X4",
427 Self::Custom {
428 display_name, name, ..
429 } => display_name.as_deref().unwrap_or(name),
430 }
431 }
432
433 pub fn max_token_count(&self) -> u64 {
434 match self {
435 Self::Claude3_5SonnetV2
436 | Self::Claude3Opus
437 | Self::Claude3Sonnet
438 | Self::Claude3_5Haiku
439 | Self::ClaudeHaiku4_5
440 | Self::Claude3_7Sonnet
441 | Self::ClaudeSonnet4
442 | Self::ClaudeOpus4
443 | Self::ClaudeOpus4_1
444 | Self::ClaudeSonnet4Thinking
445 | Self::ClaudeSonnet4_5
446 | Self::ClaudeSonnet4_5Thinking
447 | Self::ClaudeOpus4Thinking
448 | Self::ClaudeOpus4_1Thinking
449 | Self::ClaudeOpus4_5
450 | Self::ClaudeOpus4_5Thinking
451 | Self::ClaudeOpus4_6
452 | Self::ClaudeOpus4_6Thinking => 200_000,
453 Self::AmazonNovaPremier => 1_000_000,
454 Self::PalmyraWriterX5 => 1_000_000,
455 Self::PalmyraWriterX4 => 128_000,
456 Self::Custom { max_tokens, .. } => *max_tokens,
457 _ => 128_000,
458 }
459 }
460
461 pub fn max_output_tokens(&self) -> u64 {
462 match self {
463 Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3_5Haiku => 4_096,
464 Self::Claude3_7Sonnet | Self::Claude3_7SonnetThinking => 128_000,
465 Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking => 64_000,
466 Self::ClaudeSonnet4_5
467 | Self::ClaudeSonnet4_5Thinking
468 | Self::ClaudeHaiku4_5
469 | Self::ClaudeOpus4_5
470 | Self::ClaudeOpus4_5Thinking => 64_000,
471 Self::ClaudeOpus4_6 | Self::ClaudeOpus4_6Thinking => 128_000,
472 Self::ClaudeOpus4
473 | Self::ClaudeOpus4Thinking
474 | Self::ClaudeOpus4_1
475 | Self::ClaudeOpus4_1Thinking => 32_000,
476 Self::Claude3_5SonnetV2 | Self::PalmyraWriterX4 | Self::PalmyraWriterX5 => 8_192,
477 Self::Custom {
478 max_output_tokens, ..
479 } => max_output_tokens.unwrap_or(4_096),
480 _ => 4_096,
481 }
482 }
483
484 pub fn default_temperature(&self) -> f32 {
485 match self {
486 Self::Claude3_5SonnetV2
487 | Self::Claude3Opus
488 | Self::Claude3Sonnet
489 | Self::Claude3_5Haiku
490 | Self::ClaudeHaiku4_5
491 | Self::Claude3_7Sonnet
492 | Self::ClaudeOpus4
493 | Self::ClaudeOpus4Thinking
494 | Self::ClaudeOpus4_1
495 | Self::ClaudeOpus4_1Thinking
496 | Self::ClaudeOpus4_5
497 | Self::ClaudeOpus4_5Thinking
498 | Self::ClaudeOpus4_6
499 | Self::ClaudeOpus4_6Thinking
500 | Self::ClaudeSonnet4
501 | Self::ClaudeSonnet4Thinking
502 | Self::ClaudeSonnet4_5
503 | Self::ClaudeSonnet4_5Thinking => 1.0,
504 Self::Custom {
505 default_temperature,
506 ..
507 } => default_temperature.unwrap_or(1.0),
508 _ => 1.0,
509 }
510 }
511
512 pub fn supports_tool_use(&self) -> bool {
513 match self {
514 // Anthropic Claude 3 models (all support tool use)
515 Self::Claude3Opus
516 | Self::Claude3Sonnet
517 | Self::Claude3_5Sonnet
518 | Self::Claude3_5SonnetV2
519 | Self::Claude3_7Sonnet
520 | Self::Claude3_7SonnetThinking
521 | Self::ClaudeOpus4
522 | Self::ClaudeOpus4Thinking
523 | Self::ClaudeOpus4_1
524 | Self::ClaudeOpus4_1Thinking
525 | Self::ClaudeOpus4_5
526 | Self::ClaudeOpus4_5Thinking
527 | Self::ClaudeOpus4_6
528 | Self::ClaudeOpus4_6Thinking
529 | Self::ClaudeSonnet4
530 | Self::ClaudeSonnet4Thinking
531 | Self::ClaudeSonnet4_5
532 | Self::ClaudeSonnet4_5Thinking
533 | Self::Claude3_5Haiku
534 | Self::ClaudeHaiku4_5 => true,
535
536 // Amazon Nova models (all support tool use)
537 Self::AmazonNovaPremier
538 | Self::AmazonNovaPro
539 | Self::AmazonNovaLite
540 | Self::AmazonNovaMicro => true,
541
542 // AI21 Jamba 1.5 models support tool use
543 Self::AI21Jamba15LargeV1 | Self::AI21Jamba15MiniV1 => true,
544
545 // Cohere Command R models support tool use
546 Self::CohereCommandRV1 | Self::CohereCommandRPlusV1 => true,
547
548 // All other models don't support tool use
549 // Including Meta Llama 3.2, AI21 Jurassic, and others
550 _ => false,
551 }
552 }
553
554 pub fn supports_caching(&self) -> bool {
555 match self {
556 // Only Claude models on Bedrock support caching
557 // Nova models support only text caching
558 // https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html#prompt-caching-models
559 Self::Claude3_5Haiku
560 | Self::ClaudeHaiku4_5
561 | Self::Claude3_7Sonnet
562 | Self::Claude3_7SonnetThinking
563 | Self::ClaudeSonnet4
564 | Self::ClaudeSonnet4Thinking
565 | Self::ClaudeSonnet4_5
566 | Self::ClaudeSonnet4_5Thinking
567 | Self::ClaudeOpus4
568 | Self::ClaudeOpus4Thinking
569 | Self::ClaudeOpus4_1
570 | Self::ClaudeOpus4_1Thinking
571 | Self::ClaudeOpus4_5
572 | Self::ClaudeOpus4_5Thinking
573 | Self::ClaudeOpus4_6
574 | Self::ClaudeOpus4_6Thinking => true,
575
576 // Custom models - check if they have cache configuration
577 Self::Custom {
578 cache_configuration,
579 ..
580 } => cache_configuration.is_some(),
581
582 // All other models don't support caching
583 _ => false,
584 }
585 }
586
587 pub fn cache_configuration(&self) -> Option<BedrockModelCacheConfiguration> {
588 match self {
589 Self::Claude3_7Sonnet
590 | Self::Claude3_7SonnetThinking
591 | Self::ClaudeSonnet4
592 | Self::ClaudeSonnet4Thinking
593 | Self::ClaudeOpus4
594 | Self::ClaudeOpus4Thinking
595 | Self::ClaudeOpus4_1
596 | Self::ClaudeOpus4_1Thinking
597 | Self::ClaudeOpus4_5
598 | Self::ClaudeOpus4_5Thinking
599 | Self::ClaudeOpus4_6
600 | Self::ClaudeOpus4_6Thinking => Some(BedrockModelCacheConfiguration {
601 max_cache_anchors: 4,
602 min_total_token: 1024,
603 }),
604
605 Self::Claude3_5Haiku | Self::ClaudeHaiku4_5 => Some(BedrockModelCacheConfiguration {
606 max_cache_anchors: 4,
607 min_total_token: 2048,
608 }),
609
610 Self::Custom {
611 cache_configuration,
612 ..
613 } => cache_configuration.clone(),
614
615 _ => None,
616 }
617 }
618
619 pub fn mode(&self) -> BedrockModelMode {
620 match self {
621 Model::Claude3_7SonnetThinking => BedrockModelMode::Thinking {
622 budget_tokens: Some(4096),
623 },
624 Model::ClaudeSonnet4Thinking | Model::ClaudeSonnet4_5Thinking => {
625 BedrockModelMode::Thinking {
626 budget_tokens: Some(4096),
627 }
628 }
629 Model::ClaudeOpus4Thinking
630 | Model::ClaudeOpus4_1Thinking
631 | Model::ClaudeOpus4_5Thinking => BedrockModelMode::Thinking {
632 budget_tokens: Some(4096),
633 },
634 Model::ClaudeOpus4_6Thinking => BedrockModelMode::AdaptiveThinking {
635 effort: BedrockAdaptiveThinkingEffort::default(),
636 },
637 _ => BedrockModelMode::Default,
638 }
639 }
640
641 pub fn supports_extended_context(&self) -> bool {
642 matches!(
643 self,
644 Model::ClaudeSonnet4
645 | Model::ClaudeSonnet4Thinking
646 | Model::ClaudeSonnet4_5
647 | Model::ClaudeSonnet4_5Thinking
648 | Model::ClaudeOpus4_5
649 | Model::ClaudeOpus4_5Thinking
650 | Model::ClaudeOpus4_6
651 | Model::ClaudeOpus4_6Thinking
652 )
653 }
654
655 pub fn cross_region_inference_id(
656 &self,
657 region: &str,
658 allow_global: bool,
659 ) -> anyhow::Result<String> {
660 // List derived from here:
661 // https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system
662 let model_id = self.request_id();
663
664 let supports_global = matches!(
665 self,
666 Model::ClaudeOpus4_5
667 | Model::ClaudeOpus4_5Thinking
668 | Model::ClaudeOpus4_6
669 | Model::ClaudeOpus4_6Thinking
670 | Model::ClaudeHaiku4_5
671 | Model::ClaudeSonnet4
672 | Model::ClaudeSonnet4Thinking
673 | Model::ClaudeSonnet4_5
674 | Model::ClaudeSonnet4_5Thinking
675 );
676
677 let region_group = if region.starts_with("us-gov-") {
678 "us-gov"
679 } else if region.starts_with("us-")
680 || region.starts_with("ca-")
681 || region.starts_with("sa-")
682 {
683 if allow_global && supports_global {
684 "global"
685 } else {
686 "us"
687 }
688 } else if region.starts_with("eu-") {
689 if allow_global && supports_global {
690 "global"
691 } else {
692 "eu"
693 }
694 } else if region.starts_with("ap-") || region == "me-central-1" || region == "me-south-1" {
695 if allow_global && supports_global {
696 "global"
697 } else {
698 "apac"
699 }
700 } else {
701 anyhow::bail!("Unsupported Region {region}");
702 };
703
704 match (self, region_group, region) {
705 (Model::Custom { .. }, _, _) => Ok(self.request_id().into()),
706
707 (
708 Model::ClaudeOpus4_5
709 | Model::ClaudeOpus4_5Thinking
710 | Model::ClaudeOpus4_6
711 | Model::ClaudeOpus4_6Thinking
712 | Model::ClaudeHaiku4_5
713 | Model::ClaudeSonnet4
714 | Model::ClaudeSonnet4Thinking
715 | Model::ClaudeSonnet4_5
716 | Model::ClaudeSonnet4_5Thinking,
717 "global",
718 _,
719 ) => Ok(format!("{}.{}", region_group, model_id)),
720
721 (
722 Model::Claude3Haiku
723 | Model::Claude3_5Sonnet
724 | Model::Claude3_7Sonnet
725 | Model::Claude3_7SonnetThinking
726 | Model::ClaudeSonnet4_5
727 | Model::ClaudeSonnet4_5Thinking,
728 "us-gov",
729 _,
730 ) => Ok(format!("{}.{}", region_group, model_id)),
731
732 (
733 Model::ClaudeHaiku4_5 | Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking,
734 "apac",
735 "ap-southeast-2" | "ap-southeast-4",
736 ) => Ok(format!("au.{}", model_id)),
737
738 (
739 Model::ClaudeHaiku4_5 | Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking,
740 "apac",
741 "ap-northeast-1" | "ap-northeast-3",
742 ) => Ok(format!("jp.{}", model_id)),
743
744 (Model::AmazonNovaLite, "us", r) if r.starts_with("ca-") => {
745 Ok(format!("ca.{}", model_id))
746 }
747
748 (
749 Model::AmazonNovaPremier
750 | Model::AmazonNovaLite
751 | Model::AmazonNovaMicro
752 | Model::AmazonNovaPro
753 | Model::Claude3_5Haiku
754 | Model::ClaudeHaiku4_5
755 | Model::Claude3_5Sonnet
756 | Model::Claude3_5SonnetV2
757 | Model::Claude3_7Sonnet
758 | Model::Claude3_7SonnetThinking
759 | Model::ClaudeSonnet4
760 | Model::ClaudeSonnet4Thinking
761 | Model::ClaudeSonnet4_5
762 | Model::ClaudeSonnet4_5Thinking
763 | Model::ClaudeOpus4
764 | Model::ClaudeOpus4Thinking
765 | Model::ClaudeOpus4_1
766 | Model::ClaudeOpus4_1Thinking
767 | Model::ClaudeOpus4_5
768 | Model::ClaudeOpus4_5Thinking
769 | Model::ClaudeOpus4_6
770 | Model::ClaudeOpus4_6Thinking
771 | Model::Claude3Haiku
772 | Model::Claude3Opus
773 | Model::Claude3Sonnet
774 | Model::DeepSeekR1
775 | Model::MetaLlama31405BInstructV1
776 | Model::MetaLlama3170BInstructV1_128k
777 | Model::MetaLlama3170BInstructV1
778 | Model::MetaLlama318BInstructV1_128k
779 | Model::MetaLlama318BInstructV1
780 | Model::MetaLlama3211BInstructV1
781 | Model::MetaLlama321BInstructV1
782 | Model::MetaLlama323BInstructV1
783 | Model::MetaLlama3290BInstructV1
784 | Model::MetaLlama3370BInstructV1
785 | Model::MetaLlama4Maverick17BInstructV1
786 | Model::MetaLlama4Scout17BInstructV1
787 | Model::MistralPixtralLarge2502V1
788 | Model::PalmyraWriterX4
789 | Model::PalmyraWriterX5,
790 "us",
791 _,
792 ) => Ok(format!("{}.{}", region_group, model_id)),
793
794 (
795 Model::AmazonNovaLite
796 | Model::AmazonNovaMicro
797 | Model::AmazonNovaPro
798 | Model::Claude3_5Sonnet
799 | Model::ClaudeHaiku4_5
800 | Model::Claude3_7Sonnet
801 | Model::Claude3_7SonnetThinking
802 | Model::ClaudeSonnet4
803 | Model::ClaudeSonnet4_5
804 | Model::ClaudeSonnet4_5Thinking
805 | Model::ClaudeOpus4_6
806 | Model::ClaudeOpus4_6Thinking
807 | Model::Claude3Haiku
808 | Model::Claude3Sonnet
809 | Model::MetaLlama321BInstructV1
810 | Model::MetaLlama323BInstructV1
811 | Model::MistralPixtralLarge2502V1,
812 "eu",
813 _,
814 ) => Ok(format!("{}.{}", region_group, model_id)),
815
816 (
817 Model::AmazonNovaLite
818 | Model::AmazonNovaMicro
819 | Model::AmazonNovaPro
820 | Model::Claude3_5Sonnet
821 | Model::Claude3_5SonnetV2
822 | Model::ClaudeHaiku4_5
823 | Model::Claude3_7Sonnet
824 | Model::Claude3_7SonnetThinking
825 | Model::ClaudeSonnet4
826 | Model::Claude3Haiku
827 | Model::Claude3Sonnet,
828 "apac",
829 _,
830 ) => Ok(format!("{}.{}", region_group, model_id)),
831
832 _ => Ok(model_id.into()),
833 }
834 }
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840
841 #[test]
842 fn test_us_region_inference_ids() -> anyhow::Result<()> {
843 // Test US regions
844 assert_eq!(
845 Model::Claude3_5SonnetV2.cross_region_inference_id("us-east-1", false)?,
846 "us.anthropic.claude-3-5-sonnet-20241022-v2:0"
847 );
848 assert_eq!(
849 Model::Claude3_5SonnetV2.cross_region_inference_id("us-west-2", false)?,
850 "us.anthropic.claude-3-5-sonnet-20241022-v2:0"
851 );
852 assert_eq!(
853 Model::AmazonNovaPro.cross_region_inference_id("us-east-2", false)?,
854 "us.amazon.nova-pro-v1:0"
855 );
856 Ok(())
857 }
858
859 #[test]
860 fn test_eu_region_inference_ids() -> anyhow::Result<()> {
861 // Test European regions
862 assert_eq!(
863 Model::ClaudeSonnet4.cross_region_inference_id("eu-west-1", false)?,
864 "eu.anthropic.claude-sonnet-4-20250514-v1:0"
865 );
866 assert_eq!(
867 Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1", false)?,
868 "eu.anthropic.claude-sonnet-4-5-20250929-v1:0"
869 );
870 assert_eq!(
871 Model::Claude3Sonnet.cross_region_inference_id("eu-west-1", false)?,
872 "eu.anthropic.claude-3-sonnet-20240229-v1:0"
873 );
874 assert_eq!(
875 Model::AmazonNovaMicro.cross_region_inference_id("eu-north-1", false)?,
876 "eu.amazon.nova-micro-v1:0"
877 );
878 Ok(())
879 }
880
881 #[test]
882 fn test_apac_region_inference_ids() -> anyhow::Result<()> {
883 // Test Asia-Pacific regions
884 assert_eq!(
885 Model::Claude3_5SonnetV2.cross_region_inference_id("ap-northeast-1", false)?,
886 "apac.anthropic.claude-3-5-sonnet-20241022-v2:0"
887 );
888 assert_eq!(
889 Model::Claude3_5SonnetV2.cross_region_inference_id("ap-southeast-2", false)?,
890 "apac.anthropic.claude-3-5-sonnet-20241022-v2:0"
891 );
892 assert_eq!(
893 Model::AmazonNovaLite.cross_region_inference_id("ap-south-1", false)?,
894 "apac.amazon.nova-lite-v1:0"
895 );
896 Ok(())
897 }
898
899 #[test]
900 fn test_gov_region_inference_ids() -> anyhow::Result<()> {
901 // Test Government regions
902 assert_eq!(
903 Model::Claude3_5Sonnet.cross_region_inference_id("us-gov-east-1", false)?,
904 "us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0"
905 );
906 assert_eq!(
907 Model::Claude3Haiku.cross_region_inference_id("us-gov-west-1", false)?,
908 "us-gov.anthropic.claude-3-haiku-20240307-v1:0"
909 );
910 Ok(())
911 }
912
913 #[test]
914 fn test_meta_models_inference_ids() -> anyhow::Result<()> {
915 // Test Meta models
916 assert_eq!(
917 Model::MetaLlama370BInstructV1.cross_region_inference_id("us-east-1", false)?,
918 "meta.llama3-70b-instruct-v1:0"
919 );
920 assert_eq!(
921 Model::MetaLlama3170BInstructV1.cross_region_inference_id("us-east-1", false)?,
922 "us.meta.llama3-1-70b-instruct-v1:0"
923 );
924 assert_eq!(
925 Model::MetaLlama321BInstructV1.cross_region_inference_id("eu-west-1", false)?,
926 "eu.meta.llama3-2-1b-instruct-v1:0"
927 );
928 Ok(())
929 }
930
931 #[test]
932 fn test_mistral_models_inference_ids() -> anyhow::Result<()> {
933 // Mistral models don't follow the regional prefix pattern,
934 // so they should return their original IDs
935 assert_eq!(
936 Model::MistralMistralLarge2402V1.cross_region_inference_id("us-east-1", false)?,
937 "mistral.mistral-large-2402-v1:0"
938 );
939 assert_eq!(
940 Model::MistralMixtral8x7BInstructV0.cross_region_inference_id("eu-west-1", false)?,
941 "mistral.mixtral-8x7b-instruct-v0:1"
942 );
943 Ok(())
944 }
945
946 #[test]
947 fn test_ai21_models_inference_ids() -> anyhow::Result<()> {
948 // AI21 models don't follow the regional prefix pattern,
949 // so they should return their original IDs
950 assert_eq!(
951 Model::AI21J2UltraV1.cross_region_inference_id("us-east-1", false)?,
952 "ai21.j2-ultra-v1"
953 );
954 assert_eq!(
955 Model::AI21JambaInstructV1.cross_region_inference_id("eu-west-1", false)?,
956 "ai21.jamba-instruct-v1:0"
957 );
958 Ok(())
959 }
960
961 #[test]
962 fn test_cohere_models_inference_ids() -> anyhow::Result<()> {
963 // Cohere models don't follow the regional prefix pattern,
964 // so they should return their original IDs
965 assert_eq!(
966 Model::CohereCommandRV1.cross_region_inference_id("us-east-1", false)?,
967 "cohere.command-r-v1:0"
968 );
969 assert_eq!(
970 Model::CohereCommandTextV14_4k.cross_region_inference_id("ap-southeast-1", false)?,
971 "cohere.command-text-v14:7:4k"
972 );
973 Ok(())
974 }
975
976 #[test]
977 fn test_custom_model_inference_ids() -> anyhow::Result<()> {
978 // Test custom models
979 let custom_model = Model::Custom {
980 name: "custom.my-model-v1:0".to_string(),
981 max_tokens: 100000,
982 display_name: Some("My Custom Model".to_string()),
983 max_output_tokens: Some(8192),
984 default_temperature: Some(0.7),
985 cache_configuration: None,
986 };
987
988 // Custom model should return its name unchanged
989 assert_eq!(
990 custom_model.cross_region_inference_id("us-east-1", false)?,
991 "custom.my-model-v1:0"
992 );
993
994 // Test that models without global support fall back to regional when allow_global is true
995 assert_eq!(
996 Model::AmazonNovaPro.cross_region_inference_id("us-east-1", true)?,
997 "us.amazon.nova-pro-v1:0",
998 "Nova Pro should fall back to regional profile even when allow_global is true"
999 );
1000
1001 Ok(())
1002 }
1003
1004 #[test]
1005 fn test_friendly_id_vs_request_id() {
1006 // Test that id() returns friendly identifiers
1007 assert_eq!(Model::Claude3_5SonnetV2.id(), "claude-3-5-sonnet-v2");
1008 assert_eq!(Model::AmazonNovaLite.id(), "amazon-nova-lite");
1009 assert_eq!(Model::DeepSeekR1.id(), "deepseek-r1");
1010 assert_eq!(
1011 Model::MetaLlama38BInstructV1.id(),
1012 "meta-llama3-8b-instruct-v1"
1013 );
1014
1015 // Test that request_id() returns actual backend model IDs
1016 assert_eq!(
1017 Model::Claude3_5SonnetV2.request_id(),
1018 "anthropic.claude-3-5-sonnet-20241022-v2:0"
1019 );
1020 assert_eq!(Model::AmazonNovaLite.request_id(), "amazon.nova-lite-v1:0");
1021 assert_eq!(Model::DeepSeekR1.request_id(), "deepseek.r1-v1:0");
1022 assert_eq!(
1023 Model::MetaLlama38BInstructV1.request_id(),
1024 "meta.llama3-8b-instruct-v1:0"
1025 );
1026
1027 // Test thinking models have different friendly IDs but same request IDs
1028 assert_eq!(Model::ClaudeSonnet4.id(), "claude-sonnet-4");
1029 assert_eq!(
1030 Model::ClaudeSonnet4Thinking.id(),
1031 "claude-sonnet-4-thinking"
1032 );
1033 assert_eq!(
1034 Model::ClaudeSonnet4.request_id(),
1035 Model::ClaudeSonnet4Thinking.request_id()
1036 );
1037 }
1038}
1039
1040#[test]
1041fn test_global_inference_ids() -> anyhow::Result<()> {
1042 // Test global inference for models that support it when allow_global is true
1043 assert_eq!(
1044 Model::ClaudeSonnet4.cross_region_inference_id("us-east-1", true)?,
1045 "global.anthropic.claude-sonnet-4-20250514-v1:0"
1046 );
1047 assert_eq!(
1048 Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1", true)?,
1049 "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
1050 );
1051 assert_eq!(
1052 Model::ClaudeHaiku4_5.cross_region_inference_id("ap-south-1", true)?,
1053 "global.anthropic.claude-haiku-4-5-20251001-v1:0"
1054 );
1055
1056 // Test that regional prefix is used when allow_global is false
1057 assert_eq!(
1058 Model::ClaudeSonnet4.cross_region_inference_id("us-east-1", false)?,
1059 "us.anthropic.claude-sonnet-4-20250514-v1:0"
1060 );
1061
1062 Ok(())
1063}