mixtape_core/models/
mod.rs

1//! Pre-configured model definitions
2//!
3//! This module contains model structs for various LLM providers.
4//! Each model implements the `Model` trait and provider-specific traits
5//! like `BedrockModel` or `AnthropicModel`.
6//!
7//! Models are organized by vendor:
8//! - `claude` - Anthropic Claude models
9//! - `llama` - Meta Llama models
10//! - `nova` - Amazon Nova models
11//! - `mistral` - Mistral AI models
12//! - `cohere` - Cohere models
13//! - `qwen` - Alibaba Qwen models
14//! - `google` - Google models
15//! - `deepseek` - DeepSeek models
16//! - `kimi` - Moonshot Kimi models
17
18mod claude;
19mod cohere;
20mod deepseek;
21mod google;
22mod kimi;
23mod llama;
24mod mistral;
25mod nova;
26mod qwen;
27
28// Re-export all models at the module level
29pub use claude::*;
30pub use cohere::*;
31pub use deepseek::*;
32pub use google::*;
33pub use kimi::*;
34pub use llama::*;
35pub use mistral::*;
36pub use nova::*;
37pub use qwen::*;
38
39/// Macro to generate model structs with trait implementations
40///
41/// This macro creates a model struct that implements:
42/// - `Model` trait (always)
43/// - `BedrockModel` trait (always)
44/// - `AnthropicModel` trait (if `anthropic_id` is provided)
45///
46/// Optional fields:
47/// - `anthropic_id` - Anthropic API model ID (enables AnthropicModel trait)
48/// - `default_inference_profile` - Default inference profile for Bedrock (e.g., Global)
49macro_rules! define_model {
50    (
51        $(#[$meta:meta])*
52        $name:ident {
53            display_name: $display_name:expr,
54            bedrock_id: $bedrock_id:expr,
55            context_tokens: $context_tokens:expr,
56            output_tokens: $output_tokens:expr
57            $(, anthropic_id: $anthropic_id:expr)?
58            $(, default_inference_profile: $profile:expr)?
59        }
60    ) => {
61        $(#[$meta])*
62        #[derive(Debug, Clone, Copy, Default)]
63        pub struct $name;
64
65        impl $crate::model::Model for $name {
66            fn name(&self) -> &'static str {
67                $display_name
68            }
69
70            fn max_context_tokens(&self) -> usize {
71                $context_tokens
72            }
73
74            fn max_output_tokens(&self) -> usize {
75                $output_tokens
76            }
77
78            fn estimate_token_count(&self, text: &str) -> usize {
79                // Default heuristic: ~4 characters per token
80                text.len().div_ceil(4)
81            }
82        }
83
84        impl $crate::model::BedrockModel for $name {
85            fn bedrock_id(&self) -> &'static str {
86                $bedrock_id
87            }
88
89            $crate::models::define_model!(@inference_profile $($profile)?);
90        }
91
92        $(
93            impl $crate::model::AnthropicModel for $name {
94                fn anthropic_id(&self) -> &'static str {
95                    $anthropic_id
96                }
97            }
98        )?
99    };
100
101    // Helper: generate default_inference_profile method if profile is specified
102    (@inference_profile $profile:expr) => {
103        fn default_inference_profile(&self) -> $crate::model::InferenceProfile {
104            $profile
105        }
106    };
107
108    // Helper: no-op if no profile specified (uses trait default)
109    (@inference_profile) => {};
110}
111
112// Make the macro available to submodules
113pub(crate) use define_model;
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::model::{AnthropicModel, BedrockModel, Model};
119
120    #[test]
121    fn test_claude_implements_both_traits() {
122        let model = ClaudeSonnet4_5;
123
124        // Model trait
125        assert_eq!(model.name(), "Claude Sonnet 4.5");
126        assert_eq!(model.max_context_tokens(), 200_000);
127        assert_eq!(model.max_output_tokens(), 64_000);
128
129        // BedrockModel trait
130        assert!(model.bedrock_id().contains("claude-sonnet-4-5"));
131
132        // AnthropicModel trait
133        assert!(model.anthropic_id().contains("claude-sonnet-4-5"));
134    }
135
136    #[test]
137    fn test_nova_only_implements_bedrock() {
138        let model = NovaMicro;
139
140        // Model trait
141        assert_eq!(model.name(), "Nova Micro");
142
143        // BedrockModel trait
144        assert!(model.bedrock_id().contains("nova-micro"));
145
146        // NovaMicro does NOT implement AnthropicModel - compile-time check
147    }
148
149    #[test]
150    fn test_models_are_copy() {
151        let model = ClaudeSonnet4_5;
152        let copy = model;
153        assert_eq!(model.name(), copy.name());
154    }
155
156    #[test]
157    fn test_model_ids_are_valid() {
158        // Verify model ID format (no spaces, valid characters)
159        let models: Vec<&dyn BedrockModel> = vec![
160            &Claude3_7Sonnet,
161            &ClaudeOpus4,
162            &ClaudeSonnet4,
163            &ClaudeSonnet4_5,
164            &ClaudeHaiku4_5,
165            &ClaudeOpus4_5,
166            &NovaMicro,
167            &NovaLite,
168            &Nova2Lite,
169            &NovaPro,
170            &NovaPremier,
171            &MistralLarge3,
172            &MagistralSmall,
173            &CohereCommandRPlus,
174            &Qwen3_235B,
175            &Qwen3Coder480B,
176            &Gemma3_27B,
177            &DeepSeekR1,
178            &DeepSeekV3,
179            &KimiK2Thinking,
180            &Llama4Scout17B,
181            &Llama4Maverick17B,
182            &Llama3_3_70B,
183            &Llama3_2_90B,
184            &Llama3_2_11B,
185            &Llama3_2_3B,
186            &Llama3_2_1B,
187            &Llama3_1_405B,
188            &Llama3_1_70B,
189            &Llama3_1_8B,
190        ];
191
192        for model in models {
193            let id = model.bedrock_id();
194            assert!(
195                !id.contains(' '),
196                "Model ID should not contain spaces: {}",
197                id
198            );
199            assert!(
200                id.contains('.'),
201                "Model ID should contain provider prefix: {}",
202                id
203            );
204        }
205    }
206}