1use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::str::FromStr;
11
12#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
14pub enum ProviderParseError {
15 #[error("Invalid provider: '{}'. Supported providers: {}", .0, crate::provider::Provider::all_providers().iter().map(|p| p.to_string()).collect::<Vec<_>>().join(", "))]
16 InvalidProvider(String),
17}
18
19#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
22pub enum Provider {
23 Gemini,
25 #[default]
27 OpenAI,
28 Anthropic,
30 Copilot,
32 DeepSeek,
34 Meta,
36 OpenRouter,
38 Ollama,
40 OllamaCloud,
42 LmStudio,
44 LlamaCpp,
46 Moonshot,
48 ZAI,
50 Minimax,
52 MiMo,
54 Mistral,
56 HuggingFace,
58 OpenCodeZen,
60 OpenCodeGo,
62 Qwen,
64 StepFun,
66 Evolink,
68 Poolside,
70 XAI,
72 NVIDIA,
74 MergeGateway,
76 Vercel,
78}
79
80impl Provider {
81 pub fn default_api_key_env(&self) -> &'static str {
83 match self {
84 Provider::Gemini => "GEMINI_API_KEY",
85 Provider::OpenAI => "OPENAI_API_KEY",
86 Provider::Anthropic => "ANTHROPIC_API_KEY",
87 Provider::Copilot => "",
88 Provider::DeepSeek => "DEEPSEEK_API_KEY",
89 Provider::Meta => "META_API_KEY",
90 Provider::OpenRouter => "OPENROUTER_API_KEY",
91 Provider::Ollama => "OLLAMA_API_KEY",
92 Provider::OllamaCloud => "OLLAMA_API_KEY",
93 Provider::LmStudio => "LMSTUDIO_API_KEY",
94 Provider::LlamaCpp => "LLAMACPP_API_KEY",
95 Provider::Moonshot => "MOONSHOT_API_KEY",
96 Provider::ZAI => "ZAI_API_KEY",
97 Provider::Minimax => "MINIMAX_API_KEY",
98 Provider::MiMo => "MIMO_API_KEY",
99 Provider::Mistral => "MISTRAL_API_KEY",
100 Provider::HuggingFace => "HF_TOKEN",
101 Provider::OpenCodeZen => "OPENCODE_ZEN_API_KEY",
102 Provider::OpenCodeGo => "OPENCODE_GO_API_KEY",
103 Provider::Qwen => "QWEN_API_KEY",
104 Provider::StepFun => "STEPFUN_API_KEY",
105 Provider::Evolink => "EVOLINK_API_KEY",
106 Provider::Poolside => "POOLSIDE_API_KEY",
107 Provider::XAI => "XAI_API_KEY",
108 Provider::NVIDIA => "NVIDIA_API_KEY",
109 Provider::MergeGateway => "MERGE_GATEWAY_API_KEY",
110 Provider::Vercel => "AI_GATEWAY_API_KEY",
111 }
112 }
113
114 pub fn all_providers() -> Vec<Provider> {
116 vec![
117 Provider::OpenAI,
118 Provider::Anthropic,
119 Provider::Copilot,
120 Provider::Minimax,
121 Provider::MiMo,
122 Provider::Mistral,
123 Provider::Gemini,
124 Provider::DeepSeek,
125 Provider::Meta,
126 Provider::HuggingFace,
127 Provider::OpenRouter,
128 Provider::Ollama,
129 Provider::OllamaCloud,
130 Provider::LmStudio,
131 Provider::LlamaCpp,
132 Provider::Moonshot,
133 Provider::ZAI,
134 Provider::OpenCodeZen,
135 Provider::OpenCodeGo,
136 Provider::Qwen,
137 Provider::StepFun,
138 Provider::Evolink,
139 Provider::Poolside,
140 Provider::XAI,
141 Provider::NVIDIA,
142 Provider::MergeGateway,
143 Provider::Vercel,
144 ]
145 }
146
147 pub fn label(&self) -> &'static str {
149 match self {
150 Provider::Gemini => "Gemini",
151 Provider::OpenAI => "OpenAI",
152 Provider::Anthropic => "Anthropic",
153 Provider::Copilot => "GitHub Copilot",
154 Provider::DeepSeek => "DeepSeek",
155 Provider::Meta => "Meta AI",
156 Provider::OpenRouter => "OpenRouter",
157 Provider::Ollama => "Ollama",
158 Provider::OllamaCloud => "Ollama Cloud",
159 Provider::LmStudio => "LM Studio",
160 Provider::LlamaCpp => "llama.cpp",
161 Provider::Moonshot => "Moonshot",
162 Provider::ZAI => "Z.AI",
163 Provider::Minimax => "MiniMax",
164 Provider::MiMo => "Xiaomi MiMo",
165 Provider::Mistral => "Mistral",
166 Provider::HuggingFace => "Hugging Face",
167 Provider::OpenCodeZen => "OpenCode Zen",
168 Provider::OpenCodeGo => "OpenCode Go",
169 Provider::Qwen => "Qwen",
170 Provider::StepFun => "StepFun",
171 Provider::Evolink => "Evolink",
172 Provider::Poolside => "Poolside",
173 Provider::XAI => "xAI",
174 Provider::NVIDIA => "NVIDIA",
175 Provider::MergeGateway => "Merge Gateway",
176 Provider::Vercel => "Vercel AI Gateway",
177 }
178 }
179
180 pub fn is_dynamic(&self) -> bool {
181 matches!(self, Provider::Copilot) || self.is_local()
182 }
183
184 pub fn is_local(&self) -> bool {
185 matches!(self, Provider::Ollama | Provider::LmStudio | Provider::LlamaCpp)
186 }
187
188 pub fn local_install_instructions(&self) -> Option<&'static str> {
189 match self {
190 Provider::Ollama => Some(
191 "Ollama server is not running. To start:\n 1. Install Ollama from https://ollama.com\n 2. Run 'ollama serve' in a terminal\n 3. Pull models using 'ollama pull <model-name>' (e.g., 'ollama pull gpt-oss:20b')",
192 ),
193 Provider::LmStudio => Some(
194 "LM Studio server is not running. To start:\n 1. Install LM Studio from https://lmstudio.ai\n 2. Open LM Studio and start the Local Server on port 1234\n 3. Load the model you want to use",
195 ),
196 Provider::LlamaCpp => Some(
197 "llama.cpp server is not running. To start:\n 1. Install llama.cpp from https://llama.app or your package manager\n 2. Run 'llama-server -m /path/to/model.gguf --port 8080'\n 3. Keep the server running while VT Code connects",
198 ),
199 _ => None,
200 }
201 }
202
203 pub fn uses_managed_auth(&self) -> bool {
204 matches!(self, Provider::Copilot)
205 }
206
207 pub fn platform_url(&self) -> Option<&'static str> {
208 match self {
209 Provider::Gemini => Some("https://aistudio.google.com/app/apikey"),
210 Provider::OpenAI => Some("https://platform.openai.com/api-keys"),
211 Provider::Anthropic => Some("https://console.anthropic.com"),
212 Provider::Copilot => None,
213 Provider::DeepSeek => Some("https://platform.deepseek.com"),
214 Provider::Meta => Some("https://developer.meta.com/ai/models"),
215 Provider::OpenRouter => Some("https://openrouter.ai/api/v1/auth/keys"),
216 Provider::Ollama => None,
217 Provider::OllamaCloud => Some("https://ollama.com"),
218 Provider::LmStudio => None,
219 Provider::LlamaCpp => None,
220 Provider::Moonshot => Some("https://platform.kimi.ai/console/api-keys"),
221 Provider::ZAI => Some("https://z.ai/docs"),
222 Provider::Minimax => Some("https://platform.minimax.io/docs"),
223 Provider::MiMo => Some("https://platform.xiaomimimo.com/docs/en-US/welcome"),
224 Provider::Mistral => Some("https://console.mistral.ai"),
225 Provider::HuggingFace => Some("https://huggingface.co/settings/tokens"),
226 Provider::OpenCodeZen => Some("https://opencode.ai/docs/zen/"),
227 Provider::OpenCodeGo => Some("https://opencode.ai/docs/go/"),
228 Provider::Qwen => Some("https://dashscope.console.aliyun.com"),
229 Provider::StepFun => Some("https://platform.stepfun.com"),
230 Provider::Evolink => Some("https://evolink.ai/dashboard/keys"),
231 Provider::Poolside => Some("https://poolside.ai"),
232 Provider::XAI => Some("https://docs.x.ai"),
233 Provider::NVIDIA => Some("https://build.nvidia.com"),
234 Provider::MergeGateway => Some("https://dashboard.merge.dev"),
235 Provider::Vercel => Some("https://vercel.com/docs/ai-gateway/getting-started"),
236 }
237 }
238}
239
240impl fmt::Display for Provider {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 match self {
243 Provider::Gemini => write!(f, "gemini"),
244 Provider::OpenAI => write!(f, "openai"),
245 Provider::Anthropic => write!(f, "anthropic"),
246 Provider::Copilot => write!(f, "copilot"),
247 Provider::DeepSeek => write!(f, "deepseek"),
248 Provider::Meta => write!(f, "meta"),
249 Provider::OpenRouter => write!(f, "openrouter"),
250 Provider::Ollama => write!(f, "ollama"),
251 Provider::OllamaCloud => write!(f, "ollama-cloud"),
252 Provider::LmStudio => write!(f, "lmstudio"),
253 Provider::LlamaCpp => write!(f, "llamacpp"),
254 Provider::Moonshot => write!(f, "moonshot"),
255 Provider::ZAI => write!(f, "zai"),
256 Provider::Minimax => write!(f, "minimax"),
257 Provider::MiMo => write!(f, "mimo"),
258 Provider::Mistral => write!(f, "mistral"),
259 Provider::HuggingFace => write!(f, "huggingface"),
260 Provider::OpenCodeZen => write!(f, "opencode-zen"),
261 Provider::OpenCodeGo => write!(f, "opencode-go"),
262 Provider::Qwen => write!(f, "qwen"),
263 Provider::StepFun => write!(f, "stepfun"),
264 Provider::Evolink => write!(f, "evolink"),
265 Provider::Poolside => write!(f, "poolside"),
266 Provider::XAI => write!(f, "xai"),
267 Provider::NVIDIA => write!(f, "nvidia"),
268 Provider::MergeGateway => write!(f, "merge-gateway"),
269 Provider::Vercel => write!(f, "vercel"),
270 }
271 }
272}
273
274impl AsRef<str> for Provider {
275 fn as_ref(&self) -> &str {
276 match self {
277 Provider::Gemini => "gemini",
278 Provider::OpenAI => "openai",
279 Provider::Anthropic => "anthropic",
280 Provider::Copilot => "copilot",
281 Provider::DeepSeek => "deepseek",
282 Provider::Meta => "meta",
283 Provider::OpenRouter => "openrouter",
284 Provider::Ollama => "ollama",
285 Provider::OllamaCloud => "ollama-cloud",
286 Provider::LmStudio => "lmstudio",
287 Provider::LlamaCpp => "llamacpp",
288 Provider::Moonshot => "moonshot",
289 Provider::ZAI => "zai",
290 Provider::Minimax => "minimax",
291 Provider::MiMo => "mimo",
292 Provider::Mistral => "mistral",
293 Provider::HuggingFace => "huggingface",
294 Provider::OpenCodeZen => "opencode-zen",
295 Provider::OpenCodeGo => "opencode-go",
296 Provider::Qwen => "qwen",
297 Provider::StepFun => "stepfun",
298 Provider::Evolink => "evolink",
299 Provider::Poolside => "poolside",
300 Provider::XAI => "xai",
301 Provider::NVIDIA => "nvidia",
302 Provider::MergeGateway => "merge-gateway",
303 Provider::Vercel => "vercel",
304 }
305 }
306}
307
308impl FromStr for Provider {
309 type Err = ProviderParseError;
310
311 fn from_str(s: &str) -> Result<Self, Self::Err> {
312 match s.to_lowercase().as_str() {
313 "gemini" | "google" => Ok(Provider::Gemini),
314 "openai" => Ok(Provider::OpenAI),
315 "anthropic" => Ok(Provider::Anthropic),
316 "copilot" => Ok(Provider::Copilot),
317 "deepseek" => Ok(Provider::DeepSeek),
318 "meta" | "meta-ai" => Ok(Provider::Meta),
319 "openrouter" => Ok(Provider::OpenRouter),
320 "ollama" => Ok(Provider::Ollama),
321 "ollama-cloud" | "ollamacloud" | "ollama_cloud" => Ok(Provider::OllamaCloud),
322 "lmstudio" => Ok(Provider::LmStudio),
323 "llamacpp" | "llama.cpp" | "llama-cpp" => Ok(Provider::LlamaCpp),
324 "moonshot" => Ok(Provider::Moonshot),
325 "zai" => Ok(Provider::ZAI),
326 "minimax" => Ok(Provider::Minimax),
327 "mimo" => Ok(Provider::MiMo),
328 "mistral" => Ok(Provider::Mistral),
329 "huggingface" => Ok(Provider::HuggingFace),
330 "opencode-zen" | "opencodezen" => Ok(Provider::OpenCodeZen),
331 "opencode-go" | "opencodego" => Ok(Provider::OpenCodeGo),
332 "qwen" => Ok(Provider::Qwen),
333 "stepfun" => Ok(Provider::StepFun),
334 "evolink" => Ok(Provider::Evolink),
335 "poolside" => Ok(Provider::Poolside),
336 "xai" => Ok(Provider::XAI),
337 "nvidia" | "nvidia-nim" => Ok(Provider::NVIDIA),
338 "merge-gateway" | "merge_gateway" | "mergegateway" => Ok(Provider::MergeGateway),
339 "vercel" | "vercel-ai-gateway" | "ai-gateway" => Ok(Provider::Vercel),
340 _ => Err(ProviderParseError::InvalidProvider(s.to_string())),
341 }
342 }
343}