1use serde::{Deserialize, Serialize};
8
9use crate::provider;
10
11#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
12#[serde(rename_all = "kebab-case")]
13pub enum ProviderKind {
14 #[default]
15 #[serde(
16 alias = "deepseek-cn",
17 alias = "deepseek_china",
18 alias = "deepseekcn",
19 alias = "deepseek-china"
20 )]
21 Deepseek,
22 #[serde(
23 alias = "deepseek-anthropic",
24 alias = "deepseek_anthropic",
25 alias = "deepseek-claude",
26 alias = "deepseek_claude"
27 )]
28 DeepseekAnthropic,
29 NvidiaNim,
30 #[serde(alias = "open-ai")]
31 Openai,
32 Atlascloud,
33 #[serde(
34 alias = "wanjie",
35 alias = "wanjie_ark",
36 alias = "ark-wanjie",
37 alias = "ark_wanjie",
38 alias = "wanjie-maas",
39 alias = "wanjie_maas"
40 )]
41 WanjieArk,
42 #[serde(alias = "volcengine-ark", alias = "volcengine_ark", alias = "ark")]
43 Volcengine,
44 Openrouter,
45 #[serde(alias = "orca_router", alias = "orca")]
46 Orcarouter,
47 #[serde(alias = "mimo", alias = "xiaomi", alias = "xiaomi_mimo")]
48 XiaomiMimo,
49 #[serde(alias = "novita-ai", alias = "novita_ai")]
50 Novita,
51 #[serde(alias = "fireworks-ai", alias = "fireworks_ai")]
52 Fireworks,
53 #[serde(alias = "silicon-flow", alias = "silicon_flow")]
54 Siliconflow,
55 #[serde(alias = "arcee-ai", alias = "arcee_ai")]
56 Arcee,
57 #[serde(alias = "siliconflow-cn", alias = "siliconflow-CN")]
58 SiliconflowCN,
59 #[serde(alias = "moonshot-ai", alias = "moonshotai", alias = "moonshot_ai")]
60 Moonshot,
61 Sglang,
62 Vllm,
63 Ollama,
64 #[serde(alias = "hugging-face", alias = "hugging_face", alias = "hf")]
65 Huggingface,
66 #[serde(alias = "together-ai", alias = "together_ai", alias = "togetherai")]
67 Together,
68 #[serde(alias = "baidu-qianfan", alias = "baidu_qianfan", alias = "baidu")]
69 Qianfan,
70 #[serde(
71 alias = "openai-codex",
72 alias = "openai_codex",
73 alias = "codex",
74 alias = "chatgpt",
75 alias = "chatgpt-codex",
76 alias = "chatgpt_codex"
77 )]
78 OpenaiCodex,
79 #[serde(alias = "claude")]
80 Anthropic,
81 #[serde(alias = "open-model", alias = "open_model")]
82 Openmodel,
83 #[serde(
84 alias = "z-ai",
85 alias = "z_ai",
86 alias = "z.ai",
87 alias = "zhipu",
88 alias = "zhipuai",
89 alias = "bigmodel",
90 alias = "big-model"
91 )]
92 Zai,
93 #[serde(
94 alias = "step-fun",
95 alias = "step_fun",
96 alias = "stepfun",
97 alias = "stepflash",
98 alias = "step-flash",
99 alias = "step_flash"
100 )]
101 Stepfun,
102 #[serde(alias = "mini-max", alias = "mini_max", alias = "minimax")]
103 Minimax,
104 #[serde(
105 alias = "minimax_anthropic",
106 alias = "mini-max-anthropic",
107 alias = "mini_max_anthropic"
108 )]
109 MinimaxAnthropic,
110 #[serde(alias = "deep-infra", alias = "deep_infra")]
111 Deepinfra,
112 #[serde(alias = "sakana-ai", alias = "sakana_ai", alias = "fugu")]
113 Sakana,
114 #[serde(alias = "long-cat", alias = "meituan-longcat", alias = "meituan")]
115 LongCat,
116 #[serde(alias = "opencode_go", alias = "opencodego")]
117 OpencodeGo,
118 #[serde(
119 alias = "opencode_zen",
120 alias = "opencodezen",
121 alias = "zen",
122 alias = "opencode"
123 )]
124 OpencodeZen,
125 #[serde(
126 alias = "meta-ai",
127 alias = "meta_ai",
128 alias = "meta-model-api",
129 alias = "meta_model_api",
130 alias = "muse",
131 alias = "muse-spark"
132 )]
133 Meta,
134 #[serde(alias = "x-ai", alias = "x_ai", alias = "grok")]
135 Xai,
136 #[serde(
138 alias = "mistral-ai",
139 alias = "mistral_ai",
140 alias = "mistralai",
141 alias = "la-plateforme",
142 alias = "la_plateforme"
143 )]
144 Mistral,
145 #[serde(
151 alias = "telecom-js",
152 alias = "telecom_js",
153 alias = "telecomjs-cn",
154 alias = "tokenhub"
155 )]
156 Telecomjs,
157 #[serde(
164 alias = "modelstudio-token-plan",
165 alias = "modelstudio_token_plan",
166 alias = "alibaba-token-plan",
167 alias = "dashscope-token-plan"
168 )]
169 ModelstudioTokenPlan,
170 #[serde(
172 alias = "modelstudio-token-plan-anthropic",
173 alias = "modelstudio_token_plan_anthropic",
174 alias = "alibaba-token-plan-anthropic"
175 )]
176 ModelstudioTokenPlanAnthropic,
177 #[serde(
179 alias = "modelstudio-coding-plan",
180 alias = "modelstudio_coding_plan",
181 alias = "alibaba-coding-plan",
182 alias = "dashscope-coding-plan"
183 )]
184 ModelstudioCodingPlan,
185 #[serde(
187 alias = "modelstudio-coding-plan-anthropic",
188 alias = "modelstudio_coding_plan_anthropic",
189 alias = "alibaba-coding-plan-anthropic"
190 )]
191 ModelstudioCodingPlanAnthropic,
192 Custom,
200}
201
202impl ProviderKind {
203 pub const ALL: [Self; 38] = [
210 Self::Deepseek,
211 Self::NvidiaNim,
212 Self::Openai,
213 Self::Atlascloud,
214 Self::WanjieArk,
215 Self::Volcengine,
216 Self::Openrouter,
217 Self::Orcarouter,
218 Self::XiaomiMimo,
219 Self::Novita,
220 Self::Fireworks,
221 Self::Siliconflow,
222 Self::Arcee,
223 Self::SiliconflowCN,
224 Self::Moonshot,
225 Self::Sglang,
226 Self::Vllm,
227 Self::Ollama,
228 Self::Huggingface,
229 Self::Together,
230 Self::Qianfan,
231 Self::OpenaiCodex,
232 Self::Anthropic,
233 Self::Openmodel,
234 Self::Zai,
235 Self::Stepfun,
236 Self::Minimax,
237 Self::Deepinfra,
238 Self::Sakana,
239 Self::LongCat,
240 Self::OpencodeGo,
241 Self::OpencodeZen,
242 Self::Meta,
243 Self::Xai,
244 Self::Mistral,
245 Self::Telecomjs,
246 Self::ModelstudioTokenPlan,
247 Self::Custom,
248 ];
249
250 #[must_use]
251 pub fn all() -> &'static [Self] {
252 &Self::ALL
253 }
254
255 #[must_use]
256 pub fn names_hint() -> String {
257 Self::all()
258 .iter()
259 .map(|provider| provider.as_str())
260 .collect::<Vec<_>>()
261 .join(", ")
262 }
263
264 #[must_use]
265 pub fn as_str(self) -> &'static str {
266 self.provider().id()
267 }
268
269 #[must_use]
270 pub fn parse(value: &str) -> Option<Self> {
271 let trimmed = value.trim();
272 provider::all_providers()
273 .iter()
274 .find(|p| {
275 trimmed.eq_ignore_ascii_case(p.id())
276 || p.aliases().iter().any(|a| trimmed.eq_ignore_ascii_case(a))
277 })
278 .map(|p| p.kind())
279 }
280
281 #[must_use]
300 pub fn parse_config_identity(value: &str) -> Option<Self> {
301 let trimmed = value.trim();
302 provider::all_providers()
303 .iter()
304 .find(|p| {
305 trimmed.eq_ignore_ascii_case(p.id())
306 || trimmed.eq_ignore_ascii_case(p.provider_config_key())
307 })
308 .map(|p| p.kind())
309 .or_else(|| Self::parse(trimmed))
310 }
311
312 #[must_use]
313 pub fn is_siliconflow(self) -> bool {
314 matches!(self, Self::Siliconflow | Self::SiliconflowCN)
315 }
316
317 #[must_use]
331 pub fn secret_store_slot(self) -> &'static str {
332 match self {
333 Self::SiliconflowCN => "siliconflow",
334 Self::ModelstudioTokenPlan
335 | Self::ModelstudioTokenPlanAnthropic
336 | Self::ModelstudioCodingPlan
337 | Self::ModelstudioCodingPlanAnthropic => "modelstudio-token-plan",
338 _ => self.as_str(),
339 }
340 }
341
342 #[must_use]
347 pub fn provider(self) -> &'static dyn provider::Provider {
348 provider::provider_for_kind(self)
349 }
350}