1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum ProviderCategory {
9 Llm,
11 Mcp,
13 Local,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct Provider {
20 pub id: &'static str,
22 pub name: &'static str,
24 pub env_var: &'static str,
26 pub key_prefix: Option<&'static str>,
28 pub category: ProviderCategory,
30 pub requires_key: bool,
32 pub description: &'static str,
34}
35
36pub static KNOWN_PROVIDERS: &[Provider] = &[
41 Provider {
43 id: "anthropic",
44 name: "Anthropic Claude",
45 env_var: "ANTHROPIC_API_KEY",
46 key_prefix: Some("sk-ant-"),
47 category: ProviderCategory::Llm,
48 requires_key: true,
49 description: "Claude models (Opus, Sonnet, Haiku)",
50 },
51 Provider {
52 id: "openai",
53 name: "OpenAI GPT",
54 env_var: "OPENAI_API_KEY",
55 key_prefix: Some("sk-"),
56 category: ProviderCategory::Llm,
57 requires_key: true,
58 description: "GPT-4, GPT-3.5, and other OpenAI models",
59 },
60 Provider {
61 id: "mistral",
62 name: "Mistral AI",
63 env_var: "MISTRAL_API_KEY",
64 key_prefix: None,
65 category: ProviderCategory::Llm,
66 requires_key: true,
67 description: "Mistral and Mixtral models",
68 },
69 Provider {
70 id: "groq",
71 name: "Groq",
72 env_var: "GROQ_API_KEY",
73 key_prefix: Some("gsk_"),
74 category: ProviderCategory::Llm,
75 requires_key: true,
76 description: "Ultra-fast inference with Groq LPU",
77 },
78 Provider {
79 id: "deepseek",
80 name: "DeepSeek",
81 env_var: "DEEPSEEK_API_KEY",
82 key_prefix: Some("sk-"),
83 category: ProviderCategory::Llm,
84 requires_key: true,
85 description: "DeepSeek Coder and Chat models",
86 },
87 Provider {
88 id: "gemini",
89 name: "Google Gemini",
90 env_var: "GEMINI_API_KEY",
91 key_prefix: None,
92 category: ProviderCategory::Llm,
93 requires_key: true,
94 description: "Gemini Pro and Ultra models",
95 },
96 Provider {
97 id: "ollama",
98 name: "Ollama",
99 env_var: "OLLAMA_API_BASE_URL",
100 key_prefix: None,
101 category: ProviderCategory::Local,
102 requires_key: false,
103 description: "Local model runner (llama, mistral, etc.)",
104 },
105 Provider {
107 id: "neo4j",
108 name: "Neo4j Graph Database",
109 env_var: "NEO4J_PASSWORD",
110 key_prefix: None,
111 category: ProviderCategory::Mcp,
112 requires_key: true,
113 description: "Graph database for knowledge storage",
114 },
115 Provider {
116 id: "github",
117 name: "GitHub API",
118 env_var: "GITHUB_TOKEN",
119 key_prefix: Some("ghp_"),
120 category: ProviderCategory::Mcp,
121 requires_key: true,
122 description: "GitHub API access",
123 },
124 Provider {
125 id: "slack",
126 name: "Slack API",
127 env_var: "SLACK_BOT_TOKEN",
128 key_prefix: Some("xoxb-"),
129 category: ProviderCategory::Mcp,
130 requires_key: true,
131 description: "Slack workspace integration",
132 },
133 Provider {
134 id: "perplexity",
135 name: "Perplexity AI",
136 env_var: "PERPLEXITY_API_KEY",
137 key_prefix: Some("pplx-"),
138 category: ProviderCategory::Mcp,
139 requires_key: true,
140 description: "AI-powered web search",
141 },
142 Provider {
143 id: "firecrawl",
144 name: "Firecrawl",
145 env_var: "FIRECRAWL_API_KEY",
146 key_prefix: Some("fc-"),
147 category: ProviderCategory::Mcp,
148 requires_key: true,
149 description: "Web scraping and crawling",
150 },
151 Provider {
152 id: "supadata",
153 name: "Supadata API",
154 env_var: "SUPADATA_API_KEY",
155 key_prefix: None,
156 category: ProviderCategory::Mcp,
157 requires_key: true,
158 description: "Video transcription and web scraping",
159 },
160];
161
162#[must_use]
176pub fn find_provider(id: &str) -> Option<&'static Provider> {
177 KNOWN_PROVIDERS.iter().find(|p| p.id.eq_ignore_ascii_case(id))
178}
179
180pub fn provider_to_env_var(id: &str) -> Option<&'static str> {
191 find_provider(id).map(|p| p.env_var)
192}
193
194pub fn providers_by_category(
205 category: ProviderCategory,
206) -> impl Iterator<Item = &'static Provider> {
207 KNOWN_PROVIDERS.iter().filter(move |p| p.category == category)
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_find_provider() {
216 assert!(find_provider("anthropic").is_some());
217 assert!(find_provider("ANTHROPIC").is_some());
218 assert!(find_provider("unknown").is_none());
219 }
220
221 #[test]
222 fn test_provider_to_env_var() {
223 assert_eq!(provider_to_env_var("anthropic"), Some("ANTHROPIC_API_KEY"));
224 assert_eq!(provider_to_env_var("github"), Some("GITHUB_TOKEN"));
225 assert_eq!(provider_to_env_var("unknown"), None);
226 }
227
228 #[test]
229 fn test_providers_by_category() {
230 let llm: Vec<_> = providers_by_category(ProviderCategory::Llm).collect();
231 assert!(llm.len() >= 6);
232 assert!(llm.iter().all(|p| p.category == ProviderCategory::Llm));
233
234 let mcp: Vec<_> = providers_by_category(ProviderCategory::Mcp).collect();
235 assert!(mcp.len() >= 5);
236 assert!(mcp.iter().all(|p| p.category == ProviderCategory::Mcp));
237 }
238
239 #[test]
240 fn test_all_providers_have_env_var() {
241 for provider in KNOWN_PROVIDERS {
242 assert!(!provider.env_var.is_empty(), "Provider {} missing env_var", provider.id);
243 }
244 }
245
246 #[test]
247 fn test_provider_count() {
248 assert!(KNOWN_PROVIDERS.len() >= 13);
250 }
251}