1use crate::config::ProviderType;
14
15#[derive(Debug, Clone, Copy)]
17pub struct ModelAlias {
18 pub alias: &'static str,
20 pub model_id: &'static str,
22 pub provider: ProviderType,
24}
25
26pub const LOCAL_ALIAS: &str = "local";
28
29static ALIASES: &[ModelAlias] = &[
31 ModelAlias {
33 alias: "gemini-flash-lite",
34 model_id: "gemini-flash-lite-latest",
35 provider: ProviderType::Gemini,
36 },
37 ModelAlias {
38 alias: "gemini-flash",
39 model_id: "gemini-flash-latest",
40 provider: ProviderType::Gemini,
41 },
42 ModelAlias {
43 alias: "gemini-pro",
44 model_id: "gemini-pro-latest",
45 provider: ProviderType::Gemini,
46 },
47 ModelAlias {
49 alias: "claude-haiku",
50 model_id: "claude-haiku-4-5-20251001",
51 provider: ProviderType::Anthropic,
52 },
53 ModelAlias {
54 alias: "claude-sonnet",
55 model_id: "claude-sonnet-4-6",
56 provider: ProviderType::Anthropic,
57 },
58 ModelAlias {
59 alias: "claude-opus",
60 model_id: "claude-opus-4-6",
61 provider: ProviderType::Anthropic,
62 },
63];
64
65pub fn resolve(name: &str) -> Option<ResolvedAlias> {
80 if name == LOCAL_ALIAS {
81 return Some(ResolvedAlias {
82 alias: LOCAL_ALIAS,
83 model_id: "auto-detect",
84 provider: ProviderType::LMStudio,
85 });
86 }
87 ALIASES
88 .iter()
89 .find(|a| a.alias == name)
90 .map(|a| ResolvedAlias {
91 alias: a.alias,
92 model_id: a.model_id,
93 provider: a.provider,
94 })
95}
96
97#[derive(Debug, Clone)]
99pub struct ResolvedAlias {
100 pub alias: &'static str,
102 pub model_id: &'static str,
104 pub provider: ProviderType,
106}
107
108impl ResolvedAlias {
109 pub fn needs_auto_detect(&self) -> bool {
111 self.model_id == "auto-detect"
112 }
113}
114
115pub fn all() -> &'static [ModelAlias] {
117 ALIASES
118}
119
120pub fn alias_names() -> Vec<&'static str> {
122 let mut names: Vec<&str> = ALIASES.iter().map(|a| a.alias).collect();
123 names.push(LOCAL_ALIAS);
124 names
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn resolve_known_alias() {
133 let r = resolve("claude-sonnet").unwrap();
134 assert_eq!(r.model_id, "claude-sonnet-4-6");
135 assert_eq!(r.provider, ProviderType::Anthropic);
136 assert!(!r.needs_auto_detect());
137 }
138
139 #[test]
140 fn resolve_local_alias() {
141 let r = resolve("local").unwrap();
142 assert!(r.needs_auto_detect());
143 assert_eq!(r.provider, ProviderType::LMStudio);
144 }
145
146 #[test]
147 fn resolve_unknown_returns_none() {
148 assert!(resolve("not-a-real-model").is_none());
149 }
150
151 #[test]
152 fn resolve_literal_model_id_returns_none() {
153 assert!(resolve("claude-sonnet-4-6").is_none());
155 assert!(resolve("gemini-2.5-pro").is_none());
156 }
157
158 #[test]
159 fn all_aliases_non_empty() {
160 assert!(!all().is_empty());
161 }
162
163 #[test]
164 fn alias_names_includes_local() {
165 let names = alias_names();
166 assert!(names.contains(&"local"));
167 assert!(names.contains(&"gemini-flash-lite"));
168 }
169
170 #[test]
171 fn no_duplicate_aliases() {
172 let names = alias_names();
173 let mut seen = std::collections::HashSet::new();
174 for name in &names {
175 assert!(seen.insert(name), "duplicate alias: {name}");
176 }
177 }
178
179 #[test]
180 fn no_duplicate_model_ids() {
181 let mut seen = std::collections::HashSet::new();
182 for a in all() {
183 assert!(
184 seen.insert(a.model_id),
185 "duplicate model_id: {}",
186 a.model_id
187 );
188 }
189 }
190}