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    Novita,
48    Fireworks,
49    #[serde(alias = "silicon-flow", alias = "silicon_flow")]
50    Siliconflow,
51    #[serde(alias = "arcee-ai", alias = "arcee_ai")]
52    Arcee,
53    #[serde(alias = "siliconflow-cn", alias = "siliconflow-CN")]
54    SiliconflowCN,
55    Moonshot,
56    Sglang,
57    Vllm,
58    Ollama,
59    #[serde(alias = "hugging-face", alias = "hugging_face", alias = "hf")]
60    Huggingface,
61    #[serde(alias = "together-ai", alias = "together_ai")]
62    Together,
63    #[serde(alias = "baidu-qianfan", alias = "baidu_qianfan", alias = "baidu")]
64    Qianfan,
65    #[serde(
66        alias = "openai-codex",
67        alias = "openai_codex",
68        alias = "codex",
69        alias = "chatgpt",
70        alias = "chatgpt-codex",
71        alias = "chatgpt_codex"
72    )]
73    OpenaiCodex,
74    #[serde(alias = "claude")]
75    Anthropic,
76    #[serde(
77        alias = "z-ai",
78        alias = "z_ai",
79        alias = "z.ai",
80        alias = "zhipu",
81        alias = "zhipuai",
82        alias = "bigmodel",
83        alias = "big-model"
84    )]
85    Zai,
86    #[serde(
87        alias = "step-fun",
88        alias = "step_fun",
89        alias = "stepfun",
90        alias = "stepflash",
91        alias = "step-flash",
92        alias = "step_flash"
93    )]
94    Stepfun,
95    #[serde(alias = "mini-max", alias = "mini_max", alias = "minimax")]
96    Minimax,
97    #[serde(alias = "deep-infra", alias = "deep_infra")]
98    Deepinfra,
99    /// User-defined OpenAI-compatible endpoint (#1519).
100    ///
101    /// A single dynamic identity for arbitrary `[providers.<name>]
102    /// kind="openai-compatible"` entries. It speaks the OpenAI Chat Completions
103    /// wire protocol and carries no built-in base URL/model — the concrete
104    /// endpoint and model arrive via config (`base_url` / `model`) and the
105    /// route's `base_url_override`, never from this static descriptor.
106    Custom,
107}
108
109impl ProviderKind {
110    pub const ALL: [Self; 28] = [
111        Self::Deepseek,
112        Self::DeepseekAnthropic,
113        Self::NvidiaNim,
114        Self::Openai,
115        Self::Atlascloud,
116        Self::WanjieArk,
117        Self::Volcengine,
118        Self::Openrouter,
119        Self::XiaomiMimo,
120        Self::Novita,
121        Self::Fireworks,
122        Self::Siliconflow,
123        Self::Arcee,
124        Self::SiliconflowCN,
125        Self::Moonshot,
126        Self::Sglang,
127        Self::Vllm,
128        Self::Ollama,
129        Self::Huggingface,
130        Self::Together,
131        Self::Qianfan,
132        Self::OpenaiCodex,
133        Self::Anthropic,
134        Self::Zai,
135        Self::Stepfun,
136        Self::Minimax,
137        Self::Deepinfra,
138        Self::Custom,
139    ];
140
141    #[must_use]
142    pub fn all() -> &'static [Self] {
143        &Self::ALL
144    }
145
146    #[must_use]
147    pub fn names_hint() -> String {
148        Self::all()
149            .iter()
150            .map(|provider| provider.as_str())
151            .collect::<Vec<_>>()
152            .join(", ")
153    }
154
155    #[must_use]
156    pub fn as_str(self) -> &'static str {
157        self.provider().id()
158    }
159
160    #[must_use]
161    pub fn parse(value: &str) -> Option<Self> {
162        let trimmed = value.trim();
163        provider::all_providers()
164            .iter()
165            .find(|p| {
166                trimmed.eq_ignore_ascii_case(p.id())
167                    || p.aliases().iter().any(|a| trimmed.eq_ignore_ascii_case(a))
168            })
169            .map(|p| p.kind())
170    }
171
172    #[must_use]
173    pub fn is_siliconflow(self) -> bool {
174        matches!(self, Self::Siliconflow | Self::SiliconflowCN)
175    }
176
177    /// Return the built-in metadata entry for this provider.
178    ///
179    /// This is a metadata foundation only; runtime routing still resolves
180    /// through [`crate::ConfigToml::resolve_runtime_options`].
181    #[must_use]
182    pub fn provider(self) -> &'static dyn provider::Provider {
183        provider::provider_for_kind(self)
184    }
185}