1use super::consts::{
6 PROVIDER_ANTHROPIC, PROVIDER_CEREBRAS, PROVIDER_GEMINI, PROVIDER_GROQ, PROVIDER_OPENROUTER,
7 PROVIDER_ZAI, PROVIDER_ZENMUX,
8};
9
10#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct ProviderConfig {
13 pub name: &'static str,
15
16 pub display_name: &'static str,
18
19 pub api_url: &'static str,
21
22 pub api_key_env: &'static str,
24
25 pub model: &'static str,
27
28 pub max_tokens: u32,
30
31 pub temperature: f32,
33}
34
35pub static PROVIDERS: &[ProviderConfig] = &[
37 ProviderConfig {
38 name: PROVIDER_GEMINI,
39 display_name: "Google Gemini",
40 api_url: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
41 api_key_env: "GEMINI_API_KEY",
42 model: "gemini-3.5-flash-lite",
43 max_tokens: 4096,
44 temperature: 0.3,
45 },
46 ProviderConfig {
47 name: PROVIDER_OPENROUTER,
48 display_name: "OpenRouter",
49 api_url: "https://openrouter.ai/api/v1/chat/completions",
50 api_key_env: "OPENROUTER_API_KEY",
51 model: "mistralai/mistral-small-2603",
52 max_tokens: 4096,
53 temperature: 0.3,
54 },
55 ProviderConfig {
56 name: PROVIDER_GROQ,
57 display_name: "Groq",
58 api_url: "https://api.groq.com/openai/v1/chat/completions",
59 api_key_env: "GROQ_API_KEY",
60 model: "openai/gpt-oss-20b",
61 max_tokens: 4096,
62 temperature: 0.3,
63 },
64 ProviderConfig {
65 name: PROVIDER_CEREBRAS,
66 display_name: "Cerebras",
67 api_url: "https://api.cerebras.ai/v1/chat/completions",
68 api_key_env: "CEREBRAS_API_KEY",
69 model: "gemma-4-31b",
70 max_tokens: 4096,
71 temperature: 0.3,
72 },
73 ProviderConfig {
74 name: PROVIDER_ZENMUX,
75 display_name: "Zenmux",
76 api_url: "https://zenmux.ai/api/v1/chat/completions",
77 api_key_env: "ZENMUX_API_KEY",
78 model: "openai/gpt-5.4-mini",
79 max_tokens: 4096,
80 temperature: 0.3,
81 },
82 ProviderConfig {
83 name: PROVIDER_ZAI,
84 display_name: "Z.AI (Zhipu)",
85 api_url: "https://api.z.ai/api/paas/v4/chat/completions",
86 api_key_env: "ZAI_API_KEY",
87 model: "glm-5.3",
88 max_tokens: 4096,
89 temperature: 0.3,
90 },
91 ProviderConfig {
92 name: PROVIDER_ANTHROPIC,
93 display_name: "Anthropic",
94 api_url: "https://api.anthropic.com/v1/chat/completions",
95 api_key_env: "ANTHROPIC_API_KEY",
96 model: "claude-sonnet-5",
97 max_tokens: 4096,
98 temperature: 0.3,
99 },
100];
101
102#[must_use]
122pub fn get_provider(name: &str) -> Option<&'static ProviderConfig> {
123 PROVIDERS.iter().find(|p| p.name == name)
124}
125
126#[must_use]
141pub fn all_providers() -> &'static [ProviderConfig] {
142 PROVIDERS
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_get_provider_gemini() {
151 let provider = get_provider("gemini");
152 assert!(provider.is_some());
153 let provider = provider.unwrap();
154 assert_eq!(provider.display_name, "Google Gemini");
155 assert_eq!(provider.api_key_env, "GEMINI_API_KEY");
156 }
157
158 #[test]
159 fn test_get_provider_openrouter() {
160 let provider = get_provider("openrouter");
161 assert!(provider.is_some());
162 let provider = provider.unwrap();
163 assert_eq!(provider.display_name, "OpenRouter");
164 assert_eq!(provider.api_key_env, "OPENROUTER_API_KEY");
165 }
166
167 #[test]
168 fn test_get_provider_groq() {
169 let provider = get_provider("groq");
170 assert!(provider.is_some());
171 let provider = provider.unwrap();
172 assert_eq!(provider.display_name, "Groq");
173 assert_eq!(provider.api_key_env, "GROQ_API_KEY");
174 }
175
176 #[test]
177 fn test_get_provider_cerebras() {
178 let provider = get_provider("cerebras");
179 assert!(provider.is_some());
180 let provider = provider.unwrap();
181 assert_eq!(provider.display_name, "Cerebras");
182 assert_eq!(provider.api_key_env, "CEREBRAS_API_KEY");
183 }
184
185 #[test]
186 fn test_get_provider_not_found() {
187 let provider = get_provider("nonexistent");
188 assert!(provider.is_none());
189 }
190
191 #[test]
192 fn test_get_provider_case_sensitive() {
193 let provider = get_provider("OpenRouter");
194 assert!(
195 provider.is_none(),
196 "Provider lookup should be case-sensitive"
197 );
198 }
199
200 #[test]
201 fn test_all_providers_count() {
202 let providers = all_providers();
203 assert_eq!(providers.len(), 7, "Should have exactly 7 providers");
204 }
205
206 #[test]
207 fn test_all_providers_have_unique_names() {
208 let providers = all_providers();
209 let mut names = Vec::new();
210 for provider in providers {
211 assert!(
212 !names.contains(&provider.name),
213 "Duplicate provider name: {}",
214 provider.name
215 );
216 names.push(provider.name);
217 }
218 }
219
220 #[test]
221 fn test_get_provider_zenmux() {
222 let provider = get_provider("zenmux");
223 assert!(provider.is_some());
224 let provider = provider.unwrap();
225 assert_eq!(provider.display_name, "Zenmux");
226 assert_eq!(provider.api_key_env, "ZENMUX_API_KEY");
227 }
228
229 #[test]
230 fn test_get_provider_zai() {
231 let provider = get_provider("zai");
232 assert!(provider.is_some());
233 let provider = provider.unwrap();
234 assert_eq!(provider.display_name, "Z.AI (Zhipu)");
235 assert_eq!(provider.api_key_env, "ZAI_API_KEY");
236 }
237
238 #[test]
239 fn test_provider_api_urls_valid() {
240 let providers = all_providers();
241 for provider in providers {
242 assert!(
243 provider.api_url.starts_with("https://"),
244 "Provider {} API URL should use HTTPS",
245 provider.name
246 );
247 }
248 }
249
250 #[test]
251 fn test_provider_api_key_env_not_empty() {
252 let providers = all_providers();
253 for provider in providers {
254 assert!(
255 !provider.api_key_env.is_empty(),
256 "Provider {} should have API key env var",
257 provider.name
258 );
259 }
260 }
261}