Skip to main content

claude_sdk/
models.rs

1//! Model identifiers and metadata for Claude models.
2//!
3//! This module provides constants and metadata for all available Claude models,
4//! including constraints like context windows, output limits, and capabilities.
5//!
6//! # Available Models
7//!
8//! ## Latest (Claude 5)
9//!
10//! | Model | Best For | Max Output | Extended Thinking |
11//! |-------|----------|------------|-------------------|
12//! | [`CLAUDE_FABLE_5`] | Hardest knowledge work and coding | 64K | Yes |
13//! | [`CLAUDE_MYTHOS_5`] | Cybersecurity and biology research | 64K | Yes |
14//!
15//! ## Claude 4.x
16//!
17//! | Model | Best For | Max Output | Extended Thinking |
18//! |-------|----------|------------|-------------------|
19//! | [`CLAUDE_OPUS_4_8`] | Long-running agents/coding | 64K | Yes |
20//! | [`CLAUDE_OPUS_4_7`] | Long-running agents/coding | 64K | Yes |
21//! | [`CLAUDE_OPUS_4_6`] | Long-running agents/coding | 64K | Yes |
22//! | [`CLAUDE_SONNET_4_6`] | Best balance of speed and intelligence | 64K | Yes |
23//! | [`CLAUDE_SONNET_4_5`] | Complex agents, coding | 64K | Yes |
24//! | [`CLAUDE_HAIKU_4_5`] | Speed, near-frontier intelligence | 64K | Yes |
25//! | [`CLAUDE_OPUS_4_5`] | Maximum intelligence | 64K | Yes |
26//!
27//! ## Legacy (Claude 4.x and 3.x)
28//!
29//! | Model | Best For | Max Output |
30//! |-------|----------|------------|
31//! | [`CLAUDE_OPUS_4_1`] | Previous generation powerful | 32K |
32//! | [`CLAUDE_SONNET_4`] | Balanced performance | 64K |
33//! | [`CLAUDE_OPUS_4`] | Claude 4 powerful | 32K |
34//! | [`CLAUDE_HAIKU_3_5`] | Fast and efficient | 8K |
35//! | [`CLAUDE_HAIKU_3`] | Original fast model | 4K |
36//!
37//! # Usage Examples
38//!
39//! ## Basic Model Selection
40//!
41//! ```rust
42//! use claude_sdk::models::{CLAUDE_SONNET_4_5, CLAUDE_OPUS_4_5};
43//!
44//! let model = CLAUDE_SONNET_4_5;
45//! println!("Model: {} ({})", model.name, model.anthropic_id);
46//! println!("Max output: {} tokens", model.max_output_tokens);
47//! println!("Supports vision: {}", model.supports_vision);
48//! println!("Supports thinking: {}", model.supports_extended_thinking);
49//! ```
50//!
51//! ## Model Lookup
52//!
53//! ```rust
54//! use claude_sdk::models::{get_model, get_model_by_anthropic_id};
55//!
56//! // Lookup by any ID format
57//! let model = get_model("claude-sonnet-4-5-20250929").unwrap();
58//! assert_eq!(model.name, "Claude Sonnet 4.5");
59//!
60//! // Lookup by Anthropic ID specifically
61//! let model = get_model_by_anthropic_id("claude-sonnet-4-5-20250929").unwrap();
62//! ```
63//!
64//! ## Cost Estimation
65//!
66//! ```rust
67//! use claude_sdk::models::CLAUDE_SONNET_4_5;
68//!
69//! // Estimate cost for 10K input + 2K output tokens
70//! let cost = CLAUDE_SONNET_4_5.estimate_cost(10_000, 2_000);
71//! println!("Estimated cost: ${:.4}", cost);  // ~$0.06
72//! ```
73//!
74//! ## AWS Bedrock Regions
75//!
76//! ```rust
77//! use claude_sdk::models::{CLAUDE_SONNET_4_5, BedrockRegion};
78//!
79//! // Get model ID for different Bedrock endpoints
80//! let standard = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Standard);
81//! let global = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Global);
82//! let us = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::US);
83//! ```
84
85/// Model capabilities and constraints
86#[derive(Debug, Clone, PartialEq)]
87pub struct Model {
88    /// Human-readable model name
89    pub name: &'static str,
90
91    /// Model family (e.g., "sonnet", "opus", "haiku")
92    pub family: &'static str,
93
94    /// Release date/version identifier
95    pub version: &'static str,
96
97    /// Anthropic API model identifier
98    pub anthropic_id: &'static str,
99
100    /// AWS Bedrock regional endpoint model identifier (if available)
101    pub bedrock_id: Option<&'static str>,
102
103    /// AWS Bedrock global endpoint model identifier (if available)
104    ///
105    /// Claude 4.5+ models support global endpoints for dynamic routing.
106    /// Use this for maximum availability across regions.
107    pub bedrock_global_id: Option<&'static str>,
108
109    /// Google Vertex AI model identifier (if available)
110    pub vertex_id: Option<&'static str>,
111
112    /// Maximum context window in tokens (standard)
113    pub max_context_tokens: u32,
114
115    /// Extended context window in tokens (if available)
116    ///
117    /// Some models support extended context with beta headers.
118    /// For example, Claude Sonnet 4.5 and Sonnet 4 support 1M tokens
119    /// with the `context-1m-2025-08-07` beta header.
120    pub max_context_tokens_extended: Option<u32>,
121
122    /// Maximum output tokens per request
123    pub max_output_tokens: u32,
124
125    /// Supports vision (image inputs)
126    pub supports_vision: bool,
127
128    /// Supports tool use
129    pub supports_tools: bool,
130
131    /// Supports prompt caching
132    pub supports_caching: bool,
133
134    /// Supports extended thinking
135    pub supports_extended_thinking: bool,
136
137    /// Supports effort parameter (beta)
138    ///
139    /// Requires beta header: `anthropic-beta: effort-2025-11-24`
140    /// Currently only Claude Opus 4.5
141    pub supports_effort: bool,
142
143    /// Cost per million input tokens (USD)
144    pub cost_per_mtok_input: f64,
145
146    /// Cost per million output tokens (USD)
147    pub cost_per_mtok_output: f64,
148
149    /// Brief description of best use cases
150    pub description: &'static str,
151}
152
153/// AWS Bedrock endpoint region configuration.
154///
155/// Bedrock supports different endpoint types for accessing Claude models.
156/// Use this enum to generate the appropriate model ID for your endpoint.
157///
158/// # Example
159///
160/// ```rust
161/// use claude_sdk::models::{CLAUDE_SONNET_4_5, BedrockRegion};
162///
163/// // Standard regional endpoint (most common)
164/// let model_id = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Standard);
165/// // → "anthropic.claude-sonnet-4-5-20250929-v1:0"
166///
167/// // Global endpoint for better availability
168/// let model_id = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Global);
169/// // → "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
170/// ```
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum BedrockRegion {
173    /// Standard regional endpoint.
174    ///
175    /// The default endpoint type, tied to your specific AWS region.
176    /// Use this when you want predictable latency and data residency.
177    ///
178    /// Model ID format: `anthropic.claude-{model}-v1:0`
179    Standard,
180
181    /// Global endpoint with dynamic routing.
182    ///
183    /// Automatically routes requests to the best available region for
184    /// maximum availability and reduced latency. Only available for
185    /// Claude 4.5+ models.
186    ///
187    /// Model ID format: `global.anthropic.claude-{model}-v1:0`
188    Global,
189
190    /// US regional endpoint.
191    ///
192    /// Routes requests within the United States for data residency
193    /// compliance while still allowing some routing flexibility.
194    ///
195    /// Model ID format: `us.anthropic.claude-{model}-v1:0`
196    US,
197
198    /// EU regional endpoint.
199    ///
200    /// Routes requests within the European Union for GDPR compliance
201    /// and EU data residency requirements.
202    ///
203    /// Model ID format: `eu.anthropic.claude-{model}-v1:0`
204    EU,
205
206    /// Asia-Pacific regional endpoint.
207    ///
208    /// Routes requests within the Asia-Pacific region for reduced
209    /// latency in APAC deployments.
210    ///
211    /// Model ID format: `ap.anthropic.claude-{model}-v1:0`
212    AsiaPacific,
213}
214
215impl BedrockRegion {
216    /// Get the prefix for this region
217    pub fn prefix(&self) -> &'static str {
218        match self {
219            BedrockRegion::Standard => "",
220            BedrockRegion::Global => "global.",
221            BedrockRegion::US => "us.",
222            BedrockRegion::EU => "eu.",
223            BedrockRegion::AsiaPacific => "ap.",
224        }
225    }
226}
227
228impl Model {
229    /// Get the model ID for the Anthropic API
230    pub fn anthropic_id(&self) -> &'static str {
231        self.anthropic_id
232    }
233
234    /// Get the model ID for AWS Bedrock regional endpoint (if available)
235    pub fn bedrock_id(&self) -> Option<&'static str> {
236        self.bedrock_id
237    }
238
239    /// Get the model ID for AWS Bedrock with a specific region prefix
240    ///
241    /// # Example
242    ///
243    /// ```rust
244    /// use claude_sdk::models::{CLAUDE_SONNET_4_5, BedrockRegion};
245    ///
246    /// // Standard regional endpoint
247    /// let regional = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Standard);
248    /// // → Some("anthropic.claude-sonnet-4-5-20250929-v1:0")
249    ///
250    /// // Global endpoint
251    /// let global = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Global);
252    /// // → Some("global.anthropic.claude-sonnet-4-5-20250929-v1:0")
253    ///
254    /// // US regional endpoint
255    /// let us = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::US);
256    /// // → Some("us.anthropic.claude-sonnet-4-5-20250929-v1:0")
257    /// ```
258    pub fn bedrock_id_for_region(&self, region: BedrockRegion) -> Option<String> {
259        self.bedrock_id.map(|id| {
260            let prefix = region.prefix();
261            if prefix.is_empty() {
262                id.to_string()
263            } else {
264                format!("{}{}", prefix, id)
265            }
266        })
267    }
268
269    /// Get the model ID for AWS Bedrock global endpoint (if available)
270    ///
271    /// Global endpoints provide dynamic routing for maximum availability.
272    /// Available for Claude 4.5+ models.
273    ///
274    /// This is a convenience method equivalent to `bedrock_id_for_region(BedrockRegion::Global)`.
275    pub fn bedrock_global_id(&self) -> Option<&'static str> {
276        self.bedrock_global_id
277    }
278
279    /// Get the model ID for Google Vertex AI (if available)
280    pub fn vertex_id(&self) -> Option<&'static str> {
281        self.vertex_id
282    }
283
284    /// Check if this model supports extended context (e.g., 1M tokens)
285    pub fn supports_extended_context(&self) -> bool {
286        self.max_context_tokens_extended.is_some()
287    }
288
289    /// Get the extended context window size (if supported)
290    ///
291    /// Returns `Some(tokens)` if the model supports extended context with beta headers.
292    /// For example, Claude Sonnet 4.5 returns `Some(1_000_000)`.
293    ///
294    /// # Beta Header Required
295    ///
296    /// To use extended context, include the beta header in your API request:
297    /// - Header: `anthropic-beta: context-1m-2025-08-07`
298    ///
299    /// Note: Extended context may incur additional costs beyond 200K tokens.
300    pub fn max_extended_context(&self) -> Option<u32> {
301        self.max_context_tokens_extended
302    }
303
304    /// Validate that a request is compatible with this model's constraints
305    ///
306    /// # Parameters
307    /// - `max_tokens`: The requested maximum output tokens
308    /// - `use_extended_context`: Whether extended context will be used
309    pub fn validate_request(
310        &self,
311        max_tokens: u32,
312        use_extended_context: bool,
313    ) -> Result<(), String> {
314        if max_tokens > self.max_output_tokens {
315            return Err(format!(
316                "Requested max_tokens ({}) exceeds model limit ({})",
317                max_tokens, self.max_output_tokens
318            ));
319        }
320
321        if use_extended_context && !self.supports_extended_context() {
322            return Err(format!(
323                "Model {} does not support extended context",
324                self.name
325            ));
326        }
327
328        Ok(())
329    }
330
331    /// Estimate cost for a request
332    pub fn estimate_cost(&self, input_tokens: u32, output_tokens: u32) -> f64 {
333        (input_tokens as f64 / 1_000_000.0) * self.cost_per_mtok_input
334            + (output_tokens as f64 / 1_000_000.0) * self.cost_per_mtok_output
335    }
336}
337
338//
339// Latest Models (Claude 4.5)
340//
341
342/// Claude Sonnet 4.5 (2025-09-29)
343///
344/// Our smart model for complex agents and coding. Best balance of intelligence,
345/// speed, and cost for most use cases.
346///
347/// Supports 1M context window with beta header `context-1m-2025-08-07`.
348pub const CLAUDE_SONNET_4_5: Model = Model {
349    name: "Claude Sonnet 4.5",
350    family: "sonnet",
351    version: "2025-09-29",
352    anthropic_id: "claude-sonnet-4-5-20250929",
353    bedrock_id: Some("anthropic.claude-sonnet-4-5-20250929-v1:0"),
354    bedrock_global_id: Some("global.anthropic.claude-sonnet-4-5-20250929-v1:0"),
355    vertex_id: Some("claude-sonnet-4-5@20250929"),
356    max_context_tokens: 200_000,
357    max_context_tokens_extended: Some(1_000_000),
358    max_output_tokens: 64_000,
359    supports_vision: true,
360    supports_tools: true,
361    supports_caching: true,
362    supports_extended_thinking: true,
363    supports_effort: false,
364    cost_per_mtok_input: 3.0,
365    cost_per_mtok_output: 15.0,
366    description: "Smart model for complex agents and coding",
367};
368
369/// Claude Haiku 4.5 (2025-10-01)
370///
371/// Our fastest model with near-frontier intelligence.
372pub const CLAUDE_HAIKU_4_5: Model = Model {
373    name: "Claude Haiku 4.5",
374    family: "haiku",
375    version: "2025-10-01",
376    anthropic_id: "claude-haiku-4-5-20251001",
377    bedrock_id: Some("anthropic.claude-haiku-4-5-20251001-v1:0"),
378    bedrock_global_id: Some("global.anthropic.claude-haiku-4-5-20251001-v1:0"),
379    vertex_id: Some("claude-haiku-4-5@20251001"),
380    max_context_tokens: 200_000,
381    max_context_tokens_extended: None,
382    max_output_tokens: 64_000,
383    supports_vision: true,
384    supports_tools: true,
385    supports_caching: true,
386    supports_extended_thinking: true,
387    supports_effort: false,
388    cost_per_mtok_input: 1.0,
389    cost_per_mtok_output: 5.0,
390    description: "Fastest model with near-frontier intelligence",
391};
392
393/// Claude Opus 4.5 (2025-11-01)
394///
395/// Premium model combining maximum intelligence with practical performance.
396/// Supports effort parameter (beta: effort-2025-11-24).
397pub const CLAUDE_OPUS_4_5: Model = Model {
398    name: "Claude Opus 4.5",
399    family: "opus",
400    version: "2025-11-01",
401    anthropic_id: "claude-opus-4-5-20251101",
402    bedrock_id: Some("anthropic.claude-opus-4-5-20251101-v1:0"),
403    bedrock_global_id: Some("global.anthropic.claude-opus-4-5-20251101-v1:0"),
404    vertex_id: Some("claude-opus-4-5@20251101"),
405    max_context_tokens: 200_000,
406    max_context_tokens_extended: None,
407    max_output_tokens: 64_000,
408    supports_vision: true,
409    supports_tools: true,
410    supports_caching: true,
411    supports_extended_thinking: true,
412    supports_effort: true, // Only Opus 4.5 supports effort
413    cost_per_mtok_input: 5.0,
414    cost_per_mtok_output: 25.0,
415    description: "Maximum intelligence with practical performance",
416};
417
418/// Claude Sonnet 4.6 (placeholder date: 2026-01-15)
419///
420/// Best balance of speed and intelligence.
421// TODO: verify version date, IDs, and pricing from API
422pub const CLAUDE_SONNET_4_6: Model = Model {
423    name: "Claude Sonnet 4.6",
424    family: "sonnet",
425    version: "2026-01-15",
426    anthropic_id: "claude-sonnet-4-6-20260115",
427    bedrock_id: Some("anthropic.claude-sonnet-4-6-20260115-v1:0"),
428    bedrock_global_id: Some("global.anthropic.claude-sonnet-4-6-20260115-v1:0"),
429    vertex_id: Some("claude-sonnet-4-6@20260115"),
430    max_context_tokens: 200_000,
431    max_context_tokens_extended: Some(1_000_000),
432    max_output_tokens: 64_000,
433    supports_vision: true,
434    supports_tools: true,
435    supports_caching: true,
436    supports_extended_thinking: true,
437    supports_effort: false,
438    cost_per_mtok_input: 3.0,
439    cost_per_mtok_output: 15.0,
440    description: "Best balance of speed and intelligence",
441};
442
443/// Claude Opus 4.6 (placeholder date: 2026-02-01)
444///
445/// Frontier intelligence for long-running agents and coding.
446// TODO: verify version date, IDs, and pricing from API
447pub const CLAUDE_OPUS_4_6: Model = Model {
448    name: "Claude Opus 4.6",
449    family: "opus",
450    version: "2026-02-01",
451    anthropic_id: "claude-opus-4-6-20260201",
452    bedrock_id: Some("anthropic.claude-opus-4-6-20260201-v1:0"),
453    bedrock_global_id: Some("global.anthropic.claude-opus-4-6-20260201-v1:0"),
454    vertex_id: Some("claude-opus-4-6@20260201"),
455    max_context_tokens: 200_000,
456    max_context_tokens_extended: None,
457    max_output_tokens: 64_000,
458    supports_vision: true,
459    supports_tools: true,
460    supports_caching: true,
461    supports_extended_thinking: true,
462    supports_effort: true,
463    cost_per_mtok_input: 5.0,
464    cost_per_mtok_output: 25.0,
465    description: "Frontier intelligence for long-running agents and coding",
466};
467
468/// Claude Opus 4.7 (placeholder date: 2026-03-01)
469///
470/// Frontier intelligence for long-running agents and coding.
471// TODO: verify version date, IDs, and pricing from API
472pub const CLAUDE_OPUS_4_7: Model = Model {
473    name: "Claude Opus 4.7",
474    family: "opus",
475    version: "2026-03-01",
476    anthropic_id: "claude-opus-4-7-20260301",
477    bedrock_id: Some("anthropic.claude-opus-4-7-20260301-v1:0"),
478    bedrock_global_id: Some("global.anthropic.claude-opus-4-7-20260301-v1:0"),
479    vertex_id: Some("claude-opus-4-7@20260301"),
480    max_context_tokens: 200_000,
481    max_context_tokens_extended: None,
482    max_output_tokens: 64_000,
483    supports_vision: true,
484    supports_tools: true,
485    supports_caching: true,
486    supports_extended_thinking: true,
487    supports_effort: true,
488    cost_per_mtok_input: 5.0,
489    cost_per_mtok_output: 25.0,
490    description: "Frontier intelligence for long-running agents and coding",
491};
492
493/// Claude Opus 4.8 (placeholder date: 2026-04-01)
494///
495/// Frontier intelligence for long-running agents and coding.
496// TODO: verify version date, IDs, and pricing from API
497pub const CLAUDE_OPUS_4_8: Model = Model {
498    name: "Claude Opus 4.8",
499    family: "opus",
500    version: "2026-04-01",
501    anthropic_id: "claude-opus-4-8-20260401",
502    bedrock_id: Some("anthropic.claude-opus-4-8-20260401-v1:0"),
503    bedrock_global_id: Some("global.anthropic.claude-opus-4-8-20260401-v1:0"),
504    vertex_id: Some("claude-opus-4-8@20260401"),
505    max_context_tokens: 200_000,
506    max_context_tokens_extended: None,
507    max_output_tokens: 64_000,
508    supports_vision: true,
509    supports_tools: true,
510    supports_caching: true,
511    supports_extended_thinking: true,
512    supports_effort: true,
513    cost_per_mtok_input: 5.0,
514    cost_per_mtok_output: 25.0,
515    description: "Frontier intelligence for long-running agents and coding",
516};
517
518//
519// Latest Models (Claude 5)
520//
521
522/// Claude Fable 5 (placeholder date: 2026-06-01)
523///
524/// Next generation model for the hardest knowledge work and coding.
525// TODO: verify version date, IDs, and pricing from API
526pub const CLAUDE_FABLE_5: Model = Model {
527    name: "Claude Fable 5",
528    family: "fable",
529    version: "2026-06-01",
530    anthropic_id: "claude-fable-5-20260601",
531    bedrock_id: Some("anthropic.claude-fable-5-20260601-v1:0"),
532    bedrock_global_id: Some("global.anthropic.claude-fable-5-20260601-v1:0"),
533    vertex_id: Some("claude-fable-5@20260601"),
534    max_context_tokens: 200_000,
535    max_context_tokens_extended: Some(1_000_000),
536    max_output_tokens: 64_000,
537    supports_vision: true,
538    supports_tools: true,
539    supports_caching: true,
540    supports_extended_thinking: true,
541    supports_effort: false,
542    cost_per_mtok_input: 3.0,
543    cost_per_mtok_output: 15.0,
544    description: "Next generation for hardest knowledge work and coding",
545};
546
547/// Claude Mythos 5 (placeholder date: 2026-06-15)
548///
549/// Most capable model for cybersecurity and biology research.
550// TODO: verify version date, IDs, and pricing from API
551pub const CLAUDE_MYTHOS_5: Model = Model {
552    name: "Claude Mythos 5",
553    family: "mythos",
554    version: "2026-06-15",
555    anthropic_id: "claude-mythos-5-20260615",
556    bedrock_id: Some("anthropic.claude-mythos-5-20260615-v1:0"),
557    bedrock_global_id: Some("global.anthropic.claude-mythos-5-20260615-v1:0"),
558    vertex_id: Some("claude-mythos-5@20260615"),
559    max_context_tokens: 200_000,
560    max_context_tokens_extended: Some(1_000_000),
561    max_output_tokens: 64_000,
562    supports_vision: true,
563    supports_tools: true,
564    supports_caching: true,
565    supports_extended_thinking: true,
566    supports_effort: false,
567    cost_per_mtok_input: 5.0,
568    cost_per_mtok_output: 25.0,
569    description: "Most capable for cybersecurity and biology research",
570};
571
572//
573// Legacy Models (Claude 4.x and 3.x)
574//
575
576/// Claude Opus 4.1 (2025-08-05)
577pub const CLAUDE_OPUS_4_1: Model = Model {
578    name: "Claude Opus 4.1",
579    family: "opus",
580    version: "2025-08-05",
581    anthropic_id: "claude-opus-4-1-20250805",
582    bedrock_id: Some("anthropic.claude-opus-4-1-20250805-v1:0"),
583    bedrock_global_id: None,
584    vertex_id: Some("claude-opus-4-1@20250805"),
585    max_context_tokens: 200_000,
586    max_context_tokens_extended: None,
587    max_output_tokens: 32_000,
588    supports_vision: true,
589    supports_tools: true,
590    supports_caching: true,
591    supports_extended_thinking: true,
592    supports_effort: false,
593    cost_per_mtok_input: 15.0,
594    cost_per_mtok_output: 75.0,
595    description: "Previous generation powerful model",
596};
597
598/// Claude Sonnet 4 (2025-05-14)
599///
600/// Supports 1M context window with beta header `context-1m-2025-08-07`.
601pub const CLAUDE_SONNET_4: Model = Model {
602    name: "Claude Sonnet 4",
603    family: "sonnet",
604    version: "2025-05-14",
605    anthropic_id: "claude-sonnet-4-20250514",
606    bedrock_id: Some("anthropic.claude-sonnet-4-20250514-v1:0"),
607    bedrock_global_id: None,
608    vertex_id: Some("claude-sonnet-4@20250514"),
609    max_context_tokens: 200_000,
610    max_context_tokens_extended: Some(1_000_000),
611    max_output_tokens: 64_000,
612    supports_vision: true,
613    supports_tools: true,
614    supports_caching: true,
615    supports_extended_thinking: true,
616    supports_effort: false,
617    cost_per_mtok_input: 3.0,
618    cost_per_mtok_output: 15.0,
619    description: "Previous generation balanced model",
620};
621
622/// Claude Sonnet 3.7 (2025-02-19)
623///
624/// Supports 128K output with beta header `output-128k-2025-02-19`.
625pub const CLAUDE_SONNET_3_7: Model = Model {
626    name: "Claude Sonnet 3.7",
627    family: "sonnet",
628    version: "2025-02-19",
629    anthropic_id: "claude-3-7-sonnet-20250219",
630    bedrock_id: Some("anthropic.claude-3-7-sonnet-20250219-v1:0"),
631    bedrock_global_id: None,
632    vertex_id: Some("claude-3-7-sonnet@20250219"),
633    max_context_tokens: 200_000,
634    max_context_tokens_extended: None,
635    max_output_tokens: 64_000, // 128K with beta header
636    supports_vision: true,
637    supports_tools: true,
638    supports_caching: true,
639    supports_extended_thinking: true,
640    supports_effort: false,
641    cost_per_mtok_input: 3.0,
642    cost_per_mtok_output: 15.0,
643    description: "Claude 3.7 balanced model",
644};
645
646/// Claude Opus 4 (2025-05-14)
647pub const CLAUDE_OPUS_4: Model = Model {
648    name: "Claude Opus 4",
649    family: "opus",
650    version: "2025-05-14",
651    anthropic_id: "claude-opus-4-20250514",
652    bedrock_id: Some("anthropic.claude-opus-4-20250514-v1:0"),
653    bedrock_global_id: None,
654    vertex_id: Some("claude-opus-4@20250514"),
655    max_context_tokens: 200_000,
656    max_context_tokens_extended: None,
657    max_output_tokens: 32_000,
658    supports_vision: true,
659    supports_tools: true,
660    supports_caching: true,
661    supports_extended_thinking: true,
662    supports_effort: false,
663    cost_per_mtok_input: 15.0,
664    cost_per_mtok_output: 75.0,
665    description: "Claude 4 powerful model",
666};
667
668/// Claude Haiku 3.5 (2024-10-22)
669pub const CLAUDE_HAIKU_3_5: Model = Model {
670    name: "Claude Haiku 3.5",
671    family: "haiku",
672    version: "2024-10-22",
673    anthropic_id: "claude-3-5-haiku-20241022",
674    bedrock_id: Some("anthropic.claude-3-5-haiku-20241022-v1:0"),
675    bedrock_global_id: None,
676    vertex_id: Some("claude-3-5-haiku@20241022"),
677    max_context_tokens: 200_000,
678    max_context_tokens_extended: None,
679    max_output_tokens: 8_192,
680    supports_vision: true,
681    supports_tools: true,
682    supports_caching: true,
683    supports_extended_thinking: false,
684    supports_effort: false,
685    cost_per_mtok_input: 0.80,
686    cost_per_mtok_output: 4.0,
687    description: "Fast and efficient model",
688};
689
690/// Claude Haiku 3 (2024-03-07)
691pub const CLAUDE_HAIKU_3: Model = Model {
692    name: "Claude Haiku 3",
693    family: "haiku",
694    version: "2024-03-07",
695    anthropic_id: "claude-3-haiku-20240307",
696    bedrock_id: Some("anthropic.claude-3-haiku-20240307-v1:0"),
697    bedrock_global_id: None,
698    vertex_id: Some("claude-3-haiku@20240307"),
699    max_context_tokens: 200_000,
700    max_context_tokens_extended: None,
701    max_output_tokens: 4_096,
702    supports_vision: true,
703    supports_tools: true,
704    supports_caching: true,
705    supports_extended_thinking: false,
706    supports_effort: false,
707    cost_per_mtok_input: 0.25,
708    cost_per_mtok_output: 1.25,
709    description: "Original fast model",
710};
711
712/// List of all available models (latest first)
713pub const ALL_MODELS: &[&Model] = &[
714    // Latest (Claude 5)
715    &CLAUDE_MYTHOS_5,
716    &CLAUDE_FABLE_5,
717    // Claude 4.6+
718    &CLAUDE_OPUS_4_8,
719    &CLAUDE_OPUS_4_7,
720    &CLAUDE_OPUS_4_6,
721    &CLAUDE_SONNET_4_6,
722    // Claude 4.5
723    &CLAUDE_SONNET_4_5,
724    &CLAUDE_HAIKU_4_5,
725    &CLAUDE_OPUS_4_5,
726    // Legacy (Claude 4.x and 3.x)
727    &CLAUDE_OPUS_4_1,
728    &CLAUDE_SONNET_4,
729    &CLAUDE_SONNET_3_7,
730    &CLAUDE_OPUS_4,
731    &CLAUDE_HAIKU_3_5,
732    &CLAUDE_HAIKU_3,
733];
734
735/// Lookup a model by its Anthropic API ID.
736///
737/// Returns the model metadata if found, or `None` if the ID doesn't match any model.
738///
739/// # Example
740///
741/// ```rust
742/// use claude_sdk::models::get_model_by_anthropic_id;
743///
744/// let model = get_model_by_anthropic_id("claude-sonnet-4-5-20250929").unwrap();
745/// assert_eq!(model.name, "Claude Sonnet 4.5");
746/// assert_eq!(model.max_output_tokens, 64_000);
747///
748/// // Unknown models return None
749/// assert!(get_model_by_anthropic_id("unknown-model").is_none());
750/// ```
751pub fn get_model_by_anthropic_id(id: &str) -> Option<&'static Model> {
752    ALL_MODELS.iter().find(|m| m.anthropic_id == id).copied()
753}
754
755/// Lookup a model by its Bedrock ID (any region prefix).
756///
757/// This function is flexible and accepts model IDs from any Bedrock endpoint type.
758/// It will automatically strip regional prefixes when matching.
759///
760/// # Supported Formats
761///
762/// - Standard regional: `anthropic.claude-sonnet-4-5-20250929-v1:0`
763/// - Global: `global.anthropic.claude-sonnet-4-5-20250929-v1:0`
764/// - US regional: `us.anthropic.claude-sonnet-4-5-20250929-v1:0`
765/// - EU regional: `eu.anthropic.claude-sonnet-4-5-20250929-v1:0`
766/// - AP regional: `ap.anthropic.claude-sonnet-4-5-20250929-v1:0`
767///
768/// # Example
769///
770/// ```rust
771/// use claude_sdk::models::get_model_by_bedrock_id;
772///
773/// // All of these return the same model
774/// let m1 = get_model_by_bedrock_id("anthropic.claude-sonnet-4-5-20250929-v1:0");
775/// let m2 = get_model_by_bedrock_id("global.anthropic.claude-sonnet-4-5-20250929-v1:0");
776/// let m3 = get_model_by_bedrock_id("us.anthropic.claude-sonnet-4-5-20250929-v1:0");
777///
778/// assert_eq!(m1.unwrap().name, "Claude Sonnet 4.5");
779/// assert_eq!(m2.unwrap().name, "Claude Sonnet 4.5");
780/// assert_eq!(m3.unwrap().name, "Claude Sonnet 4.5");
781/// ```
782pub fn get_model_by_bedrock_id(id: &str) -> Option<&'static Model> {
783    // Try exact match first
784    if let Some(model) = ALL_MODELS
785        .iter()
786        .find(|m| m.bedrock_id == Some(id) || m.bedrock_global_id == Some(id))
787    {
788        return Some(*model);
789    }
790
791    // Try stripping regional prefixes and matching base ID
792    let base_id = id
793        .strip_prefix("global.")
794        .or_else(|| id.strip_prefix("us."))
795        .or_else(|| id.strip_prefix("eu."))
796        .or_else(|| id.strip_prefix("ap."))
797        .unwrap_or(id);
798
799    ALL_MODELS
800        .iter()
801        .find(|m| m.bedrock_id == Some(base_id))
802        .copied()
803}
804
805/// Lookup a model by its Google Vertex AI ID.
806///
807/// # Example
808///
809/// ```rust
810/// use claude_sdk::models::get_model_by_vertex_id;
811///
812/// let model = get_model_by_vertex_id("claude-sonnet-4-5@20250929").unwrap();
813/// assert_eq!(model.name, "Claude Sonnet 4.5");
814///
815/// // Unknown models return None
816/// assert!(get_model_by_vertex_id("unknown-model").is_none());
817/// ```
818pub fn get_model_by_vertex_id(id: &str) -> Option<&'static Model> {
819    ALL_MODELS.iter().find(|m| m.vertex_id == Some(id)).copied()
820}
821
822/// Lookup a model by any ID format.
823///
824/// This is the most flexible lookup function. It tries to match the ID against:
825/// 1. Anthropic API IDs (e.g., `claude-sonnet-4-5-20250929`)
826/// 2. AWS Bedrock IDs (e.g., `anthropic.claude-sonnet-4-5-20250929-v1:0`)
827/// 3. Google Vertex AI IDs (e.g., `claude-sonnet-4-5@20250929`)
828///
829/// Use this when you need to accept model IDs from different sources.
830///
831/// # Example
832///
833/// ```rust
834/// use claude_sdk::models::get_model;
835///
836/// // Anthropic ID
837/// let m1 = get_model("claude-sonnet-4-5-20250929").unwrap();
838///
839/// // Bedrock ID (any regional prefix)
840/// let m2 = get_model("anthropic.claude-sonnet-4-5-20250929-v1:0").unwrap();
841/// let m3 = get_model("global.anthropic.claude-sonnet-4-5-20250929-v1:0").unwrap();
842///
843/// // Vertex AI ID
844/// let m4 = get_model("claude-sonnet-4-5@20250929").unwrap();
845///
846/// // All return the same model
847/// assert_eq!(m1.name, "Claude Sonnet 4.5");
848/// assert_eq!(m2.name, "Claude Sonnet 4.5");
849/// assert_eq!(m3.name, "Claude Sonnet 4.5");
850/// assert_eq!(m4.name, "Claude Sonnet 4.5");
851/// ```
852pub fn get_model(id: &str) -> Option<&'static Model> {
853    get_model_by_anthropic_id(id)
854        .or_else(|| get_model_by_bedrock_id(id))
855        .or_else(|| get_model_by_vertex_id(id))
856}
857
858#[cfg(test)]
859#[allow(clippy::assertions_on_constants)]
860mod tests {
861    use super::*;
862
863    #[test]
864    fn test_model_constants() {
865        assert_eq!(CLAUDE_SONNET_4_5.anthropic_id, "claude-sonnet-4-5-20250929");
866        assert_eq!(
867            CLAUDE_SONNET_4_5.bedrock_id,
868            Some("anthropic.claude-sonnet-4-5-20250929-v1:0")
869        );
870        assert_eq!(
871            CLAUDE_SONNET_4_5.bedrock_global_id,
872            Some("global.anthropic.claude-sonnet-4-5-20250929-v1:0")
873        );
874        assert_eq!(
875            CLAUDE_SONNET_4_5.max_context_tokens_extended,
876            Some(1_000_000)
877        );
878    }
879
880    #[test]
881    fn test_model_lookup_anthropic() {
882        let model = get_model_by_anthropic_id("claude-sonnet-4-5-20250929");
883        assert!(model.is_some());
884        assert_eq!(model.unwrap().name, "Claude Sonnet 4.5");
885    }
886
887    #[test]
888    fn test_model_lookup_bedrock_regional() {
889        let model = get_model_by_bedrock_id("anthropic.claude-sonnet-4-5-20250929-v1:0");
890        assert!(model.is_some());
891        assert_eq!(model.unwrap().name, "Claude Sonnet 4.5");
892    }
893
894    #[test]
895    fn test_model_lookup_bedrock_global() {
896        let model = get_model_by_bedrock_id("global.anthropic.claude-sonnet-4-5-20250929-v1:0");
897        assert!(model.is_some());
898        assert_eq!(model.unwrap().name, "Claude Sonnet 4.5");
899    }
900
901    #[test]
902    fn test_model_lookup_bedrock_us() {
903        let model = get_model_by_bedrock_id("us.anthropic.claude-sonnet-4-5-20250929-v1:0");
904        assert!(model.is_some());
905        assert_eq!(model.unwrap().name, "Claude Sonnet 4.5");
906    }
907
908    #[test]
909    fn test_model_lookup_bedrock_eu() {
910        let model = get_model_by_bedrock_id("eu.anthropic.claude-sonnet-4-5-20250929-v1:0");
911        assert!(model.is_some());
912        assert_eq!(model.unwrap().name, "Claude Sonnet 4.5");
913    }
914
915    #[test]
916    fn test_model_lookup_bedrock_ap() {
917        let model = get_model_by_bedrock_id("ap.anthropic.claude-sonnet-4-5-20250929-v1:0");
918        assert!(model.is_some());
919        assert_eq!(model.unwrap().name, "Claude Sonnet 4.5");
920    }
921
922    #[test]
923    fn test_bedrock_id_for_region() {
924        // Standard regional endpoint
925        let regional = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Standard);
926        assert_eq!(
927            regional.as_deref(),
928            Some("anthropic.claude-sonnet-4-5-20250929-v1:0")
929        );
930
931        // Global endpoint
932        let global = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::Global);
933        assert_eq!(
934            global.as_deref(),
935            Some("global.anthropic.claude-sonnet-4-5-20250929-v1:0")
936        );
937
938        // US regional endpoint
939        let us = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::US);
940        assert_eq!(
941            us.as_deref(),
942            Some("us.anthropic.claude-sonnet-4-5-20250929-v1:0")
943        );
944
945        // EU regional endpoint
946        let eu = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::EU);
947        assert_eq!(
948            eu.as_deref(),
949            Some("eu.anthropic.claude-sonnet-4-5-20250929-v1:0")
950        );
951
952        // AP regional endpoint
953        let ap = CLAUDE_SONNET_4_5.bedrock_id_for_region(BedrockRegion::AsiaPacific);
954        assert_eq!(
955            ap.as_deref(),
956            Some("ap.anthropic.claude-sonnet-4-5-20250929-v1:0")
957        );
958    }
959
960    #[test]
961    fn test_model_lookup_any() {
962        // Should work with Anthropic, Bedrock regional, and all Bedrock prefixes
963        assert!(get_model("claude-sonnet-4-5-20250929").is_some());
964        assert!(get_model("anthropic.claude-sonnet-4-5-20250929-v1:0").is_some());
965        assert!(get_model("global.anthropic.claude-sonnet-4-5-20250929-v1:0").is_some());
966        assert!(get_model("us.anthropic.claude-sonnet-4-5-20250929-v1:0").is_some());
967        assert!(get_model("eu.anthropic.claude-sonnet-4-5-20250929-v1:0").is_some());
968        assert!(get_model("ap.anthropic.claude-sonnet-4-5-20250929-v1:0").is_some());
969    }
970
971    #[test]
972    fn test_validate_request() {
973        assert!(CLAUDE_SONNET_4_5.validate_request(1024, false).is_ok());
974        assert!(CLAUDE_SONNET_4_5.validate_request(64_000, false).is_ok());
975        assert!(CLAUDE_SONNET_4_5.validate_request(100_000, false).is_err());
976
977        // Test extended context validation
978        assert!(CLAUDE_SONNET_4_5.validate_request(1024, true).is_ok());
979        assert!(CLAUDE_HAIKU_4_5.validate_request(1024, true).is_err()); // Doesn't support 1M
980    }
981
982    #[test]
983    fn test_extended_context_support() {
984        // Models that support 1M context
985        assert!(CLAUDE_SONNET_4_5.supports_extended_context());
986        assert_eq!(CLAUDE_SONNET_4_5.max_extended_context(), Some(1_000_000));
987        assert!(CLAUDE_SONNET_4.supports_extended_context());
988
989        // Models that don't support 1M context
990        assert!(!CLAUDE_HAIKU_4_5.supports_extended_context());
991        assert_eq!(CLAUDE_HAIKU_4_5.max_extended_context(), None);
992        assert!(!CLAUDE_OPUS_4_5.supports_extended_context());
993    }
994
995    #[test]
996    fn test_bedrock_global_endpoints() {
997        // Claude 4.5 models support global endpoints
998        assert!(CLAUDE_SONNET_4_5.bedrock_global_id().is_some());
999        assert!(CLAUDE_HAIKU_4_5.bedrock_global_id().is_some());
1000        assert!(CLAUDE_OPUS_4_5.bedrock_global_id().is_some());
1001
1002        // Legacy models don't support global endpoints
1003        assert!(CLAUDE_SONNET_4.bedrock_global_id().is_none());
1004        assert!(CLAUDE_HAIKU_3_5.bedrock_global_id().is_none());
1005    }
1006
1007    #[test]
1008    fn test_estimate_cost() {
1009        let cost = CLAUDE_SONNET_4_5.estimate_cost(1000, 500);
1010        // $3/MTok input + $15/MTok output
1011        // = (1000/1M * 3) + (500/1M * 15)
1012        // = 0.003 + 0.0075 = 0.0105
1013        assert!((cost - 0.0105).abs() < 0.0001);
1014    }
1015
1016    #[test]
1017    fn test_all_models_have_unique_ids() {
1018        let mut ids = std::collections::HashSet::new();
1019        for model in ALL_MODELS {
1020            assert!(ids.insert(model.anthropic_id));
1021        }
1022    }
1023
1024    // --- New model tests ---
1025
1026    #[test]
1027    fn test_claude_sonnet_4_6_exists() {
1028        assert_eq!(CLAUDE_SONNET_4_6.name, "Claude Sonnet 4.6");
1029        assert_eq!(CLAUDE_SONNET_4_6.family, "sonnet");
1030        assert_eq!(CLAUDE_SONNET_4_6.anthropic_id, "claude-sonnet-4-6-20260115");
1031        assert!(CLAUDE_SONNET_4_6.supports_vision);
1032        assert!(CLAUDE_SONNET_4_6.supports_tools);
1033        assert!(CLAUDE_SONNET_4_6.supports_caching);
1034        assert!(CLAUDE_SONNET_4_6.supports_extended_thinking);
1035        assert!(!CLAUDE_SONNET_4_6.supports_effort);
1036        assert!(CLAUDE_SONNET_4_6.bedrock_global_id.is_some());
1037    }
1038
1039    #[test]
1040    fn test_claude_sonnet_4_6_lookup() {
1041        let model = get_model_by_anthropic_id("claude-sonnet-4-6-20260115");
1042        assert!(model.is_some());
1043        assert_eq!(model.unwrap().name, "Claude Sonnet 4.6");
1044
1045        let model = get_model("claude-sonnet-4-6-20260115");
1046        assert!(model.is_some());
1047    }
1048
1049    #[test]
1050    fn test_claude_opus_4_6_exists() {
1051        assert_eq!(CLAUDE_OPUS_4_6.name, "Claude Opus 4.6");
1052        assert_eq!(CLAUDE_OPUS_4_6.family, "opus");
1053        assert_eq!(CLAUDE_OPUS_4_6.anthropic_id, "claude-opus-4-6-20260201");
1054        assert!(CLAUDE_OPUS_4_6.supports_vision);
1055        assert!(CLAUDE_OPUS_4_6.supports_tools);
1056        assert!(CLAUDE_OPUS_4_6.supports_caching);
1057        assert!(CLAUDE_OPUS_4_6.supports_extended_thinking);
1058        assert!(CLAUDE_OPUS_4_6.supports_effort);
1059        assert!(CLAUDE_OPUS_4_6.bedrock_global_id.is_some());
1060    }
1061
1062    #[test]
1063    fn test_claude_opus_4_6_lookup() {
1064        let model = get_model_by_anthropic_id("claude-opus-4-6-20260201");
1065        assert!(model.is_some());
1066        assert_eq!(model.unwrap().name, "Claude Opus 4.6");
1067
1068        let model = get_model("claude-opus-4-6-20260201");
1069        assert!(model.is_some());
1070    }
1071
1072    #[test]
1073    fn test_claude_opus_4_7_exists() {
1074        assert_eq!(CLAUDE_OPUS_4_7.name, "Claude Opus 4.7");
1075        assert_eq!(CLAUDE_OPUS_4_7.family, "opus");
1076        assert_eq!(CLAUDE_OPUS_4_7.anthropic_id, "claude-opus-4-7-20260301");
1077        assert!(CLAUDE_OPUS_4_7.supports_vision);
1078        assert!(CLAUDE_OPUS_4_7.supports_tools);
1079        assert!(CLAUDE_OPUS_4_7.supports_caching);
1080        assert!(CLAUDE_OPUS_4_7.supports_extended_thinking);
1081        assert!(CLAUDE_OPUS_4_7.supports_effort);
1082        assert!(CLAUDE_OPUS_4_7.bedrock_global_id.is_some());
1083    }
1084
1085    #[test]
1086    fn test_claude_opus_4_7_lookup() {
1087        let model = get_model_by_anthropic_id("claude-opus-4-7-20260301");
1088        assert!(model.is_some());
1089        assert_eq!(model.unwrap().name, "Claude Opus 4.7");
1090
1091        let model = get_model("claude-opus-4-7-20260301");
1092        assert!(model.is_some());
1093    }
1094
1095    #[test]
1096    fn test_claude_opus_4_8_exists() {
1097        assert_eq!(CLAUDE_OPUS_4_8.name, "Claude Opus 4.8");
1098        assert_eq!(CLAUDE_OPUS_4_8.family, "opus");
1099        assert_eq!(CLAUDE_OPUS_4_8.anthropic_id, "claude-opus-4-8-20260401");
1100        assert!(CLAUDE_OPUS_4_8.supports_vision);
1101        assert!(CLAUDE_OPUS_4_8.supports_tools);
1102        assert!(CLAUDE_OPUS_4_8.supports_caching);
1103        assert!(CLAUDE_OPUS_4_8.supports_extended_thinking);
1104        assert!(CLAUDE_OPUS_4_8.supports_effort);
1105        assert!(CLAUDE_OPUS_4_8.bedrock_global_id.is_some());
1106    }
1107
1108    #[test]
1109    fn test_claude_opus_4_8_lookup() {
1110        let model = get_model_by_anthropic_id("claude-opus-4-8-20260401");
1111        assert!(model.is_some());
1112        assert_eq!(model.unwrap().name, "Claude Opus 4.8");
1113
1114        let model = get_model("claude-opus-4-8-20260401");
1115        assert!(model.is_some());
1116    }
1117
1118    #[test]
1119    fn test_claude_fable_5_exists() {
1120        assert_eq!(CLAUDE_FABLE_5.name, "Claude Fable 5");
1121        assert_eq!(CLAUDE_FABLE_5.family, "fable");
1122        assert_eq!(CLAUDE_FABLE_5.anthropic_id, "claude-fable-5-20260601");
1123        assert!(CLAUDE_FABLE_5.supports_vision);
1124        assert!(CLAUDE_FABLE_5.supports_tools);
1125        assert!(CLAUDE_FABLE_5.supports_caching);
1126        assert!(CLAUDE_FABLE_5.supports_extended_thinking);
1127        assert!(!CLAUDE_FABLE_5.supports_effort);
1128        assert!(CLAUDE_FABLE_5.bedrock_global_id.is_some());
1129        assert_eq!(CLAUDE_FABLE_5.max_context_tokens_extended, Some(1_000_000));
1130    }
1131
1132    #[test]
1133    fn test_claude_fable_5_lookup() {
1134        let model = get_model_by_anthropic_id("claude-fable-5-20260601");
1135        assert!(model.is_some());
1136        assert_eq!(model.unwrap().name, "Claude Fable 5");
1137
1138        let model = get_model("claude-fable-5-20260601");
1139        assert!(model.is_some());
1140    }
1141
1142    #[test]
1143    fn test_claude_mythos_5_exists() {
1144        assert_eq!(CLAUDE_MYTHOS_5.name, "Claude Mythos 5");
1145        assert_eq!(CLAUDE_MYTHOS_5.family, "mythos");
1146        assert_eq!(CLAUDE_MYTHOS_5.anthropic_id, "claude-mythos-5-20260615");
1147        assert!(CLAUDE_MYTHOS_5.supports_vision);
1148        assert!(CLAUDE_MYTHOS_5.supports_tools);
1149        assert!(CLAUDE_MYTHOS_5.supports_caching);
1150        assert!(CLAUDE_MYTHOS_5.supports_extended_thinking);
1151        assert!(!CLAUDE_MYTHOS_5.supports_effort);
1152        assert!(CLAUDE_MYTHOS_5.bedrock_global_id.is_some());
1153        assert_eq!(CLAUDE_MYTHOS_5.max_context_tokens_extended, Some(1_000_000));
1154    }
1155
1156    #[test]
1157    fn test_claude_mythos_5_lookup() {
1158        let model = get_model_by_anthropic_id("claude-mythos-5-20260615");
1159        assert!(model.is_some());
1160        assert_eq!(model.unwrap().name, "Claude Mythos 5");
1161
1162        let model = get_model("claude-mythos-5-20260615");
1163        assert!(model.is_some());
1164    }
1165
1166    #[test]
1167    fn test_new_models_in_all_models() {
1168        let ids: Vec<&str> = ALL_MODELS.iter().map(|m| m.anthropic_id).collect();
1169        assert!(ids.contains(&"claude-sonnet-4-6-20260115"));
1170        assert!(ids.contains(&"claude-opus-4-6-20260201"));
1171        assert!(ids.contains(&"claude-opus-4-7-20260301"));
1172        assert!(ids.contains(&"claude-opus-4-8-20260401"));
1173        assert!(ids.contains(&"claude-fable-5-20260601"));
1174        assert!(ids.contains(&"claude-mythos-5-20260615"));
1175    }
1176
1177    #[test]
1178    fn test_new_models_bedrock_global_ids() {
1179        assert!(CLAUDE_SONNET_4_6.bedrock_global_id().is_some());
1180        assert!(CLAUDE_OPUS_4_6.bedrock_global_id().is_some());
1181        assert!(CLAUDE_OPUS_4_7.bedrock_global_id().is_some());
1182        assert!(CLAUDE_OPUS_4_8.bedrock_global_id().is_some());
1183        assert!(CLAUDE_FABLE_5.bedrock_global_id().is_some());
1184        assert!(CLAUDE_MYTHOS_5.bedrock_global_id().is_some());
1185    }
1186}