Split SiliconflowCN into its own [providers.siliconflow_cn] TOML section instead of silently ignoring [providers.siliconflow-CN] config. - ProvidersToml / ProvidersConfig: add siliconflow_cn field with serde alias - for_provider / for_provider_mut / provider_config_for: route SiliconflowCN to the new field - resolve_runtime_options_with_secrets: fallback siliconflow_cn → siliconflow for api_key / base_url / model when unset - deepseek_api_key: add config-file fallback for SiliconflowCn - provider_config_key: update metadata to "siliconflow_cn" - save_api_key_for: write SiliconflowCn keys to providers.siliconflow_cn - docs/PROVIDERS.md, config.example.toml, scripts/check-provider-registry.py
This commit is contained in:
@@ -378,6 +378,13 @@ max_subagents = 10 # optional (1-20)
|
||||
# base_url = "https://api.siliconflow.com/v1"
|
||||
# model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
|
||||
|
||||
# SiliconFlow China-hosted DeepSeek V4 (https://siliconflow.cn)
|
||||
# Falls back to [providers.siliconflow] for api_key / base_url / model when unset.
|
||||
[providers.siliconflow-CN]
|
||||
# api_key = "YOUR_SILICONFLOW_API_KEY"
|
||||
# base_url = "https://api.siliconflow.cn/v1"
|
||||
# model = "deepseek-ai/DeepSeek-V4-Pro"
|
||||
|
||||
# Arcee AI direct OpenAI-compatible endpoint (https://docs.arcee.ai)
|
||||
[providers.arcee]
|
||||
# api_key = "YOUR_ARCEE_API_KEY"
|
||||
|
||||
@@ -296,6 +296,8 @@ pub struct ProvidersToml {
|
||||
pub fireworks: ProviderConfigToml,
|
||||
#[serde(default)]
|
||||
pub siliconflow: ProviderConfigToml,
|
||||
#[serde(default, alias = "siliconflow-CN", alias = "siliconflow-cn")]
|
||||
pub siliconflow_cn: ProviderConfigToml,
|
||||
#[serde(default)]
|
||||
pub arcee: ProviderConfigToml,
|
||||
#[serde(default)]
|
||||
@@ -361,7 +363,8 @@ impl ProvidersToml {
|
||||
ProviderKind::XiaomiMimo => &self.xiaomi_mimo,
|
||||
ProviderKind::Novita => &self.novita,
|
||||
ProviderKind::Fireworks => &self.fireworks,
|
||||
ProviderKind::Siliconflow | ProviderKind::SiliconflowCN => &self.siliconflow,
|
||||
ProviderKind::Siliconflow => &self.siliconflow,
|
||||
ProviderKind::SiliconflowCN => &self.siliconflow_cn,
|
||||
ProviderKind::Arcee => &self.arcee,
|
||||
ProviderKind::Moonshot => &self.moonshot,
|
||||
ProviderKind::Sglang => &self.sglang,
|
||||
@@ -386,7 +389,8 @@ impl ProvidersToml {
|
||||
ProviderKind::XiaomiMimo => &mut self.xiaomi_mimo,
|
||||
ProviderKind::Novita => &mut self.novita,
|
||||
ProviderKind::Fireworks => &mut self.fireworks,
|
||||
ProviderKind::Siliconflow | ProviderKind::SiliconflowCN => &mut self.siliconflow,
|
||||
ProviderKind::Siliconflow => &mut self.siliconflow,
|
||||
ProviderKind::SiliconflowCN => &mut self.siliconflow_cn,
|
||||
ProviderKind::Arcee => &mut self.arcee,
|
||||
ProviderKind::Moonshot => &mut self.moonshot,
|
||||
ProviderKind::Sglang => &mut self.sglang,
|
||||
@@ -1956,7 +1960,19 @@ impl ConfigToml {
|
||||
let env = EnvRuntimeOverrides::load();
|
||||
let provider = cli.provider.or(env.provider).unwrap_or(self.provider);
|
||||
|
||||
let provider_cfg = self.providers.for_provider(provider);
|
||||
let mut provider_cfg = self.providers.for_provider(provider).clone();
|
||||
if provider == ProviderKind::SiliconflowCN {
|
||||
let fb = &self.providers.siliconflow;
|
||||
if provider_cfg.api_key.is_none() {
|
||||
provider_cfg.api_key = fb.api_key.clone();
|
||||
}
|
||||
if provider_cfg.base_url.is_none() {
|
||||
provider_cfg.base_url = fb.base_url.clone();
|
||||
}
|
||||
if provider_cfg.model.is_none() {
|
||||
provider_cfg.model = fb.model.clone();
|
||||
}
|
||||
}
|
||||
let root_deepseek_api_key = (provider == ProviderKind::Deepseek)
|
||||
.then(|| self.api_key.clone())
|
||||
.flatten();
|
||||
@@ -5135,11 +5151,11 @@ unix_socket_path = "/tmp/cw-hooks.sock"
|
||||
provider::resolve_provider("siliconflow-cn").expect("siliconflow-cn alias resolves");
|
||||
assert_eq!(siliconflow_cn.kind(), ProviderKind::SiliconflowCN);
|
||||
assert_eq!(siliconflow_cn.id(), "siliconflow-CN");
|
||||
assert_eq!(siliconflow_cn.provider_config_key(), "siliconflow");
|
||||
assert_eq!(siliconflow_cn.provider_config_key(), "siliconflow_cn");
|
||||
|
||||
let config = ProvidersToml::default();
|
||||
let shared_table = config.for_provider(ProviderKind::SiliconflowCN);
|
||||
assert!(std::ptr::eq(
|
||||
assert!(!std::ptr::eq(
|
||||
shared_table,
|
||||
config.for_provider(ProviderKind::Siliconflow)
|
||||
));
|
||||
|
||||
@@ -223,7 +223,7 @@ provider!(
|
||||
DEFAULT_SILICONFLOW_CN_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_MODEL,
|
||||
["SILICONFLOW_API_KEY"],
|
||||
"siliconflow"
|
||||
"siliconflow_cn"
|
||||
);
|
||||
provider!(
|
||||
Arcee,
|
||||
|
||||
@@ -1979,6 +1979,8 @@ pub struct ProvidersConfig {
|
||||
pub fireworks: ProviderConfig,
|
||||
#[serde(default)]
|
||||
pub siliconflow: ProviderConfig,
|
||||
#[serde(default, alias = "siliconflow-CN", alias = "siliconflow-cn")]
|
||||
pub siliconflow_cn: ProviderConfig,
|
||||
#[serde(default)]
|
||||
pub arcee: ProviderConfig,
|
||||
#[serde(default)]
|
||||
@@ -2302,7 +2304,8 @@ impl Config {
|
||||
ApiProvider::XiaomiMimo => &providers.xiaomi_mimo,
|
||||
ApiProvider::Novita => &providers.novita,
|
||||
ApiProvider::Fireworks => &providers.fireworks,
|
||||
ApiProvider::Siliconflow | ApiProvider::SiliconflowCn => &providers.siliconflow,
|
||||
ApiProvider::Siliconflow => &providers.siliconflow,
|
||||
ApiProvider::SiliconflowCn => &providers.siliconflow_cn,
|
||||
ApiProvider::Arcee => &providers.arcee,
|
||||
ApiProvider::Moonshot => &providers.moonshot,
|
||||
ApiProvider::Sglang => &providers.sglang,
|
||||
@@ -2329,7 +2332,8 @@ impl Config {
|
||||
ApiProvider::XiaomiMimo => &mut providers.xiaomi_mimo,
|
||||
ApiProvider::Novita => &mut providers.novita,
|
||||
ApiProvider::Fireworks => &mut providers.fireworks,
|
||||
ApiProvider::Siliconflow | ApiProvider::SiliconflowCn => &mut providers.siliconflow,
|
||||
ApiProvider::Siliconflow => &mut providers.siliconflow,
|
||||
ApiProvider::SiliconflowCn => &mut providers.siliconflow_cn,
|
||||
ApiProvider::Arcee => &mut providers.arcee,
|
||||
ApiProvider::Moonshot => &mut providers.moonshot,
|
||||
ApiProvider::Sglang => &mut providers.sglang,
|
||||
@@ -3801,7 +3805,8 @@ fn apply_env_overrides(config: &mut Config) {
|
||||
ApiProvider::XiaomiMimo => &mut providers.xiaomi_mimo,
|
||||
ApiProvider::Novita => &mut providers.novita,
|
||||
ApiProvider::Fireworks => &mut providers.fireworks,
|
||||
ApiProvider::Siliconflow | ApiProvider::SiliconflowCn => &mut providers.siliconflow,
|
||||
ApiProvider::Siliconflow => &mut providers.siliconflow,
|
||||
ApiProvider::SiliconflowCn => &mut providers.siliconflow_cn,
|
||||
ApiProvider::Arcee => &mut providers.arcee,
|
||||
ApiProvider::Moonshot => &mut providers.moonshot,
|
||||
ApiProvider::Sglang => &mut providers.sglang,
|
||||
@@ -3998,7 +4003,8 @@ fn apply_env_overrides(config: &mut Config) {
|
||||
ApiProvider::XiaomiMimo => &mut providers.xiaomi_mimo,
|
||||
ApiProvider::Novita => &mut providers.novita,
|
||||
ApiProvider::Fireworks => &mut providers.fireworks,
|
||||
ApiProvider::Siliconflow | ApiProvider::SiliconflowCn => &mut providers.siliconflow,
|
||||
ApiProvider::Siliconflow => &mut providers.siliconflow,
|
||||
ApiProvider::SiliconflowCn => &mut providers.siliconflow_cn,
|
||||
ApiProvider::Arcee => &mut providers.arcee,
|
||||
ApiProvider::Moonshot => &mut providers.moonshot,
|
||||
ApiProvider::Sglang => &mut providers.sglang,
|
||||
@@ -4749,6 +4755,7 @@ fn merge_providers(
|
||||
novita: merge_provider_config(base.novita, override_cfg.novita),
|
||||
fireworks: merge_provider_config(base.fireworks, override_cfg.fireworks),
|
||||
siliconflow: merge_provider_config(base.siliconflow, override_cfg.siliconflow),
|
||||
siliconflow_cn: merge_provider_config(base.siliconflow_cn, override_cfg.siliconflow_cn),
|
||||
arcee: merge_provider_config(base.arcee, override_cfg.arcee),
|
||||
moonshot: merge_provider_config(base.moonshot, override_cfg.moonshot),
|
||||
sglang: merge_provider_config(base.sglang, override_cfg.sglang),
|
||||
@@ -5457,7 +5464,7 @@ pub fn save_api_key_for(provider: ApiProvider, api_key: &str) -> Result<PathBuf>
|
||||
ApiProvider::Novita => "novita",
|
||||
ApiProvider::Fireworks => "fireworks",
|
||||
ApiProvider::Siliconflow => "siliconflow",
|
||||
ApiProvider::SiliconflowCn => "siliconflow",
|
||||
ApiProvider::SiliconflowCn => "siliconflow_cn",
|
||||
ApiProvider::Arcee => "arcee",
|
||||
ApiProvider::Huggingface => "huggingface",
|
||||
ApiProvider::Moonshot => "moonshot",
|
||||
@@ -10473,16 +10480,18 @@ api_key = "moonshot-platform-key"
|
||||
assert_eq!(
|
||||
parsed
|
||||
.get("providers")
|
||||
.and_then(|p| p.get("siliconflow"))
|
||||
.and_then(|p| p.get("siliconflow_cn"))
|
||||
.and_then(|t| t.get("api_key"))
|
||||
.and_then(toml::Value::as_str),
|
||||
Some("sf-cn-saved-key")
|
||||
);
|
||||
assert!(
|
||||
assert_eq!(
|
||||
parsed
|
||||
.get("providers")
|
||||
.and_then(|p| p.get("siliconflow-CN"))
|
||||
.is_none()
|
||||
.and_then(|p| p.get("siliconflow"))
|
||||
.and_then(|t| t.get("api_key"))
|
||||
.and_then(toml::Value::as_str),
|
||||
Some("sf-saved-key")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ endpoint.
|
||||
| `novita` | `[providers.novita]` | `NOVITA_API_KEY` | `NOVITA_BASE_URL`; default `https://api.novita.ai/v1` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash` | OpenAI-compatible hosted route for DeepSeek model IDs. Use config or `CODEWHALE_MODEL` / `DEEPSEEK_MODEL` for model overrides. |
|
||||
| `fireworks` | `[providers.fireworks]` | `FIREWORKS_API_KEY` | `FIREWORKS_BASE_URL`; default `https://api.fireworks.ai/inference/v1` | `accounts/fireworks/models/deepseek-v4-pro` | OpenAI-compatible hosted route. Use config or `CODEWHALE_MODEL` / `DEEPSEEK_MODEL` for model overrides. |
|
||||
| `siliconflow` | `[providers.siliconflow]` | `SILICONFLOW_API_KEY` | `SILICONFLOW_BASE_URL`; default `https://api.siliconflow.com/v1` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | OpenAI-compatible hosted route. Official docs use the `.com` endpoint. `SILICONFLOW_MODEL` is accepted. Reasoning aliases `deepseek-reasoner` and `deepseek-r1` map to Pro; `deepseek-chat` and `deepseek-v3` map to Flash. |
|
||||
| `siliconflow-CN` | `[providers.siliconflow]` | `SILICONFLOW_API_KEY` | `SILICONFLOW_BASE_URL`; default `https://api.siliconflow.cn/v1` | Uses the SiliconFlow model set | China regional SiliconFlow route. This intentionally shares `[providers.siliconflow]` and `SILICONFLOW_API_KEY`; do not create `[providers.siliconflow_CN]`. Select it with `provider = "siliconflow-CN"` or `CODEWHALE_PROVIDER=siliconflow-CN`. |
|
||||
| `siliconflow-CN` | `[providers.siliconflow_cn]` | `SILICONFLOW_API_KEY` | `SILICONFLOW_BASE_URL`; default `https://api.siliconflow.cn/v1` | Uses the SiliconFlow model set | China regional SiliconFlow route. Falls back to `[providers.siliconflow]` for api_key / base_url / model when unset. Select it with `provider = "siliconflow-CN"` or `CODEWHALE_PROVIDER=siliconflow-CN`. |
|
||||
| `arcee` | `[providers.arcee]` | `ARCEE_API_KEY` | `ARCEE_BASE_URL`; default `https://api.arcee.ai/api/v1` | `trinity-large-thinking`, `trinity-large-preview` | Arcee AI direct OpenAI-compatible route, tracked as 256K-context BF16 serving. `ARCEE_MODEL` is accepted. OpenRouter's `arcee-ai/trinity-large-thinking` remains the OpenRouter namespaced model ID; direct Arcee uses the bare `trinity-large-thinking` ID. |
|
||||
| `moonshot` | `[providers.moonshot]` | `MOONSHOT_API_KEY`, `KIMI_API_KEY` | `MOONSHOT_BASE_URL`, `KIMI_BASE_URL`; default `https://api.moonshot.ai/v1` | `kimi-k2.6`; Kimi Code path uses `kimi-for-coding` at `https://api.kimi.com/coding/v1` | Moonshot/Kimi route. `MOONSHOT_MODEL`, `KIMI_MODEL_NAME`, and `KIMI_MODEL` are accepted. `[providers.moonshot] auth_mode = "kimi_oauth"` reads Kimi CLI OAuth credentials when present. |
|
||||
| `sglang` | `[providers.sglang]` | Optional `SGLANG_API_KEY` | `SGLANG_BASE_URL`; default `http://localhost:30000/v1` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | Self-hosted OpenAI-compatible route. Localhost deployments commonly omit auth. `SGLANG_MODEL` is accepted. |
|
||||
|
||||
@@ -28,7 +28,7 @@ PROVIDERS_MD = ROOT / "docs" / "PROVIDERS.md"
|
||||
|
||||
API_PROVIDER_ONLY_IDS = {"deepseek-cn"}
|
||||
SHARED_PROVIDER_TABLES = {
|
||||
"siliconflow-CN": "siliconflow",
|
||||
"siliconflow-CN": "siliconflow_cn",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user