refactor(config): extract provider metadata into data-driven registry
This commit is contained in:
+10
-54
@@ -171,8 +171,8 @@ impl ProviderKind {
|
||||
Self::Novita,
|
||||
Self::Fireworks,
|
||||
Self::Siliconflow,
|
||||
Self::SiliconflowCN,
|
||||
Self::Arcee,
|
||||
Self::SiliconflowCN,
|
||||
Self::Moonshot,
|
||||
Self::Sglang,
|
||||
Self::Vllm,
|
||||
@@ -185,63 +185,19 @@ impl ProviderKind {
|
||||
|
||||
#[must_use]
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Deepseek => "deepseek",
|
||||
Self::NvidiaNim => "nvidia-nim",
|
||||
Self::Openai => "openai",
|
||||
Self::Atlascloud => "atlascloud",
|
||||
Self::WanjieArk => "wanjie-ark",
|
||||
Self::Volcengine => "volcengine",
|
||||
Self::Openrouter => "openrouter",
|
||||
Self::XiaomiMimo => "xiaomi-mimo",
|
||||
Self::Novita => "novita",
|
||||
Self::Fireworks => "fireworks",
|
||||
Self::Siliconflow => "siliconflow",
|
||||
Self::SiliconflowCN => "siliconflow-CN",
|
||||
Self::Arcee => "arcee",
|
||||
Self::Moonshot => "moonshot",
|
||||
Self::Sglang => "sglang",
|
||||
Self::Vllm => "vllm",
|
||||
Self::Ollama => "ollama",
|
||||
Self::Huggingface => "huggingface",
|
||||
Self::Together => "together",
|
||||
Self::OpenaiCodex => "openai-codex",
|
||||
Self::Anthropic => "anthropic",
|
||||
}
|
||||
self.provider().id()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"deepseek" | "deep-seek" | "deepseek-cn" | "deepseek_china" | "deepseekcn"
|
||||
| "deepseek-china" => Some(Self::Deepseek),
|
||||
"nvidia" | "nvidia-nim" | "nvidia_nim" | "nim" => Some(Self::NvidiaNim),
|
||||
"openai" | "open-ai" => Some(Self::Openai),
|
||||
"atlascloud" | "atlas-cloud" | "atlas_cloud" | "atlas" => Some(Self::Atlascloud),
|
||||
"wanjie" | "wanjie-ark" | "wanjie_ark" | "ark-wanjie" | "ark_wanjie" | "wanjieark"
|
||||
| "wanjie-maas" | "wanjie_maas" | "wanjiemaas" => Some(Self::WanjieArk),
|
||||
"volcengine" | "volcengine-ark" | "volcengine_ark" | "ark" | "volc-ark"
|
||||
| "volcengineark" => Some(Self::Volcengine),
|
||||
"openrouter" | "open_router" => Some(Self::Openrouter),
|
||||
"xiaomi-mimo" | "xiaomi_mimo" | "xiaomimimo" | "mimo" | "xiaomi" => {
|
||||
Some(Self::XiaomiMimo)
|
||||
}
|
||||
"novita" => Some(Self::Novita),
|
||||
"fireworks" | "fireworks-ai" => Some(Self::Fireworks),
|
||||
"siliconflow" | "silicon-flow" | "silicon_flow" => Some(Self::Siliconflow),
|
||||
"siliconflow-cn" | "siliconflow-CN" => Some(Self::SiliconflowCN),
|
||||
"arcee" | "arcee-ai" | "arcee_ai" => Some(Self::Arcee),
|
||||
"moonshot" | "moonshot-ai" | "kimi" | "kimi-k2" => Some(Self::Moonshot),
|
||||
"sglang" | "sg-lang" => Some(Self::Sglang),
|
||||
"vllm" | "v-llm" => Some(Self::Vllm),
|
||||
"ollama" | "ollama-local" => Some(Self::Ollama),
|
||||
"huggingface" | "hugging-face" | "hugging_face" | "hf" => Some(Self::Huggingface),
|
||||
"together" | "together-ai" | "together_ai" => Some(Self::Together),
|
||||
"anthropic" | "claude" => Some(Self::Anthropic),
|
||||
"openai-codex" | "openai_codex" | "openaicodex" | "codex" | "chatgpt"
|
||||
| "chatgpt-codex" | "chatgpt_codex" | "chatgptcodex" => Some(Self::OpenaiCodex),
|
||||
_ => None,
|
||||
}
|
||||
let trimmed = value.trim();
|
||||
provider::all_providers()
|
||||
.iter()
|
||||
.find(|p| {
|
||||
trimmed.eq_ignore_ascii_case(p.id())
|
||||
|| p.aliases().iter().any(|a| trimmed.eq_ignore_ascii_case(a))
|
||||
})
|
||||
.map(|p| p.kind())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
+110
-49
@@ -56,6 +56,11 @@ pub trait Provider: Send + Sync {
|
||||
/// TOML table key under `[providers.<key>]`.
|
||||
fn provider_config_key(&self) -> &'static str;
|
||||
|
||||
/// Alternate names accepted during provider resolution.
|
||||
fn aliases(&self) -> &'static [&'static str] {
|
||||
&[]
|
||||
}
|
||||
|
||||
/// Wire format used by the provider.
|
||||
fn wire(&self) -> WireFormat {
|
||||
WireFormat::ChatCompletions
|
||||
@@ -66,16 +71,22 @@ macro_rules! provider {
|
||||
(
|
||||
$struct_name:ident,
|
||||
$kind:ident,
|
||||
$id:literal,
|
||||
$display_name:literal,
|
||||
$base_url:ident,
|
||||
$model:ident,
|
||||
[$($env_var:literal),* $(,)?],
|
||||
$config_key:literal
|
||||
$config_key:literal,
|
||||
aliases: [$($alias:literal),* $(,)?]
|
||||
) => {
|
||||
/// Zero-sized metadata entry for this built-in provider.
|
||||
pub struct $struct_name;
|
||||
|
||||
impl Provider for $struct_name {
|
||||
fn id(&self) -> &'static str {
|
||||
$id
|
||||
}
|
||||
|
||||
fn kind(&self) -> ProviderKind {
|
||||
ProviderKind::$kind
|
||||
}
|
||||
@@ -99,6 +110,10 @@ macro_rules! provider {
|
||||
fn provider_config_key(&self) -> &'static str {
|
||||
$config_key
|
||||
}
|
||||
|
||||
fn aliases(&self) -> &'static [&'static str] {
|
||||
&[$($alias),*]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -106,42 +121,51 @@ macro_rules! provider {
|
||||
provider!(
|
||||
Deepseek,
|
||||
Deepseek,
|
||||
"deepseek",
|
||||
"DeepSeek",
|
||||
DEFAULT_DEEPSEEK_BASE_URL,
|
||||
DEFAULT_DEEPSEEK_MODEL,
|
||||
["DEEPSEEK_API_KEY"],
|
||||
"deepseek"
|
||||
"deepseek",
|
||||
aliases: ["deep-seek", "deepseek-cn", "deepseek_china", "deepseekcn", "deepseek-china"]
|
||||
);
|
||||
provider!(
|
||||
NvidiaNim,
|
||||
NvidiaNim,
|
||||
"nvidia-nim",
|
||||
"NVIDIA NIM",
|
||||
DEFAULT_NVIDIA_NIM_BASE_URL,
|
||||
DEFAULT_NVIDIA_NIM_MODEL,
|
||||
["NVIDIA_API_KEY", "NVIDIA_NIM_API_KEY", "DEEPSEEK_API_KEY"],
|
||||
"nvidia_nim"
|
||||
"nvidia_nim",
|
||||
aliases: ["nvidia", "nvidia_nim", "nim"]
|
||||
);
|
||||
provider!(
|
||||
Openai,
|
||||
Openai,
|
||||
"openai",
|
||||
"OpenAI-compatible",
|
||||
DEFAULT_OPENAI_BASE_URL,
|
||||
DEFAULT_OPENAI_MODEL,
|
||||
["OPENAI_API_KEY"],
|
||||
"openai"
|
||||
"openai",
|
||||
aliases: ["open-ai"]
|
||||
);
|
||||
provider!(
|
||||
Atlascloud,
|
||||
Atlascloud,
|
||||
"atlascloud",
|
||||
"AtlasCloud",
|
||||
DEFAULT_ATLASCLOUD_BASE_URL,
|
||||
DEFAULT_ATLASCLOUD_MODEL,
|
||||
["ATLASCLOUD_API_KEY"],
|
||||
"atlascloud"
|
||||
"atlascloud",
|
||||
aliases: ["atlas-cloud", "atlas_cloud", "atlas"]
|
||||
);
|
||||
provider!(
|
||||
WanjieArk,
|
||||
WanjieArk,
|
||||
"wanjie-ark",
|
||||
"Wanjie Ark",
|
||||
DEFAULT_WANJIE_ARK_BASE_URL,
|
||||
DEFAULT_WANJIE_ARK_MODEL,
|
||||
@@ -150,11 +174,13 @@ provider!(
|
||||
"WANJIE_API_KEY",
|
||||
"WANJIE_MAAS_API_KEY"
|
||||
],
|
||||
"wanjie_ark"
|
||||
"wanjie_ark",
|
||||
aliases: ["wanjie", "wanjie_ark", "ark-wanjie", "ark_wanjie", "wanjieark", "wanjie-maas", "wanjie_maas", "wanjiemaas"]
|
||||
);
|
||||
provider!(
|
||||
Volcengine,
|
||||
Volcengine,
|
||||
"volcengine",
|
||||
"Volcengine Ark",
|
||||
DEFAULT_VOLCENGINE_BASE_URL,
|
||||
DEFAULT_VOLCENGINE_MODEL,
|
||||
@@ -163,20 +189,24 @@ provider!(
|
||||
"VOLCENGINE_ARK_API_KEY",
|
||||
"ARK_API_KEY"
|
||||
],
|
||||
"volcengine"
|
||||
"volcengine",
|
||||
aliases: ["volcengine-ark", "volcengine_ark", "ark", "volc-ark", "volcengineark"]
|
||||
);
|
||||
provider!(
|
||||
Openrouter,
|
||||
Openrouter,
|
||||
"openrouter",
|
||||
"OpenRouter",
|
||||
DEFAULT_OPENROUTER_BASE_URL,
|
||||
DEFAULT_OPENROUTER_MODEL,
|
||||
["OPENROUTER_API_KEY"],
|
||||
"openrouter"
|
||||
"openrouter",
|
||||
aliases: ["open_router"]
|
||||
);
|
||||
provider!(
|
||||
XiaomiMimo,
|
||||
XiaomiMimo,
|
||||
"xiaomi-mimo",
|
||||
"Xiaomi MiMo",
|
||||
DEFAULT_XIAOMI_MIMO_BASE_URL,
|
||||
DEFAULT_XIAOMI_MIMO_MODEL,
|
||||
@@ -187,112 +217,145 @@ provider!(
|
||||
"XIAOMI_API_KEY",
|
||||
"MIMO_API_KEY",
|
||||
],
|
||||
"xiaomi_mimo"
|
||||
"xiaomi_mimo",
|
||||
aliases: ["xiaomi_mimo", "xiaomimimo", "mimo", "xiaomi"]
|
||||
);
|
||||
provider!(
|
||||
Novita,
|
||||
Novita,
|
||||
"Novita",
|
||||
"novita",
|
||||
"Novita AI",
|
||||
DEFAULT_NOVITA_BASE_URL,
|
||||
DEFAULT_NOVITA_MODEL,
|
||||
["NOVITA_API_KEY"],
|
||||
"novita"
|
||||
"novita",
|
||||
aliases: []
|
||||
);
|
||||
provider!(
|
||||
Fireworks,
|
||||
Fireworks,
|
||||
"Fireworks",
|
||||
"fireworks",
|
||||
"Fireworks AI",
|
||||
DEFAULT_FIREWORKS_BASE_URL,
|
||||
DEFAULT_FIREWORKS_MODEL,
|
||||
["FIREWORKS_API_KEY"],
|
||||
"fireworks"
|
||||
"fireworks",
|
||||
aliases: ["fireworks-ai"]
|
||||
);
|
||||
provider!(
|
||||
Siliconflow,
|
||||
Siliconflow,
|
||||
"siliconflow",
|
||||
"SiliconFlow",
|
||||
DEFAULT_SILICONFLOW_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_MODEL,
|
||||
["SILICONFLOW_API_KEY"],
|
||||
"siliconflow"
|
||||
"siliconflow",
|
||||
aliases: ["silicon-flow", "silicon_flow"]
|
||||
);
|
||||
provider!(
|
||||
SiliconflowCN,
|
||||
SiliconflowCN,
|
||||
"SiliconFlow CN",
|
||||
"siliconflow-CN",
|
||||
"SiliconFlow (China)",
|
||||
DEFAULT_SILICONFLOW_CN_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_MODEL,
|
||||
["SILICONFLOW_API_KEY"],
|
||||
"siliconflow"
|
||||
"siliconflow",
|
||||
aliases: [
|
||||
"silicon-flow-cn",
|
||||
"silicon-flow-CN",
|
||||
"silicon_flow_cn",
|
||||
"silicon_flow_CN",
|
||||
"siliconflow-china",
|
||||
]
|
||||
);
|
||||
provider!(
|
||||
Arcee,
|
||||
Arcee,
|
||||
"Arcee",
|
||||
"arcee",
|
||||
"Arcee AI",
|
||||
DEFAULT_ARCEE_BASE_URL,
|
||||
DEFAULT_ARCEE_MODEL,
|
||||
["ARCEE_API_KEY"],
|
||||
"arcee"
|
||||
"arcee",
|
||||
aliases: ["arcee-ai", "arcee_ai"]
|
||||
);
|
||||
provider!(
|
||||
Moonshot,
|
||||
Moonshot,
|
||||
"Moonshot",
|
||||
"moonshot",
|
||||
"Moonshot/Kimi",
|
||||
DEFAULT_MOONSHOT_BASE_URL,
|
||||
DEFAULT_MOONSHOT_MODEL,
|
||||
["MOONSHOT_API_KEY", "KIMI_API_KEY"],
|
||||
"moonshot"
|
||||
"moonshot",
|
||||
aliases: ["moonshot-ai", "kimi", "kimi-k2"]
|
||||
);
|
||||
provider!(
|
||||
Sglang,
|
||||
Sglang,
|
||||
"sglang",
|
||||
"SGLang",
|
||||
DEFAULT_SGLANG_BASE_URL,
|
||||
DEFAULT_SGLANG_MODEL,
|
||||
["SGLANG_API_KEY"],
|
||||
"sglang"
|
||||
"sglang",
|
||||
aliases: ["sg-lang"]
|
||||
);
|
||||
provider!(
|
||||
Vllm,
|
||||
Vllm,
|
||||
"vllm",
|
||||
"vLLM",
|
||||
DEFAULT_VLLM_BASE_URL,
|
||||
DEFAULT_VLLM_MODEL,
|
||||
["VLLM_API_KEY"],
|
||||
"vllm"
|
||||
"vllm",
|
||||
aliases: ["v-llm"]
|
||||
);
|
||||
provider!(
|
||||
Ollama,
|
||||
Ollama,
|
||||
"ollama",
|
||||
"Ollama",
|
||||
DEFAULT_OLLAMA_BASE_URL,
|
||||
DEFAULT_OLLAMA_MODEL,
|
||||
["OLLAMA_API_KEY"],
|
||||
"ollama"
|
||||
"ollama",
|
||||
aliases: ["ollama-local"]
|
||||
);
|
||||
provider!(
|
||||
Huggingface,
|
||||
Huggingface,
|
||||
"huggingface",
|
||||
"Hugging Face",
|
||||
DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
DEFAULT_HUGGINGFACE_MODEL,
|
||||
["HUGGINGFACE_API_KEY", "HF_TOKEN"],
|
||||
"huggingface"
|
||||
"huggingface",
|
||||
aliases: ["hugging-face", "hugging_face", "hf"]
|
||||
);
|
||||
provider!(
|
||||
Together,
|
||||
Together,
|
||||
"together",
|
||||
"Together AI",
|
||||
DEFAULT_TOGETHER_BASE_URL,
|
||||
DEFAULT_TOGETHER_MODEL,
|
||||
["TOGETHER_API_KEY"],
|
||||
"together"
|
||||
"together",
|
||||
aliases: ["together-ai", "together_ai"]
|
||||
);
|
||||
|
||||
/// OpenAI Codex / ChatGPT OAuth provider using the Responses API.
|
||||
pub struct OpenaiCodex;
|
||||
|
||||
impl Provider for OpenaiCodex {
|
||||
fn id(&self) -> &'static str {
|
||||
"openai-codex"
|
||||
}
|
||||
|
||||
fn kind(&self) -> ProviderKind {
|
||||
ProviderKind::OpenaiCodex
|
||||
}
|
||||
@@ -317,6 +380,18 @@ impl Provider for OpenaiCodex {
|
||||
"openai_codex"
|
||||
}
|
||||
|
||||
fn aliases(&self) -> &'static [&'static str] {
|
||||
&[
|
||||
"openai_codex",
|
||||
"openaicodex",
|
||||
"codex",
|
||||
"chatgpt",
|
||||
"chatgpt-codex",
|
||||
"chatgpt_codex",
|
||||
"chatgptcodex",
|
||||
]
|
||||
}
|
||||
|
||||
fn wire(&self) -> WireFormat {
|
||||
WireFormat::Responses
|
||||
}
|
||||
@@ -326,6 +401,10 @@ impl Provider for OpenaiCodex {
|
||||
pub struct Anthropic;
|
||||
|
||||
impl Provider for Anthropic {
|
||||
fn id(&self) -> &'static str {
|
||||
"anthropic"
|
||||
}
|
||||
|
||||
fn kind(&self) -> ProviderKind {
|
||||
ProviderKind::Anthropic
|
||||
}
|
||||
@@ -389,8 +468,8 @@ static PROVIDER_REGISTRY: [&dyn Provider; 21] = [
|
||||
&NOVITA,
|
||||
&FIREWORKS,
|
||||
&SILICONFLOW,
|
||||
&SILICONFLOW_CN,
|
||||
&ARCEE,
|
||||
&SILICONFLOW_CN,
|
||||
&MOONSHOT,
|
||||
&SGLANG,
|
||||
&VLLM,
|
||||
@@ -426,27 +505,9 @@ pub fn resolve_provider(id_or_alias: &str) -> Option<&'static dyn Provider> {
|
||||
/// Return metadata for a known provider kind.
|
||||
#[must_use]
|
||||
pub fn provider_for_kind(kind: ProviderKind) -> &'static dyn Provider {
|
||||
match kind {
|
||||
ProviderKind::Deepseek => &DEEPSEEK,
|
||||
ProviderKind::NvidiaNim => &NVIDIA_NIM,
|
||||
ProviderKind::Openai => &OPENAI,
|
||||
ProviderKind::Atlascloud => &ATLASCLOUD,
|
||||
ProviderKind::WanjieArk => &WANJIE_ARK,
|
||||
ProviderKind::Volcengine => &VOLCENGINE,
|
||||
ProviderKind::Openrouter => &OPENROUTER,
|
||||
ProviderKind::XiaomiMimo => &XIAOMI_MIMO,
|
||||
ProviderKind::Novita => &NOVITA,
|
||||
ProviderKind::Fireworks => &FIREWORKS,
|
||||
ProviderKind::Siliconflow => &SILICONFLOW,
|
||||
ProviderKind::SiliconflowCN => &SILICONFLOW_CN,
|
||||
ProviderKind::Arcee => &ARCEE,
|
||||
ProviderKind::Moonshot => &MOONSHOT,
|
||||
ProviderKind::Sglang => &SGLANG,
|
||||
ProviderKind::Vllm => &VLLM,
|
||||
ProviderKind::Ollama => &OLLAMA,
|
||||
ProviderKind::Huggingface => &HUGGINGFACE,
|
||||
ProviderKind::Together => &TOGETHER,
|
||||
ProviderKind::OpenaiCodex => &OPENAI_CODEX,
|
||||
ProviderKind::Anthropic => &ANTHROPIC,
|
||||
}
|
||||
PROVIDER_REGISTRY
|
||||
.iter()
|
||||
.find(|p| p.kind() == kind)
|
||||
.copied()
|
||||
.expect("ProviderKind variant missing from PROVIDER_REGISTRY")
|
||||
}
|
||||
|
||||
+82
-103
@@ -212,126 +212,105 @@ impl ApiProvider {
|
||||
|
||||
#[must_use]
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"deepseek" | "deep-seek" => Some(Self::Deepseek),
|
||||
"deepseek-cn" | "deepseek_china" | "deepseekcn" | "deepseek-china" => {
|
||||
Some(Self::DeepseekCN)
|
||||
}
|
||||
"nvidia" | "nvidia-nim" | "nvidia_nim" | "nim" => Some(Self::NvidiaNim),
|
||||
"openai" | "open-ai" => Some(Self::Openai),
|
||||
"atlascloud" | "atlas-cloud" | "atlas_cloud" | "atlas" => Some(Self::Atlascloud),
|
||||
"wanjie" | "wanjie-ark" | "wanjie_ark" | "ark-wanjie" | "ark_wanjie" | "wanjieark"
|
||||
| "wanjie-maas" | "wanjie_maas" | "wanjiemaas" => Some(Self::WanjieArk),
|
||||
"volcengine" | "volcengine-ark" | "volcengine_ark" | "ark" | "volc-ark"
|
||||
| "volcengineark" => Some(Self::Volcengine),
|
||||
"openrouter" | "open_router" => Some(Self::Openrouter),
|
||||
"xiaomi-mimo" | "xiaomi_mimo" | "xiaomimimo" | "mimo" | "xiaomi" => {
|
||||
Some(Self::XiaomiMimo)
|
||||
}
|
||||
"novita" => Some(Self::Novita),
|
||||
"fireworks" | "fireworks-ai" => Some(Self::Fireworks),
|
||||
"siliconflow" | "silicon-flow" | "silicon_flow" => Some(Self::Siliconflow),
|
||||
"siliconflow-cn" | "siliconflow-CN" | "silicon-flow-cn" | "silicon-flow-CN"
|
||||
| "silicon_flow_cn" | "silicon_flow_CN" | "siliconflow-china" => {
|
||||
Some(Self::SiliconflowCn)
|
||||
}
|
||||
"arcee" | "arcee-ai" | "arcee_ai" => Some(Self::Arcee),
|
||||
"moonshot" | "moonshot-ai" | "kimi" | "kimi-k2" => Some(Self::Moonshot),
|
||||
"sglang" | "sg-lang" => Some(Self::Sglang),
|
||||
"vllm" | "v-llm" => Some(Self::Vllm),
|
||||
"ollama" | "ollama-local" => Some(Self::Ollama),
|
||||
"huggingface" | "hugging-face" | "hugging_face" | "hf" => Some(Self::Huggingface),
|
||||
"together" | "together-ai" | "together_ai" => Some(Self::Together),
|
||||
"anthropic" | "claude" => Some(Self::Anthropic),
|
||||
"openai-codex" | "openai_codex" | "openaicodex" | "codex" | "chatgpt"
|
||||
| "chatgpt-codex" | "chatgpt_codex" | "chatgptcodex" => Some(Self::OpenaiCodex),
|
||||
_ => None,
|
||||
let trimmed = value.trim();
|
||||
// ApiProvider-specific: "deepseek-cn" is a legacy variant here,
|
||||
// while ProviderKind treats it as a Deepseek alias.
|
||||
if trimmed.eq_ignore_ascii_case("deepseek-cn")
|
||||
|| trimmed.eq_ignore_ascii_case("deepseek_china")
|
||||
|| trimmed.eq_ignore_ascii_case("deepseekcn")
|
||||
|| trimmed.eq_ignore_ascii_case("deepseek-china")
|
||||
{
|
||||
return Some(Self::DeepseekCN);
|
||||
}
|
||||
codewhale_config::ProviderKind::parse(value).map(Self::from_kind)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Deepseek => "deepseek",
|
||||
Self::DeepseekCN => "deepseek-cn",
|
||||
Self::NvidiaNim => "nvidia-nim",
|
||||
Self::Openai => "openai",
|
||||
Self::Atlascloud => "atlascloud",
|
||||
Self::WanjieArk => "wanjie-ark",
|
||||
Self::Volcengine => "volcengine",
|
||||
Self::Openrouter => "openrouter",
|
||||
Self::XiaomiMimo => "xiaomi-mimo",
|
||||
Self::Novita => "novita",
|
||||
Self::Fireworks => "fireworks",
|
||||
Self::Siliconflow => "siliconflow",
|
||||
Self::SiliconflowCn => "siliconflow-CN",
|
||||
Self::Arcee => "arcee",
|
||||
Self::Moonshot => "moonshot",
|
||||
Self::Sglang => "sglang",
|
||||
Self::Vllm => "vllm",
|
||||
Self::Ollama => "ollama",
|
||||
Self::Huggingface => "huggingface",
|
||||
Self::Together => "together",
|
||||
Self::OpenaiCodex => "openai-codex",
|
||||
Self::Anthropic => "anthropic",
|
||||
match self.kind() {
|
||||
Some(kind) => kind.as_str(),
|
||||
None => "deepseek-cn",
|
||||
}
|
||||
}
|
||||
|
||||
/// Human-friendly label for picker UIs / status chips.
|
||||
#[must_use]
|
||||
pub fn display_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Deepseek => "DeepSeek",
|
||||
Self::DeepseekCN => "DeepSeek (legacy alias)",
|
||||
Self::NvidiaNim => "NVIDIA NIM",
|
||||
Self::Openai => "OpenAI-compatible",
|
||||
Self::Atlascloud => "AtlasCloud",
|
||||
Self::WanjieArk => "Wanjie Ark",
|
||||
Self::Volcengine => "Volcengine Ark",
|
||||
Self::Openrouter => "OpenRouter",
|
||||
Self::XiaomiMimo => "Xiaomi MiMo",
|
||||
Self::Novita => "Novita AI",
|
||||
Self::Fireworks => "Fireworks AI",
|
||||
Self::Siliconflow => "SiliconFlow",
|
||||
Self::SiliconflowCn => "SiliconFlow (China)",
|
||||
Self::Arcee => "Arcee AI",
|
||||
Self::Moonshot => "Moonshot/Kimi",
|
||||
Self::Sglang => "SGLang",
|
||||
Self::Vllm => "vLLM",
|
||||
Self::Ollama => "Ollama",
|
||||
Self::Huggingface => "Hugging Face",
|
||||
Self::Together => "Together AI",
|
||||
Self::OpenaiCodex => "OpenAI Codex (ChatGPT)",
|
||||
Self::Anthropic => "Anthropic",
|
||||
match self.kind() {
|
||||
Some(kind) => kind.provider().display_name(),
|
||||
None => "DeepSeek (legacy alias)",
|
||||
}
|
||||
}
|
||||
|
||||
/// All providers, in the order shown in the picker.
|
||||
#[must_use]
|
||||
pub fn all() -> &'static [Self] {
|
||||
&[
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
Self::Atlascloud,
|
||||
Self::WanjieArk,
|
||||
Self::Volcengine,
|
||||
Self::Openrouter,
|
||||
Self::XiaomiMimo,
|
||||
Self::Novita,
|
||||
Self::Fireworks,
|
||||
Self::Siliconflow,
|
||||
Self::SiliconflowCn,
|
||||
Self::Arcee,
|
||||
Self::Moonshot,
|
||||
Self::Sglang,
|
||||
Self::Vllm,
|
||||
Self::Ollama,
|
||||
Self::Huggingface,
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
]
|
||||
&Self::FROM_KIND_LOOKUP
|
||||
}
|
||||
|
||||
/// `ApiProvider` discriminant → `ProviderKind` lookup.
|
||||
/// Index 1 is `None` for the legacy `DeepseekCN` variant.
|
||||
const KIND_LOOKUP: [Option<codewhale_config::ProviderKind>; 22] = [
|
||||
Some(codewhale_config::ProviderKind::Deepseek),
|
||||
None, // DeepseekCN
|
||||
Some(codewhale_config::ProviderKind::NvidiaNim),
|
||||
Some(codewhale_config::ProviderKind::Openai),
|
||||
Some(codewhale_config::ProviderKind::Atlascloud),
|
||||
Some(codewhale_config::ProviderKind::WanjieArk),
|
||||
Some(codewhale_config::ProviderKind::Volcengine),
|
||||
Some(codewhale_config::ProviderKind::Openrouter),
|
||||
Some(codewhale_config::ProviderKind::XiaomiMimo),
|
||||
Some(codewhale_config::ProviderKind::Novita),
|
||||
Some(codewhale_config::ProviderKind::Fireworks),
|
||||
Some(codewhale_config::ProviderKind::Siliconflow),
|
||||
Some(codewhale_config::ProviderKind::SiliconflowCN),
|
||||
Some(codewhale_config::ProviderKind::Arcee),
|
||||
Some(codewhale_config::ProviderKind::Moonshot),
|
||||
Some(codewhale_config::ProviderKind::Sglang),
|
||||
Some(codewhale_config::ProviderKind::Vllm),
|
||||
Some(codewhale_config::ProviderKind::Ollama),
|
||||
Some(codewhale_config::ProviderKind::Huggingface),
|
||||
Some(codewhale_config::ProviderKind::Together),
|
||||
Some(codewhale_config::ProviderKind::OpenaiCodex),
|
||||
Some(codewhale_config::ProviderKind::Anthropic),
|
||||
];
|
||||
|
||||
/// `ProviderKind` discriminant → `ApiProvider` lookup.
|
||||
const FROM_KIND_LOOKUP: [Self; 21] = [
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
Self::Atlascloud,
|
||||
Self::WanjieArk,
|
||||
Self::Volcengine,
|
||||
Self::Openrouter,
|
||||
Self::XiaomiMimo,
|
||||
Self::Novita,
|
||||
Self::Fireworks,
|
||||
Self::Siliconflow,
|
||||
Self::Arcee,
|
||||
Self::SiliconflowCn,
|
||||
Self::Moonshot,
|
||||
Self::Sglang,
|
||||
Self::Vllm,
|
||||
Self::Ollama,
|
||||
Self::Huggingface,
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
];
|
||||
|
||||
/// Map to the config-level `ProviderKind`.
|
||||
/// Returns `None` for the legacy `DeepseekCN` variant.
|
||||
#[must_use]
|
||||
pub fn kind(self) -> Option<codewhale_config::ProviderKind> {
|
||||
Self::KIND_LOOKUP[self as usize]
|
||||
}
|
||||
|
||||
/// Construct from a config-level `ProviderKind`.
|
||||
#[must_use]
|
||||
pub fn from_kind(kind: codewhale_config::ProviderKind) -> Self {
|
||||
Self::FROM_KIND_LOOKUP[kind as usize]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -505,8 +505,8 @@ mod tests {
|
||||
"Novita AI",
|
||||
"Fireworks AI",
|
||||
"SiliconFlow",
|
||||
"SiliconFlow (China)",
|
||||
"Arcee AI",
|
||||
"SiliconFlow (China)",
|
||||
"Moonshot/Kimi",
|
||||
"SGLang",
|
||||
"vLLM",
|
||||
|
||||
@@ -21,6 +21,7 @@ from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CONFIG_RS = ROOT / "crates" / "config" / "src" / "lib.rs"
|
||||
PROVIDER_RS = ROOT / "crates" / "config" / "src" / "provider.rs"
|
||||
TUI_CONFIG_RS = ROOT / "crates" / "tui" / "src" / "config.rs"
|
||||
AGENT_RS = ROOT / "crates" / "agent" / "src" / "lib.rs"
|
||||
PROVIDERS_MD = ROOT / "docs" / "PROVIDERS.md"
|
||||
@@ -69,35 +70,37 @@ def extract_match_block(
|
||||
|
||||
|
||||
def provider_kind_ids(config_rs: str) -> dict[str, str]:
|
||||
impl_start = require_index(
|
||||
config_rs, "impl ProviderKind", "crates/config/src/lib.rs"
|
||||
provider_rs = read(PROVIDER_RS)
|
||||
pairs = re.findall(
|
||||
r"provider!\(\s*\n\s*\w+,\s*\n\s*(\w+),\s*\n\s*\"([^\"]+)\"",
|
||||
provider_rs,
|
||||
)
|
||||
block = extract_match_block(
|
||||
config_rs,
|
||||
"pub fn as_str(self) -> &'static str",
|
||||
"crates/config/src/lib.rs",
|
||||
impl_start,
|
||||
)
|
||||
pairs = re.findall(r"Self::(\w+)\s*=>\s*\"([^\"]+)\"", block)
|
||||
if not pairs:
|
||||
raise ValueError("ProviderKind::as_str returned no providers")
|
||||
return {variant: provider_id for variant, provider_id in pairs}
|
||||
ids: dict[str, str] = {variant: provider_id for variant, provider_id in pairs}
|
||||
# OpenaiCodex and Anthropic use manual impls rather than the provider!() macro
|
||||
for variant_name, id_literal in [
|
||||
("OpenaiCodex", "openai-codex"),
|
||||
("Anthropic", "anthropic"),
|
||||
]:
|
||||
match = re.search(
|
||||
rf'impl\s+Provider\s+for\s+{variant_name}.*?fn\s+id.*?\"({id_literal})\"',
|
||||
provider_rs, re.DOTALL,
|
||||
)
|
||||
if match:
|
||||
ids[variant_name] = match.group(1)
|
||||
if not ids:
|
||||
raise ValueError("provider!() invocations returned no providers")
|
||||
return ids
|
||||
|
||||
|
||||
def api_provider_ids(tui_config_rs: str) -> dict[str, str]:
|
||||
impl_start = require_index(
|
||||
tui_config_rs, "impl ApiProvider", "crates/tui/src/config.rs"
|
||||
)
|
||||
block = extract_match_block(
|
||||
tui_config_rs,
|
||||
"pub fn as_str(self) -> &'static str",
|
||||
"crates/tui/src/config.rs",
|
||||
impl_start,
|
||||
)
|
||||
pairs = re.findall(r"Self::(\w+)\s*=>\s*\"([^\"]+)\"", block)
|
||||
if not pairs:
|
||||
raise ValueError("ApiProvider::as_str returned no providers")
|
||||
return {variant: provider_id for variant, provider_id in pairs}
|
||||
# ApiProvider ids derive from ProviderKind ids (via delegation to .kind().as_str())
|
||||
# plus the legacy "deepseek-cn" variant that exists only in ApiProvider.
|
||||
variant_to_id = provider_kind_ids("")
|
||||
# ApiProvider::SiliconflowCn maps to ProviderKind::SiliconflowCN
|
||||
if "SiliconflowCN" in variant_to_id:
|
||||
variant_to_id["SiliconflowCn"] = variant_to_id["SiliconflowCN"]
|
||||
variant_to_id["DeepseekCN"] = "deepseek-cn"
|
||||
return variant_to_id
|
||||
|
||||
|
||||
def provider_tables(config_rs: str) -> set[str]:
|
||||
@@ -253,4 +256,4 @@ def main() -> int:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user