Skip to main content

codewhale_config/
provider_kind.rs

1//! The canonical [`ProviderKind`] enum (#3311): the set of built-in provider
2//! kinds, their serde aliases, and identity helpers (`all`, `as_str`, `parse`,
3//! `provider`). Extracted verbatim from `lib.rs` to separate provider identity
4//! from config schema/loading; re-exported at the crate root so
5//! `codewhale_config::ProviderKind` is unchanged. Behavior is identical.
6
7use 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(
115        alias = "meta-ai",
116        alias = "meta_ai",
117        alias = "meta-model-api",
118        alias = "meta_model_api",
119        alias = "muse",
120        alias = "muse-spark"
121    )]
122    Meta,
123    #[serde(alias = "x-ai", alias = "x_ai", alias = "grok")]
124    Xai,
125    /// User-defined OpenAI-compatible endpoint (#1519).
126    ///
127    /// A single dynamic identity for arbitrary `[providers.<name>]
128    /// kind="openai-compatible"` entries. It speaks the OpenAI Chat Completions
129    /// wire protocol and carries no built-in base URL/model — the concrete
130    /// endpoint and model arrive via config (`base_url` / `model`) and the
131    /// route's `base_url_override`, never from this static descriptor.
132    Custom,
133}
134
135impl ProviderKind {
136    pub const ALL: [Self; 34] = [
137        Self::Deepseek,
138        Self::DeepseekAnthropic,
139        Self::NvidiaNim,
140        Self::Openai,
141        Self::Atlascloud,
142        Self::WanjieArk,
143        Self::Volcengine,
144        Self::Openrouter,
145        Self::XiaomiMimo,
146        Self::Novita,
147        Self::Fireworks,
148        Self::Siliconflow,
149        Self::Arcee,
150        Self::SiliconflowCN,
151        Self::Moonshot,
152        Self::Sglang,
153        Self::Vllm,
154        Self::Ollama,
155        Self::Huggingface,
156        Self::Together,
157        Self::Qianfan,
158        Self::OpenaiCodex,
159        Self::Anthropic,
160        Self::Openmodel,
161        Self::Zai,
162        Self::Stepfun,
163        Self::Minimax,
164        Self::MinimaxAnthropic,
165        Self::Deepinfra,
166        Self::Sakana,
167        Self::LongCat,
168        Self::Meta,
169        Self::Xai,
170        Self::Custom,
171    ];
172
173    #[must_use]
174    pub fn all() -> &'static [Self] {
175        &Self::ALL
176    }
177
178    #[must_use]
179    pub fn names_hint() -> String {
180        Self::all()
181            .iter()
182            .map(|provider| provider.as_str())
183            .collect::<Vec<_>>()
184            .join(", ")
185    }
186
187    #[must_use]
188    pub fn as_str(self) -> &'static str {
189        self.provider().id()
190    }
191
192    #[must_use]
193    pub fn parse(value: &str) -> Option<Self> {
194        let trimmed = value.trim();
195        provider::all_providers()
196            .iter()
197            .find(|p| {
198                trimmed.eq_ignore_ascii_case(p.id())
199                    || p.aliases().iter().any(|a| trimmed.eq_ignore_ascii_case(a))
200            })
201            .map(|p| p.kind())
202    }
203
204    #[must_use]
205    pub fn is_siliconflow(self) -> bool {
206        matches!(self, Self::Siliconflow | Self::SiliconflowCN)
207    }
208
209    /// Return the built-in metadata entry for this provider.
210    ///
211    /// This is a metadata foundation only; runtime routing still resolves
212    /// through [`crate::ConfigToml::resolve_runtime_options`].
213    #[must_use]
214    pub fn provider(self) -> &'static dyn provider::Provider {
215        provider::provider_for_kind(self)
216    }
217}