cc_sdk/
model_recommendation.rs1use std::collections::HashMap;
7
8#[derive(Debug, Clone)]
13pub struct ModelRecommendation {
14 recommendations: HashMap<String, String>,
15}
16
17impl ModelRecommendation {
18 pub fn with_defaults() -> Self {
35 let mut map = HashMap::new();
36
37 map.insert("simple".to_string(), "claude-haiku-4-5-20251001".to_string());
39 map.insert("fast".to_string(), "claude-haiku-4-5-20251001".to_string());
40 map.insert("cheap".to_string(), "claude-haiku-4-5-20251001".to_string());
41 map.insert("quick".to_string(), "claude-haiku-4-5-20251001".to_string());
42
43 map.insert("balanced".to_string(), "claude-sonnet-4-5-20250929".to_string());
45 map.insert("general".to_string(), "claude-sonnet-4-5-20250929".to_string());
46 map.insert("normal".to_string(), "claude-sonnet-4-5-20250929".to_string());
47 map.insert("standard".to_string(), "claude-sonnet-4-5-20250929".to_string());
48 map.insert("latest".to_string(), "claude-sonnet-4-5-20250929".to_string());
49
50 map.insert("complex".to_string(), "claude-opus-4-6".to_string());
52 map.insert("best".to_string(), "claude-opus-4-6".to_string());
53 map.insert("quality".to_string(), "claude-opus-4-6".to_string());
54 map.insert("critical".to_string(), "claude-opus-4-6".to_string());
55 map.insert("advanced".to_string(), "claude-opus-4-6".to_string());
56
57 Self { recommendations: map }
58 }
59
60 pub fn custom(recommendations: HashMap<String, String>) -> Self {
75 Self { recommendations }
76 }
77
78 pub fn suggest(&self, task_type: &str) -> Option<&str> {
96 self.recommendations.get(task_type).map(|s| s.as_str())
97 }
98
99 pub fn add(&mut self, task_type: impl Into<String>, model: impl Into<String>) {
111 self.recommendations.insert(task_type.into(), model.into());
112 }
113
114 pub fn remove(&mut self, task_type: &str) -> Option<String> {
116 self.recommendations.remove(task_type)
117 }
118
119 pub fn task_types(&self) -> Vec<&str> {
121 self.recommendations.keys().map(|s| s.as_str()).collect()
122 }
123
124 pub fn all_recommendations(&self) -> &HashMap<String, String> {
126 &self.recommendations
127 }
128}
129
130impl Default for ModelRecommendation {
131 fn default() -> Self {
132 ModelRecommendation::with_defaults()
134 }
135}
136
137pub fn cheapest_model() -> &'static str {
140 "claude-haiku-4-5-20251001"
141}
142
143pub fn balanced_model() -> &'static str {
145 "claude-sonnet-4-5-20250929"
146}
147
148pub fn latest_sonnet() -> &'static str {
150 "claude-sonnet-4-5-20250929"
151}
152
153pub fn best_model() -> &'static str {
155 "claude-opus-4-6"
156}
157
158pub fn estimate_cost_multiplier(model: &str) -> f64 {
178 match model {
179 "haiku"
181 | "claude-haiku-4-5-20251001" | "claude-3-5-haiku-20241022" => 1.0,
184
185 "sonnet"
187 | "claude-sonnet-4-5-20250929" | "claude-sonnet-4-20250514" | "claude-3-5-sonnet-20241022" => 5.0,
191
192 "opus"
194 | "claude-opus-4-6" | "claude-opus-4-1-20250805" | "claude-opus-4-20250514" => 15.0,
198
199 _ => 5.0,
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_default_recommendations() {
210 let recommender = ModelRecommendation::default();
211
212 assert_eq!(recommender.suggest("simple"), Some("claude-haiku-4-5-20251001"));
213 assert_eq!(recommender.suggest("fast"), Some("claude-haiku-4-5-20251001"));
214 assert_eq!(recommender.suggest("balanced"), Some("claude-sonnet-4-5-20250929"));
215 assert_eq!(recommender.suggest("latest"), Some("claude-sonnet-4-5-20250929"));
216 assert_eq!(recommender.suggest("complex"), Some("claude-opus-4-6"));
217 assert_eq!(recommender.suggest("unknown"), None);
218 }
219
220 #[test]
221 fn test_custom_recommendations() {
222 let mut map = HashMap::new();
223 map.insert("code_review".to_string(), "sonnet".to_string());
224
225 let recommender = ModelRecommendation::custom(map);
226 assert_eq!(recommender.suggest("code_review"), Some("sonnet"));
227 }
228
229 #[test]
230 fn test_add_remove() {
231 let mut recommender = ModelRecommendation::default();
232
233 recommender.add("my_task", "sonnet");
234 assert_eq!(recommender.suggest("my_task"), Some("sonnet"));
235
236 recommender.remove("my_task");
237 assert_eq!(recommender.suggest("my_task"), None);
238 }
239
240 #[test]
241 fn test_cost_multipliers() {
242 assert_eq!(estimate_cost_multiplier("haiku"), 1.0);
244 assert_eq!(estimate_cost_multiplier("sonnet"), 5.0);
245 assert_eq!(estimate_cost_multiplier("opus"), 15.0);
246 assert_eq!(estimate_cost_multiplier("claude-haiku-4-5-20251001"), 1.0);
248 assert_eq!(estimate_cost_multiplier("claude-sonnet-4-5-20250929"), 5.0);
249 assert_eq!(estimate_cost_multiplier("claude-opus-4-6"), 15.0);
250 }
251
252 #[test]
253 fn test_quick_helpers() {
254 assert_eq!(cheapest_model(), "claude-haiku-4-5-20251001");
255 assert_eq!(balanced_model(), "claude-sonnet-4-5-20250929");
256 assert_eq!(latest_sonnet(), "claude-sonnet-4-5-20250929");
257 assert_eq!(best_model(), "claude-opus-4-6");
258 }
259}