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 OpenRouter,
36 Ollama,
38 OllamaCloud,
40 LmStudio,
42 LlamaCpp,
44 Moonshot,
46 ZAI,
48 Minimax,
50 MiMo,
52 Mistral,
54 HuggingFace,
56 OpenCodeZen,
58 OpenCodeGo,
60 Qwen,
62 StepFun,
64 Evolink,
66 Poolside,
68 XAI,
70}
71
72impl Provider {
73 pub fn default_api_key_env(&self) -> &'static str {
75 match self {
76 Provider::Gemini => "GEMINI_API_KEY",
77 Provider::OpenAI => "OPENAI_API_KEY",
78 Provider::Anthropic => "ANTHROPIC_API_KEY",
79 Provider::Copilot => "",
80 Provider::DeepSeek => "DEEPSEEK_API_KEY",
81 Provider::OpenRouter => "OPENROUTER_API_KEY",
82 Provider::Ollama => "OLLAMA_API_KEY",
83 Provider::OllamaCloud => "OLLAMA_API_KEY",
84 Provider::LmStudio => "LMSTUDIO_API_KEY",
85 Provider::LlamaCpp => "LLAMACPP_API_KEY",
86 Provider::Moonshot => "MOONSHOT_API_KEY",
87 Provider::ZAI => "ZAI_API_KEY",
88 Provider::Minimax => "MINIMAX_API_KEY",
89 Provider::MiMo => "MIMO_API_KEY",
90 Provider::Mistral => "MISTRAL_API_KEY",
91 Provider::HuggingFace => "HF_TOKEN",
92 Provider::OpenCodeZen => "OPENCODE_ZEN_API_KEY",
93 Provider::OpenCodeGo => "OPENCODE_GO_API_KEY",
94 Provider::Qwen => "QWEN_API_KEY",
95 Provider::StepFun => "STEPFUN_API_KEY",
96 Provider::Evolink => "EVOLINK_API_KEY",
97 Provider::Poolside => "POOLSIDE_API_KEY",
98 Provider::XAI => "XAI_API_KEY",
99 }
100 }
101
102 pub fn all_providers() -> Vec<Provider> {
104 vec![
105 Provider::OpenAI,
106 Provider::Anthropic,
107 Provider::Copilot,
108 Provider::Minimax,
109 Provider::MiMo,
110 Provider::Mistral,
111 Provider::Gemini,
112 Provider::DeepSeek,
113 Provider::HuggingFace,
114 Provider::OpenRouter,
115 Provider::Ollama,
116 Provider::OllamaCloud,
117 Provider::LmStudio,
118 Provider::LlamaCpp,
119 Provider::Moonshot,
120 Provider::ZAI,
121 Provider::OpenCodeZen,
122 Provider::OpenCodeGo,
123 Provider::Qwen,
124 Provider::StepFun,
125 Provider::Evolink,
126 Provider::Poolside,
127 Provider::XAI,
128 ]
129 }
130
131 pub fn label(&self) -> &'static str {
133 match self {
134 Provider::Gemini => "Gemini",
135 Provider::OpenAI => "OpenAI",
136 Provider::Anthropic => "Anthropic",
137 Provider::Copilot => "GitHub Copilot",
138 Provider::DeepSeek => "DeepSeek",
139 Provider::OpenRouter => "OpenRouter",
140 Provider::Ollama => "Ollama",
141 Provider::OllamaCloud => "Ollama Cloud",
142 Provider::LmStudio => "LM Studio",
143 Provider::LlamaCpp => "llama.cpp",
144 Provider::Moonshot => "Moonshot",
145 Provider::ZAI => "Z.AI",
146 Provider::Minimax => "MiniMax",
147 Provider::MiMo => "Xiaomi MiMo",
148 Provider::Mistral => "Mistral",
149 Provider::HuggingFace => "Hugging Face",
150 Provider::OpenCodeZen => "OpenCode Zen",
151 Provider::OpenCodeGo => "OpenCode Go",
152 Provider::Qwen => "Qwen",
153 Provider::StepFun => "StepFun",
154 Provider::Evolink => "Evolink",
155 Provider::Poolside => "Poolside",
156 Provider::XAI => "xAI",
157 }
158 }
159
160 pub fn is_dynamic(&self) -> bool {
161 matches!(self, Provider::Copilot) || self.is_local()
162 }
163
164 pub fn is_local(&self) -> bool {
165 matches!(self, Provider::Ollama | Provider::LmStudio | Provider::LlamaCpp)
166 }
167
168 pub fn local_install_instructions(&self) -> Option<&'static str> {
169 match self {
170 Provider::Ollama => Some(
171 "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')",
172 ),
173 Provider::LmStudio => Some(
174 "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",
175 ),
176 Provider::LlamaCpp => Some(
177 "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",
178 ),
179 _ => None,
180 }
181 }
182
183 pub fn uses_managed_auth(&self) -> bool {
184 matches!(self, Provider::Copilot)
185 }
186
187 pub fn platform_url(&self) -> Option<&'static str> {
188 match self {
189 Provider::Gemini => Some("https://aistudio.google.com/app/apikey"),
190 Provider::OpenAI => Some("https://platform.openai.com/api-keys"),
191 Provider::Anthropic => Some("https://console.anthropic.com"),
192 Provider::Copilot => None,
193 Provider::DeepSeek => Some("https://platform.deepseek.com"),
194 Provider::OpenRouter => Some("https://openrouter.ai/api/v1/auth/keys"),
195 Provider::Ollama => None,
196 Provider::OllamaCloud => Some("https://ollama.com"),
197 Provider::LmStudio => None,
198 Provider::LlamaCpp => None,
199 Provider::Moonshot => Some("https://platform.kimi.ai/console/api-keys"),
200 Provider::ZAI => Some("https://z.ai/docs"),
201 Provider::Minimax => Some("https://platform.minimax.io/docs"),
202 Provider::MiMo => Some("https://platform.xiaomimimo.com/docs/en-US/welcome"),
203 Provider::Mistral => Some("https://console.mistral.ai"),
204 Provider::HuggingFace => Some("https://huggingface.co/settings/tokens"),
205 Provider::OpenCodeZen => Some("https://opencode.ai/docs/zen/"),
206 Provider::OpenCodeGo => Some("https://opencode.ai/docs/go/"),
207 Provider::Qwen => Some("https://dashscope.console.aliyun.com"),
208 Provider::StepFun => Some("https://platform.stepfun.com"),
209 Provider::Evolink => Some("https://evolink.ai/dashboard/keys"),
210 Provider::Poolside => Some("https://poolside.ai"),
211 Provider::XAI => Some("https://docs.x.ai"),
212 }
213 }
214}
215
216impl fmt::Display for Provider {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 match self {
219 Provider::Gemini => write!(f, "gemini"),
220 Provider::OpenAI => write!(f, "openai"),
221 Provider::Anthropic => write!(f, "anthropic"),
222 Provider::Copilot => write!(f, "copilot"),
223 Provider::DeepSeek => write!(f, "deepseek"),
224 Provider::OpenRouter => write!(f, "openrouter"),
225 Provider::Ollama => write!(f, "ollama"),
226 Provider::OllamaCloud => write!(f, "ollama-cloud"),
227 Provider::LmStudio => write!(f, "lmstudio"),
228 Provider::LlamaCpp => write!(f, "llamacpp"),
229 Provider::Moonshot => write!(f, "moonshot"),
230 Provider::ZAI => write!(f, "zai"),
231 Provider::Minimax => write!(f, "minimax"),
232 Provider::MiMo => write!(f, "mimo"),
233 Provider::Mistral => write!(f, "mistral"),
234 Provider::HuggingFace => write!(f, "huggingface"),
235 Provider::OpenCodeZen => write!(f, "opencode-zen"),
236 Provider::OpenCodeGo => write!(f, "opencode-go"),
237 Provider::Qwen => write!(f, "qwen"),
238 Provider::StepFun => write!(f, "stepfun"),
239 Provider::Evolink => write!(f, "evolink"),
240 Provider::Poolside => write!(f, "poolside"),
241 Provider::XAI => write!(f, "xai"),
242 }
243 }
244}
245
246impl AsRef<str> for Provider {
247 fn as_ref(&self) -> &str {
248 match self {
249 Provider::Gemini => "gemini",
250 Provider::OpenAI => "openai",
251 Provider::Anthropic => "anthropic",
252 Provider::Copilot => "copilot",
253 Provider::DeepSeek => "deepseek",
254 Provider::OpenRouter => "openrouter",
255 Provider::Ollama => "ollama",
256 Provider::OllamaCloud => "ollama-cloud",
257 Provider::LmStudio => "lmstudio",
258 Provider::LlamaCpp => "llamacpp",
259 Provider::Moonshot => "moonshot",
260 Provider::ZAI => "zai",
261 Provider::Minimax => "minimax",
262 Provider::MiMo => "mimo",
263 Provider::Mistral => "mistral",
264 Provider::HuggingFace => "huggingface",
265 Provider::OpenCodeZen => "opencode-zen",
266 Provider::OpenCodeGo => "opencode-go",
267 Provider::Qwen => "qwen",
268 Provider::StepFun => "stepfun",
269 Provider::Evolink => "evolink",
270 Provider::Poolside => "poolside",
271 Provider::XAI => "xai",
272 }
273 }
274}
275
276impl FromStr for Provider {
277 type Err = ProviderParseError;
278
279 fn from_str(s: &str) -> Result<Self, Self::Err> {
280 match s.to_lowercase().as_str() {
281 "gemini" => Ok(Provider::Gemini),
282 "openai" => Ok(Provider::OpenAI),
283 "anthropic" => Ok(Provider::Anthropic),
284 "copilot" => Ok(Provider::Copilot),
285 "deepseek" => Ok(Provider::DeepSeek),
286 "openrouter" => Ok(Provider::OpenRouter),
287 "ollama" => Ok(Provider::Ollama),
288 "ollama-cloud" | "ollamacloud" | "ollama_cloud" => Ok(Provider::OllamaCloud),
289 "lmstudio" => Ok(Provider::LmStudio),
290 "llamacpp" | "llama.cpp" | "llama-cpp" => Ok(Provider::LlamaCpp),
291 "moonshot" => Ok(Provider::Moonshot),
292 "zai" => Ok(Provider::ZAI),
293 "minimax" => Ok(Provider::Minimax),
294 "mimo" => Ok(Provider::MiMo),
295 "mistral" => Ok(Provider::Mistral),
296 "huggingface" => Ok(Provider::HuggingFace),
297 "opencode-zen" | "opencodezen" => Ok(Provider::OpenCodeZen),
298 "opencode-go" | "opencodego" => Ok(Provider::OpenCodeGo),
299 "qwen" => Ok(Provider::Qwen),
300 "stepfun" => Ok(Provider::StepFun),
301 "evolink" => Ok(Provider::Evolink),
302 "poolside" => Ok(Provider::Poolside),
303 "xai" => Ok(Provider::XAI),
304 _ => Err(ProviderParseError::InvalidProvider(s.to_string())),
305 }
306 }
307}