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 = "mimo", alias = "xiaomi", alias = "xiaomi_mimo")]
46 XiaomiMimo,
47 #[serde(alias = "novita-ai", alias = "novita_ai")]
48 Novita,
49 #[serde(alias = "fireworks-ai", alias = "fireworks_ai")]
50 Fireworks,
51 #[serde(alias = "silicon-flow", alias = "silicon_flow")]
52 Siliconflow,
53 #[serde(alias = "arcee-ai", alias = "arcee_ai")]
54 Arcee,
55 #[serde(alias = "siliconflow-cn", alias = "siliconflow-CN")]
56 SiliconflowCN,
57 #[serde(alias = "moonshot-ai", alias = "moonshotai", alias = "moonshot_ai")]
58 Moonshot,
59 Sglang,
60 Vllm,
61 Ollama,
62 #[serde(alias = "hugging-face", alias = "hugging_face", alias = "hf")]
63 Huggingface,
64 #[serde(alias = "together-ai", alias = "together_ai", alias = "togetherai")]
65 Together,
66 #[serde(alias = "baidu-qianfan", alias = "baidu_qianfan", alias = "baidu")]
67 Qianfan,
68 #[serde(
69 alias = "openai-codex",
70 alias = "openai_codex",
71 alias = "codex",
72 alias = "chatgpt",
73 alias = "chatgpt-codex",
74 alias = "chatgpt_codex"
75 )]
76 OpenaiCodex,
77 #[serde(alias = "claude")]
78 Anthropic,
79 #[serde(alias = "open-model", alias = "open_model")]
80 Openmodel,
81 #[serde(
82 alias = "z-ai",
83 alias = "z_ai",
84 alias = "z.ai",
85 alias = "zhipu",
86 alias = "zhipuai",
87 alias = "bigmodel",
88 alias = "big-model"
89 )]
90 Zai,
91 #[serde(
92 alias = "step-fun",
93 alias = "step_fun",
94 alias = "stepfun",
95 alias = "stepflash",
96 alias = "step-flash",
97 alias = "step_flash"
98 )]
99 Stepfun,
100 #[serde(alias = "mini-max", alias = "mini_max", alias = "minimax")]
101 Minimax,
102 #[serde(
103 alias = "minimax_anthropic",
104 alias = "mini-max-anthropic",
105 alias = "mini_max_anthropic"
106 )]
107 MinimaxAnthropic,
108 #[serde(alias = "deep-infra", alias = "deep_infra")]
109 Deepinfra,
110 #[serde(alias = "sakana-ai", alias = "sakana_ai", alias = "fugu")]
111 Sakana,
112 #[serde(alias = "long-cat", alias = "meituan-longcat", alias = "meituan")]
113 LongCat,
114 #[serde(alias = "opencode_go", alias = "opencodego")]
115 OpencodeGo,
116 #[serde(
117 alias = "opencode_zen",
118 alias = "opencodezen",
119 alias = "zen",
120 alias = "opencode"
121 )]
122 OpencodeZen,
123 #[serde(
124 alias = "meta-ai",
125 alias = "meta_ai",
126 alias = "meta-model-api",
127 alias = "meta_model_api",
128 alias = "muse",
129 alias = "muse-spark"
130 )]
131 Meta,
132 #[serde(alias = "x-ai", alias = "x_ai", alias = "grok")]
133 Xai,
134 #[serde(
136 alias = "mistral-ai",
137 alias = "mistral_ai",
138 alias = "mistralai",
139 alias = "la-plateforme",
140 alias = "la_plateforme"
141 )]
142 Mistral,
143 #[serde(
149 alias = "telecom-js",
150 alias = "telecom_js",
151 alias = "telecomjs-cn",
152 alias = "tokenhub"
153 )]
154 Telecomjs,
155 #[serde(
162 alias = "modelstudio-token-plan",
163 alias = "modelstudio_token_plan",
164 alias = "alibaba-token-plan",
165 alias = "dashscope-token-plan"
166 )]
167 ModelstudioTokenPlan,
168 #[serde(
170 alias = "modelstudio-token-plan-anthropic",
171 alias = "modelstudio_token_plan_anthropic",
172 alias = "alibaba-token-plan-anthropic"
173 )]
174 ModelstudioTokenPlanAnthropic,
175 #[serde(
177 alias = "modelstudio-coding-plan",
178 alias = "modelstudio_coding_plan",
179 alias = "alibaba-coding-plan",
180 alias = "dashscope-coding-plan"
181 )]
182 ModelstudioCodingPlan,
183 #[serde(
185 alias = "modelstudio-coding-plan-anthropic",
186 alias = "modelstudio_coding_plan_anthropic",
187 alias = "alibaba-coding-plan-anthropic"
188 )]
189 ModelstudioCodingPlanAnthropic,
190 Custom,
198}
199
200impl ProviderKind {
201 pub const ALL: [Self; 37] = [
208 Self::Deepseek,
209 Self::NvidiaNim,
210 Self::Openai,
211 Self::Atlascloud,
212 Self::WanjieArk,
213 Self::Volcengine,
214 Self::Openrouter,
215 Self::XiaomiMimo,
216 Self::Novita,
217 Self::Fireworks,
218 Self::Siliconflow,
219 Self::Arcee,
220 Self::SiliconflowCN,
221 Self::Moonshot,
222 Self::Sglang,
223 Self::Vllm,
224 Self::Ollama,
225 Self::Huggingface,
226 Self::Together,
227 Self::Qianfan,
228 Self::OpenaiCodex,
229 Self::Anthropic,
230 Self::Openmodel,
231 Self::Zai,
232 Self::Stepfun,
233 Self::Minimax,
234 Self::Deepinfra,
235 Self::Sakana,
236 Self::LongCat,
237 Self::OpencodeGo,
238 Self::OpencodeZen,
239 Self::Meta,
240 Self::Xai,
241 Self::Mistral,
242 Self::Telecomjs,
243 Self::ModelstudioTokenPlan,
244 Self::Custom,
245 ];
246
247 #[must_use]
248 pub fn all() -> &'static [Self] {
249 &Self::ALL
250 }
251
252 #[must_use]
253 pub fn names_hint() -> String {
254 Self::all()
255 .iter()
256 .map(|provider| provider.as_str())
257 .collect::<Vec<_>>()
258 .join(", ")
259 }
260
261 #[must_use]
262 pub fn as_str(self) -> &'static str {
263 self.provider().id()
264 }
265
266 #[must_use]
267 pub fn parse(value: &str) -> Option<Self> {
268 let trimmed = value.trim();
269 provider::all_providers()
270 .iter()
271 .find(|p| {
272 trimmed.eq_ignore_ascii_case(p.id())
273 || p.aliases().iter().any(|a| trimmed.eq_ignore_ascii_case(a))
274 })
275 .map(|p| p.kind())
276 }
277
278 #[must_use]
297 pub fn parse_config_identity(value: &str) -> Option<Self> {
298 let trimmed = value.trim();
299 provider::all_providers()
300 .iter()
301 .find(|p| {
302 trimmed.eq_ignore_ascii_case(p.id())
303 || trimmed.eq_ignore_ascii_case(p.provider_config_key())
304 })
305 .map(|p| p.kind())
306 .or_else(|| Self::parse(trimmed))
307 }
308
309 #[must_use]
310 pub fn is_siliconflow(self) -> bool {
311 matches!(self, Self::Siliconflow | Self::SiliconflowCN)
312 }
313
314 #[must_use]
328 pub fn secret_store_slot(self) -> &'static str {
329 match self {
330 Self::SiliconflowCN => "siliconflow",
331 Self::ModelstudioTokenPlan
332 | Self::ModelstudioTokenPlanAnthropic
333 | Self::ModelstudioCodingPlan
334 | Self::ModelstudioCodingPlanAnthropic => "modelstudio-token-plan",
335 _ => self.as_str(),
336 }
337 }
338
339 #[must_use]
344 pub fn provider(self) -> &'static dyn provider::Provider {
345 provider::provider_for_kind(self)
346 }
347}