mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-10 17:10:18 -10:00
feat(providers): add ModelScope as a built-in provider
ModelScope's OpenAI-compatible inference API (https://api-inference.modelscope.cn/v1) serves open-source Qwen, DeepSeek, Kimi, GLM and MiniMax models. Users need a ModelScope API key (MODELSCOPE_API_KEY) and a bound Alibaba Cloud account. - Modelscope variant in ProviderKind + ApiProvider, PROVIDER_REGISTRY 49->50, ALL 43->44, aliases modelscope-cn etc. - 11 static models (Qwen/DeepSeek/GLM); default Qwen/Qwen3.5-397B-A17B. - Reasoning effort joins the Arcee/Huggingface branch (xhigh/max cap to high; off is no-op); DashScope reasoning_content streaming for qwen//zhipuai/ prefixes, deliberately not replayed. - Billing Unknown (free inference API with rate limits). - Docs (EN + zh_hans), config.example.toml, facts-lib.mjs. Fixed the PROVIDERS.md canonical-list position to match ProviderKind::ALL. Tests: codewhale-config 711 passed 0 failed; modelscope scenario 1/1; cli provider helpers 1/1. clippy clean on config/agent/secrets; tui-lib clippy has 4 pre-existing errors outside this change. cargo fmt --all -- --check clean. Harvested from PR #6299 by @yrk111222 Co-authored-by: Rongkun Yan <[email protected]>
This commit is contained in:
1 parent
282127efda
commit
ba62522a20
27 files changed
+361
-46
No files matched your search
@@ -74,3 +74,4 @@ all:Pinvou
|
||||
all:SparkofSpike
|
||||
all:goransh-walia
|
||||
all:xiechimon
|
||||
all:yrk111222
|
||||
@@ -244,3 +244,4 @@ [email protected] = zhuowp <[email protected]>
|
||||
|
||||
asto18089 = asto18089 <[email protected]>
|
||||
xiechimon = Xmon Dai <[email protected]>
|
||||
yrk111222 = Rongkun Yan <[email protected]>
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
# `api_key` / `base_url` are
|
||||
# still read as DeepSeek defaults when `[providers.deepseek]` is absent
|
||||
# (backward compatibility).
|
||||
provider = "deepseek" # deepseek | deepseek-cn | deepseek-anthropic | nvidia-nim | openai | atlascloud | wanjie-ark | volcengine | openrouter | orcarouter | xiaomi-mimo | novita | fireworks | siliconflow | siliconflow-CN | arcee | moonshot | zai | stepfun | minimax | sglang | vllm | ollama | huggingface | together | qianfan | openai-codex | anthropic | openmodel | deepinfra | sakana | longcat | opencode-go | opencode-zen | meta | xai | modelstudio-token-plan | modelstudio-coding-plan | codewhale
|
||||
provider = "deepseek" # deepseek | deepseek-cn | deepseek-anthropic | nvidia-nim | openai | atlascloud | wanjie-ark | volcengine | openrouter | orcarouter | xiaomi-mimo | novita | fireworks | siliconflow | siliconflow-CN | arcee | moonshot | zai | stepfun | minimax | sglang | vllm | ollama | huggingface | modelscope | together | qianfan | openai-codex | anthropic | openmodel | deepinfra | sakana | longcat | opencode-go | opencode-zen | meta | xai | modelstudio-token-plan | modelstudio-coding-plan | codewhale
|
||||
api_key = "YOUR_DEEPSEEK_API_KEY" # must be non-empty
|
||||
base_url = "https://api.deepseek.com/beta"
|
||||
# provider = "deepseek-cn" # legacy alias (official host is still https://api.deepseek.com)
|
||||
|
||||
@@ -898,6 +898,114 @@ impl Default for ModelRegistry {
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
// ModelScope provider models
|
||||
ModelInfo {
|
||||
id: "Qwen/Qwen3.5-397B-A17B".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"qwen3.5-397b-a17b".to_string(),
|
||||
"modelscope-qwen3.5-397b-a17b".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "Qwen/Qwen3.5-122B-A10B".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"qwen3.5-122b-a10b".to_string(),
|
||||
"modelscope-qwen3.5-122b-a10b".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "Qwen/Qwen3.5-27B".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"qwen3.5-27b".to_string(),
|
||||
"modelscope-qwen3.5-27b".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "Qwen/Qwen3.5-35B-A3B".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"qwen3.5-35b-a3b".to_string(),
|
||||
"modelscope-qwen3.5-35b-a3b".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "Qwen/Qwen3.8-27B".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"qwen3.8-27b".to_string(),
|
||||
"modelscope-qwen3.8-27b".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "Qwen/Qwen3.8-Flash-Next".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"qwen3.8-flash-next".to_string(),
|
||||
"modelscope-qwen3.8-flash-next".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "deepseek-ai/DeepSeek-V4-Pro".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"deepseek-v4-pro".to_string(),
|
||||
"modelscope-deepseek-v4-pro".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "deepseek-ai/DeepSeek-V4-Pro-0813".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"deepseek-v4-pro-0813".to_string(),
|
||||
"modelscope-deepseek-v4-pro-0813".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "deepseek-ai/DeepSeek-V4.1-Flash".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"deepseek-v4.1-flash".to_string(),
|
||||
"modelscope-deepseek-v4.1-flash".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "ZhipuAI/GLM-4.7-Flash".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec![
|
||||
"glm-4.7-flash".to_string(),
|
||||
"modelscope-glm-4.7-flash".to_string(),
|
||||
],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "ZhipuAI/GLM-5.2".to_string(),
|
||||
provider: ProviderKind::Modelscope,
|
||||
aliases: vec!["glm-5.2".to_string(), "modelscope-glm-5.2".to_string()],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
// Together AI provider models
|
||||
ModelInfo {
|
||||
id: "deepseek-ai/DeepSeek-V4-Pro".to_string(),
|
||||
|
||||
@@ -10158,11 +10158,11 @@ verbosity = "project-imported"
|
||||
.map(|provider| provider.kind())
|
||||
.collect();
|
||||
// Full registry keeps legacy dialect/plan kinds; ALL is the catalog surface.
|
||||
assert_eq!(registry_kinds.len(), 49);
|
||||
assert_eq!(registry_kinds.len(), 50);
|
||||
// The tombstone stays in the registry (old config must still parse
|
||||
// and clear) and left the catalog surface when it stopped being
|
||||
// selectable.
|
||||
assert_eq!(ProviderKind::ALL.len(), 43);
|
||||
assert_eq!(ProviderKind::ALL.len(), 44);
|
||||
for kind in ProviderKind::ALL {
|
||||
assert!(
|
||||
registry_kinds.contains(&kind),
|
||||
|
||||
@@ -352,6 +352,8 @@ pub struct ProvidersToml {
|
||||
#[serde(default, skip_serializing_if = "ProviderConfigToml::is_empty")]
|
||||
pub huggingface: ProviderConfigToml,
|
||||
#[serde(default, skip_serializing_if = "ProviderConfigToml::is_empty")]
|
||||
pub modelscope: ProviderConfigToml,
|
||||
#[serde(default, skip_serializing_if = "ProviderConfigToml::is_empty")]
|
||||
pub together: ProviderConfigToml,
|
||||
#[serde(
|
||||
default,
|
||||
@@ -747,6 +749,7 @@ impl ProvidersToml {
|
||||
ProviderKind::Ollama => &self.ollama,
|
||||
ProviderKind::OllamaCloud => &self.ollama_cloud,
|
||||
ProviderKind::Huggingface => &self.huggingface,
|
||||
ProviderKind::Modelscope => &self.modelscope,
|
||||
ProviderKind::Together => &self.together,
|
||||
ProviderKind::Qianfan => &self.qianfan,
|
||||
ProviderKind::OpenaiCodex => &self.openai_codex,
|
||||
@@ -801,6 +804,7 @@ impl ProvidersToml {
|
||||
ProviderKind::Ollama => &mut self.ollama,
|
||||
ProviderKind::OllamaCloud => &mut self.ollama_cloud,
|
||||
ProviderKind::Huggingface => &mut self.huggingface,
|
||||
ProviderKind::Modelscope => &mut self.modelscope,
|
||||
ProviderKind::Together => &mut self.together,
|
||||
ProviderKind::Qianfan => &mut self.qianfan,
|
||||
ProviderKind::OpenaiCodex => &mut self.openai_codex,
|
||||
@@ -4271,6 +4275,7 @@ fn provider_passes_model_through(provider: ProviderKind) -> bool {
|
||||
| ProviderKind::Ollama
|
||||
| ProviderKind::OllamaCloud
|
||||
| ProviderKind::Huggingface
|
||||
| ProviderKind::Modelscope
|
||||
| ProviderKind::Meta
|
||||
| ProviderKind::Xai
|
||||
| ProviderKind::Telecomjs
|
||||
@@ -4791,6 +4796,7 @@ fn default_model_for_provider(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Ollama => DEFAULT_OLLAMA_MODEL,
|
||||
ProviderKind::OllamaCloud => DEFAULT_OLLAMA_CLOUD_MODEL,
|
||||
ProviderKind::Huggingface => DEFAULT_HUGGINGFACE_MODEL,
|
||||
ProviderKind::Modelscope => DEFAULT_MODELSCOPE_MODEL,
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_MODEL,
|
||||
ProviderKind::Qianfan => DEFAULT_QIANFAN_MODEL,
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_MODEL,
|
||||
@@ -4845,6 +4851,7 @@ fn default_base_url_for_provider(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Ollama => DEFAULT_OLLAMA_BASE_URL,
|
||||
ProviderKind::OllamaCloud => DEFAULT_OLLAMA_CLOUD_BASE_URL,
|
||||
ProviderKind::Huggingface => DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
ProviderKind::Modelscope => DEFAULT_MODELSCOPE_BASE_URL,
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_BASE_URL,
|
||||
ProviderKind::Qianfan => DEFAULT_QIANFAN_BASE_URL,
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL,
|
||||
@@ -7304,6 +7311,8 @@ struct EnvRuntimeOverrides {
|
||||
ollama_cloud_model: Option<String>,
|
||||
huggingface_base_url: Option<String>,
|
||||
huggingface_model: Option<String>,
|
||||
modelscope_base_url: Option<String>,
|
||||
modelscope_model: Option<String>,
|
||||
together_base_url: Option<String>,
|
||||
together_model: Option<String>,
|
||||
qianfan_base_url: Option<String>,
|
||||
@@ -7539,6 +7548,12 @@ impl EnvRuntimeOverrides {
|
||||
.or_else(|_| std::env::var("HF_MODEL"))
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
modelscope_base_url: std::env::var("MODELSCOPE_BASE_URL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
modelscope_model: std::env::var("MODELSCOPE_MODEL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
together_base_url: std::env::var("TOGETHER_BASE_URL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
@@ -7773,6 +7788,7 @@ impl EnvRuntimeOverrides {
|
||||
ProviderKind::Ollama => self.ollama_base_url.clone(),
|
||||
ProviderKind::OllamaCloud => self.ollama_cloud_base_url.clone(),
|
||||
ProviderKind::Huggingface => self.huggingface_base_url.clone(),
|
||||
ProviderKind::Modelscope => self.modelscope_base_url.clone(),
|
||||
ProviderKind::Together => self.together_base_url.clone(),
|
||||
ProviderKind::Qianfan => self.qianfan_base_url.clone(),
|
||||
ProviderKind::OpenaiCodex => self.openai_codex_base_url.clone(),
|
||||
@@ -7823,6 +7839,7 @@ impl EnvRuntimeOverrides {
|
||||
ProviderKind::Novita => self.novita_model.clone(),
|
||||
ProviderKind::Fireworks => self.fireworks_model.clone(),
|
||||
ProviderKind::Huggingface => self.huggingface_model.clone(),
|
||||
ProviderKind::Modelscope => self.modelscope_model.clone(),
|
||||
ProviderKind::Together => self.together_model.clone(),
|
||||
ProviderKind::Qianfan => self.qianfan_model.clone(),
|
||||
ProviderKind::OpenaiCodex => self.openai_codex_model.clone(),
|
||||
|
||||
@@ -17,26 +17,27 @@ use super::{
|
||||
DEFAULT_HUGGINGFACE_MODEL, DEFAULT_LONGCAT_BASE_URL, DEFAULT_LONGCAT_MODEL,
|
||||
DEFAULT_META_BASE_URL, DEFAULT_META_MODEL, DEFAULT_MINIMAX_ANTHROPIC_BASE_URL,
|
||||
DEFAULT_MINIMAX_BASE_URL, DEFAULT_MINIMAX_MODEL, DEFAULT_MISTRAL_BASE_URL,
|
||||
DEFAULT_MISTRAL_MODEL, DEFAULT_MODELSTUDIO_CODING_PLAN_BASE_URL,
|
||||
DEFAULT_MODELSTUDIO_TOKEN_PLAN_BASE_URL, DEFAULT_MODELSTUDIO_TOKEN_PLAN_MODEL,
|
||||
DEFAULT_MOONSHOT_BASE_URL, DEFAULT_MOONSHOT_MODEL, DEFAULT_NOVITA_BASE_URL,
|
||||
DEFAULT_NOVITA_MODEL, DEFAULT_NVIDIA_NIM_BASE_URL, DEFAULT_NVIDIA_NIM_MODEL,
|
||||
DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_CLOUD_BASE_URL, DEFAULT_OLLAMA_CLOUD_MODEL,
|
||||
DEFAULT_OLLAMA_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_CODEX_BASE_URL,
|
||||
DEFAULT_OPENAI_CODEX_MODEL, DEFAULT_OPENAI_MODEL, DEFAULT_OPENCODE_GO_BASE_URL,
|
||||
DEFAULT_OPENCODE_GO_MODEL, DEFAULT_OPENCODE_ZEN_BASE_URL, DEFAULT_OPENCODE_ZEN_MODEL,
|
||||
DEFAULT_OPENMODEL_BASE_URL, DEFAULT_OPENMODEL_MODEL, DEFAULT_OPENROUTER_BASE_URL,
|
||||
DEFAULT_OPENROUTER_MODEL, DEFAULT_ORCAROUTER_BASE_URL, DEFAULT_ORCAROUTER_MODEL,
|
||||
DEFAULT_QIANFAN_BASE_URL, DEFAULT_QIANFAN_MODEL, DEFAULT_SAKANA_BASE_URL, DEFAULT_SAKANA_MODEL,
|
||||
DEFAULT_SGLANG_BASE_URL, DEFAULT_SGLANG_MODEL, DEFAULT_SILICONFLOW_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_CN_BASE_URL, DEFAULT_SILICONFLOW_MODEL, DEFAULT_STEPFUN_BASE_URL,
|
||||
DEFAULT_STEPFUN_MODEL, DEFAULT_TELECOMJS_BASE_URL, DEFAULT_TELECOMJS_MODEL,
|
||||
DEFAULT_TOGETHER_BASE_URL, DEFAULT_TOGETHER_MODEL, DEFAULT_VLLM_BASE_URL, DEFAULT_VLLM_MODEL,
|
||||
DEFAULT_VOLCENGINE_BASE_URL, DEFAULT_VOLCENGINE_MODEL, DEFAULT_WANJIE_ARK_BASE_URL,
|
||||
DEFAULT_WANJIE_ARK_MODEL, DEFAULT_XAI_BASE_URL, DEFAULT_XAI_MODEL,
|
||||
DEFAULT_XIAOMI_MIMO_BASE_URL, DEFAULT_XIAOMI_MIMO_MODEL, DEFAULT_ZAI_BASE_URL,
|
||||
DEFAULT_ZAI_MODEL, MODELSTUDIO_CODING_PLAN_ANTHROPIC_BASE_URL,
|
||||
MODELSTUDIO_TOKEN_PLAN_ANTHROPIC_BASE_URL, ProviderKind,
|
||||
DEFAULT_MISTRAL_MODEL, DEFAULT_MODELSCOPE_BASE_URL, DEFAULT_MODELSCOPE_MODEL,
|
||||
DEFAULT_MODELSTUDIO_CODING_PLAN_BASE_URL, DEFAULT_MODELSTUDIO_TOKEN_PLAN_BASE_URL,
|
||||
DEFAULT_MODELSTUDIO_TOKEN_PLAN_MODEL, DEFAULT_MOONSHOT_BASE_URL, DEFAULT_MOONSHOT_MODEL,
|
||||
DEFAULT_NOVITA_BASE_URL, DEFAULT_NOVITA_MODEL, DEFAULT_NVIDIA_NIM_BASE_URL,
|
||||
DEFAULT_NVIDIA_NIM_MODEL, DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_CLOUD_BASE_URL,
|
||||
DEFAULT_OLLAMA_CLOUD_MODEL, DEFAULT_OLLAMA_MODEL, DEFAULT_OPENAI_BASE_URL,
|
||||
DEFAULT_OPENAI_CODEX_BASE_URL, DEFAULT_OPENAI_CODEX_MODEL, DEFAULT_OPENAI_MODEL,
|
||||
DEFAULT_OPENCODE_GO_BASE_URL, DEFAULT_OPENCODE_GO_MODEL, DEFAULT_OPENCODE_ZEN_BASE_URL,
|
||||
DEFAULT_OPENCODE_ZEN_MODEL, DEFAULT_OPENMODEL_BASE_URL, DEFAULT_OPENMODEL_MODEL,
|
||||
DEFAULT_OPENROUTER_BASE_URL, DEFAULT_OPENROUTER_MODEL, DEFAULT_ORCAROUTER_BASE_URL,
|
||||
DEFAULT_ORCAROUTER_MODEL, DEFAULT_QIANFAN_BASE_URL, DEFAULT_QIANFAN_MODEL,
|
||||
DEFAULT_SAKANA_BASE_URL, DEFAULT_SAKANA_MODEL, DEFAULT_SGLANG_BASE_URL, DEFAULT_SGLANG_MODEL,
|
||||
DEFAULT_SILICONFLOW_BASE_URL, DEFAULT_SILICONFLOW_CN_BASE_URL, DEFAULT_SILICONFLOW_MODEL,
|
||||
DEFAULT_STEPFUN_BASE_URL, DEFAULT_STEPFUN_MODEL, DEFAULT_TELECOMJS_BASE_URL,
|
||||
DEFAULT_TELECOMJS_MODEL, DEFAULT_TOGETHER_BASE_URL, DEFAULT_TOGETHER_MODEL,
|
||||
DEFAULT_VLLM_BASE_URL, DEFAULT_VLLM_MODEL, DEFAULT_VOLCENGINE_BASE_URL,
|
||||
DEFAULT_VOLCENGINE_MODEL, DEFAULT_WANJIE_ARK_BASE_URL, DEFAULT_WANJIE_ARK_MODEL,
|
||||
DEFAULT_XAI_BASE_URL, DEFAULT_XAI_MODEL, DEFAULT_XIAOMI_MIMO_BASE_URL,
|
||||
DEFAULT_XIAOMI_MIMO_MODEL, DEFAULT_ZAI_BASE_URL, DEFAULT_ZAI_MODEL,
|
||||
MODELSTUDIO_CODING_PLAN_ANTHROPIC_BASE_URL, MODELSTUDIO_TOKEN_PLAN_ANTHROPIC_BASE_URL,
|
||||
ProviderKind,
|
||||
};
|
||||
|
||||
/// Wire protocol spoken by a provider.
|
||||
@@ -371,6 +372,12 @@ pub const fn credential_help(kind: ProviderKind) -> CredentialHelp {
|
||||
docs_url: Some("https://huggingface.co/docs/hub/en/security-tokens"),
|
||||
guidance: "Create a scoped Hugging Face access token.",
|
||||
},
|
||||
ProviderKind::Modelscope => CredentialHelp {
|
||||
acquisition: ApiKey,
|
||||
credential_url: Some("https://modelscope.cn/my/settings/token"),
|
||||
docs_url: None,
|
||||
guidance: "Create an SDK token in ModelScope account settings.",
|
||||
},
|
||||
ProviderKind::Together => CredentialHelp {
|
||||
acquisition: ApiKey,
|
||||
credential_url: Some("https://api.together.ai/settings/api-keys"),
|
||||
@@ -1068,6 +1075,17 @@ provider!(
|
||||
"huggingface",
|
||||
aliases: ["hugging-face", "hugging_face", "hf"]
|
||||
);
|
||||
provider!(
|
||||
Modelscope,
|
||||
Modelscope,
|
||||
"modelscope",
|
||||
"ModelScope",
|
||||
DEFAULT_MODELSCOPE_BASE_URL,
|
||||
DEFAULT_MODELSCOPE_MODEL,
|
||||
["MODELSCOPE_API_KEY"],
|
||||
"modelscope",
|
||||
aliases: ["model-scope", "model_scope", "modelscope-cn", "modelscope_cn"]
|
||||
);
|
||||
provider!(
|
||||
Together,
|
||||
Together,
|
||||
@@ -1856,6 +1874,7 @@ static VLLM: Vllm = Vllm;
|
||||
static OLLAMA: Ollama = Ollama;
|
||||
static OLLAMA_CLOUD: OllamaCloud = OllamaCloud;
|
||||
static HUGGINGFACE: Huggingface = Huggingface;
|
||||
static MODELSCOPE: Modelscope = Modelscope;
|
||||
static TOGETHER: Together = Together;
|
||||
static QIANFAN: Qianfan = Qianfan;
|
||||
static OPENAI_CODEX: OpenaiCodex = OpenaiCodex;
|
||||
@@ -1886,7 +1905,7 @@ static MODELSTUDIO_CODING_PLAN_ANTHROPIC: ModelstudioCodingPlanAnthropic =
|
||||
ModelstudioCodingPlanAnthropic;
|
||||
static CUSTOM: Custom = Custom;
|
||||
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 49] = [
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 50] = [
|
||||
&DEEPSEEK,
|
||||
&DEEPSEEK_ANTHROPIC,
|
||||
&NVIDIA_NIM,
|
||||
@@ -1908,6 +1927,7 @@ static PROVIDER_REGISTRY: [&dyn Provider; 49] = [
|
||||
&OLLAMA,
|
||||
&OLLAMA_CLOUD,
|
||||
&HUGGINGFACE,
|
||||
&MODELSCOPE,
|
||||
&TOGETHER,
|
||||
&QIANFAN,
|
||||
&OPENAI_CODEX,
|
||||
|
||||
@@ -113,6 +113,8 @@ pub(crate) const DEFAULT_ARCEE_BASE_URL: &str = "https://api.arcee.ai/api/v1";
|
||||
pub(crate) const DEFAULT_HUGGINGFACE_MODEL: &str = "deepseek-ai/DeepSeek-V4-Pro";
|
||||
pub(crate) const DEFAULT_HUGGINGFACE_FLASH_MODEL: &str = "deepseek-ai/DeepSeek-V4-Flash";
|
||||
pub(crate) const DEFAULT_HUGGINGFACE_BASE_URL: &str = "https://router.huggingface.co/v1";
|
||||
pub(crate) const DEFAULT_MODELSCOPE_MODEL: &str = "Qwen/Qwen3.5-397B-A17B";
|
||||
pub(crate) const DEFAULT_MODELSCOPE_BASE_URL: &str = "https://api-inference.modelscope.cn/v1";
|
||||
pub(crate) const DEFAULT_TOGETHER_MODEL: &str = "deepseek-ai/DeepSeek-V4-Pro";
|
||||
pub(crate) const DEFAULT_TOGETHER_FLASH_MODEL: &str = "deepseek-ai/DeepSeek-V4-Flash";
|
||||
pub(crate) const DEFAULT_TOGETHER_BASE_URL: &str = "https://api.together.xyz/v1";
|
||||
|
||||
@@ -199,6 +199,14 @@ pub enum ProviderKind {
|
||||
/// [`Google`]: Self::Google
|
||||
#[serde(alias = "agy")]
|
||||
Antigravity,
|
||||
/// ModelScope — Alibaba's ModelScope inference API (OpenAI-compatible).
|
||||
#[serde(
|
||||
alias = "model-scope",
|
||||
alias = "model_scope",
|
||||
alias = "modelscope-cn",
|
||||
alias = "modelscope_cn"
|
||||
)]
|
||||
Modelscope,
|
||||
/// Google — Gemini OpenAI-compatible endpoint. Its own backend, not an
|
||||
/// OpenAI alias: thought signatures on tool calls are captured and
|
||||
/// replayed per Google's contract.
|
||||
@@ -266,7 +274,7 @@ impl ProviderKind {
|
||||
/// stay on the enum for serde and `provider_for_kind`, but they are not
|
||||
/// first-class catalog rows. Plan is `mode` / base_url; dialect is
|
||||
/// `wire = openai|anthropic` on the primary provider config.
|
||||
pub const ALL: [Self; 43] = [
|
||||
pub const ALL: [Self; 44] = [
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
@@ -305,6 +313,7 @@ impl ProviderKind {
|
||||
Self::Mistral,
|
||||
Self::Telecomjs,
|
||||
Self::ModelstudioTokenPlan,
|
||||
Self::Modelscope,
|
||||
Self::Google,
|
||||
Self::Edenai,
|
||||
Self::Concentrate,
|
||||
|
||||
@@ -19,6 +19,7 @@ meta
|
||||
minimax
|
||||
minimax-anthropic
|
||||
mistral
|
||||
modelscope
|
||||
modelstudio-coding-plan
|
||||
modelstudio-coding-plan-anthropic
|
||||
modelstudio-token-plan
|
||||
|
||||
@@ -420,6 +420,24 @@
|
||||
],
|
||||
"transport": "chat-completions"
|
||||
},
|
||||
{
|
||||
"id": "modelscope",
|
||||
"family": "modelscope",
|
||||
"label": "ModelScope",
|
||||
"endpoint": "https://api-inference.modelscope.cn/v1",
|
||||
"wire": "chat-completions",
|
||||
"defaultModel": "Qwen/Qwen3.5-397B-A17B",
|
||||
"envVars": [
|
||||
"MODELSCOPE_API_KEY"
|
||||
],
|
||||
"auth": [
|
||||
{
|
||||
"kind": "api-key",
|
||||
"label": "API key"
|
||||
}
|
||||
],
|
||||
"transport": "chat-completions"
|
||||
},
|
||||
{
|
||||
"id": "modelstudio-coding-plan",
|
||||
"family": "alibaba-modelstudio",
|
||||
|
||||
@@ -4553,6 +4553,14 @@ fn provider_kind_parses_openrouter_and_novita_aliases() {
|
||||
assert_eq!(parsed.provider, ProviderKind::Huggingface);
|
||||
}
|
||||
|
||||
for alias in ["modelscope", "modelscope-cn"] {
|
||||
assert_eq!(ProviderKind::parse(alias), Some(ProviderKind::Modelscope));
|
||||
|
||||
let parsed: ConfigToml =
|
||||
toml::from_str(&format!("provider = \"{alias}\"")).expect("modelscope alias");
|
||||
assert_eq!(parsed.provider, ProviderKind::Modelscope);
|
||||
}
|
||||
|
||||
for alias in ["deepinfra", "deep-infra", "deep_infra"] {
|
||||
assert_eq!(ProviderKind::parse(alias), Some(ProviderKind::Deepinfra));
|
||||
|
||||
@@ -5562,12 +5570,12 @@ fn meta_model_api_scopes_both_documented_key_names_to_official_endpoint() {
|
||||
fn provider_metadata_registry_covers_every_provider_kind_once() {
|
||||
let providers = provider::all_providers();
|
||||
// Full registry keeps legacy dialect/plan kinds for provider_for_kind.
|
||||
assert_eq!(providers.len(), 49);
|
||||
assert_eq!(providers.len(), 50);
|
||||
// Catalog surface is one identity per vendor (no dual-wire / plan rows),
|
||||
// and never a retired tombstone: Antigravity stays in the full registry
|
||||
// so old config parses and can be cleared, but it left `ALL` when it
|
||||
// stopped being selectable (PRD §4.4 PROD-002).
|
||||
assert_eq!(ProviderKind::ALL.len(), 43);
|
||||
assert_eq!(ProviderKind::ALL.len(), 44);
|
||||
assert!(
|
||||
!ProviderKind::ALL.contains(&ProviderKind::Antigravity),
|
||||
"a tombstone must never be offered as a selectable provider"
|
||||
|
||||
@@ -1183,6 +1183,7 @@ impl Secrets {
|
||||
/// | `siliconflow` / `siliconflow-cn` | `SILICONFLOW_API_KEY` |
|
||||
/// | `arcee` / `arcee-ai` | `ARCEE_API_KEY` |
|
||||
/// | `moonshot` / `kimi` | `MOONSHOT_API_KEY`, `KIMI_API_KEY` |
|
||||
/// | `modelscope` / `modelscope-cn` | `MODELSCOPE_API_KEY` |
|
||||
/// | `sglang` | `SGLANG_API_KEY` |
|
||||
/// | `vllm` | `VLLM_API_KEY` |
|
||||
/// | `ollama` | `OLLAMA_API_KEY` |
|
||||
@@ -1217,6 +1218,9 @@ pub fn env_for(name: &str) -> Option<String> {
|
||||
| "silicon-flow-cn" | "silicon_flow_cn" | "siliconflow-china" => &["SILICONFLOW_API_KEY"],
|
||||
"arcee" | "arcee-ai" | "arcee_ai" => &["ARCEE_API_KEY"],
|
||||
"moonshot" | "moonshot-ai" | "kimi" | "kimi-k2" => &["MOONSHOT_API_KEY", "KIMI_API_KEY"],
|
||||
"modelscope" | "model-scope" | "model_scope" | "modelscope-cn" | "modelscope_cn" => {
|
||||
&["MODELSCOPE_API_KEY"]
|
||||
}
|
||||
"sglang" | "sg-lang" => &["SGLANG_API_KEY"],
|
||||
"vllm" | "v-llm" => &["VLLM_API_KEY"],
|
||||
"ollama" | "ollama-local" => &["OLLAMA_API_KEY"],
|
||||
|
||||
@@ -4764,6 +4764,7 @@ pub(super) fn apply_reasoning_effort(
|
||||
| ApiProvider::Qianfan
|
||||
| ApiProvider::Arcee
|
||||
| ApiProvider::Huggingface
|
||||
| ApiProvider::Modelscope
|
||||
| ApiProvider::Custom => {}
|
||||
ApiProvider::Moonshot => {
|
||||
// #3024: Kimi models accept thinking enable/disable.
|
||||
@@ -4853,7 +4854,7 @@ pub(super) fn apply_reasoning_effort(
|
||||
ApiProvider::XiaomiMimo => {
|
||||
body["thinking"] = json!({ "type": "enabled" });
|
||||
}
|
||||
ApiProvider::Arcee | ApiProvider::Huggingface => {
|
||||
ApiProvider::Arcee | ApiProvider::Huggingface | ApiProvider::Modelscope => {
|
||||
let value = match normalized.as_str() {
|
||||
"minimal" => "minimal",
|
||||
"low" => "low",
|
||||
@@ -4968,7 +4969,7 @@ pub(super) fn apply_reasoning_effort(
|
||||
ApiProvider::XiaomiMimo => {
|
||||
body["thinking"] = json!({ "type": "enabled" });
|
||||
}
|
||||
ApiProvider::Arcee | ApiProvider::Huggingface => {
|
||||
ApiProvider::Arcee | ApiProvider::Huggingface | ApiProvider::Modelscope => {
|
||||
body["reasoning_effort"] = json!("high");
|
||||
}
|
||||
ApiProvider::Fireworks => {
|
||||
|
||||
@@ -3549,6 +3549,21 @@ fn is_reasoning_model_for_stream_on_route(
|
||||
return true;
|
||||
}
|
||||
|
||||
// ModelScope's OpenAI-compatible inference API streams hybrid-model
|
||||
// reasoning as `delta.reasoning_content` (the DashScope dialect) for the
|
||||
// Qwen and ZhipuAI families, whose model ids carry a `qwen/` or
|
||||
// `zhipuai/` namespace prefix. Surface those deltas as Thinking instead
|
||||
// of inlining them into the answer text. As with Model Studio above,
|
||||
// `reasoning_content` is deliberately NOT replayed back on later turns:
|
||||
// the provider is absent from `provider_accepts_reasoning_content`, and
|
||||
// the DashScope dialect does not require the reasoning field in history.
|
||||
if provider == ApiProvider::Modelscope {
|
||||
let lower = model.to_ascii_lowercase();
|
||||
if lower.starts_with("qwen/") || lower.starts_with("zhipuai/") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
provider_accepts_reasoning_content(provider) && model_supports_reasoning(model)
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ pub enum ApiProvider {
|
||||
/// Retired Antigravity identity retained only to deserialize and clear
|
||||
/// legacy Codewhale configuration. It is never selectable or runnable.
|
||||
Antigravity,
|
||||
/// ModelScope — Alibaba's ModelScope inference API (OpenAI-compatible).
|
||||
Modelscope,
|
||||
/// Jiangsu Telecom TokenHub — OpenAI-compatible AI gateway.
|
||||
Telecomjs,
|
||||
/// Eden AI — OpenAI-compatible AI gateway (aggregator).
|
||||
@@ -367,7 +369,7 @@ impl ApiProvider {
|
||||
|
||||
/// `ApiProvider` discriminant → `ProviderKind` lookup.
|
||||
/// Index 1 is `None` for the legacy `DeepseekCN` variant.
|
||||
const KIND_LOOKUP: [Option<codewhale_config::ProviderKind>; 50] = [
|
||||
const KIND_LOOKUP: [Option<codewhale_config::ProviderKind>; 51] = [
|
||||
Some(codewhale_config::ProviderKind::Deepseek),
|
||||
None, // DeepseekCN
|
||||
Some(codewhale_config::ProviderKind::DeepseekAnthropic),
|
||||
@@ -409,6 +411,7 @@ impl ApiProvider {
|
||||
Some(codewhale_config::ProviderKind::Mistral),
|
||||
Some(codewhale_config::ProviderKind::Google),
|
||||
Some(codewhale_config::ProviderKind::Antigravity),
|
||||
Some(codewhale_config::ProviderKind::Modelscope),
|
||||
Some(codewhale_config::ProviderKind::Telecomjs),
|
||||
Some(codewhale_config::ProviderKind::Edenai),
|
||||
Some(codewhale_config::ProviderKind::Concentrate),
|
||||
@@ -421,7 +424,7 @@ impl ApiProvider {
|
||||
];
|
||||
|
||||
/// `ProviderKind` discriminant → `ApiProvider` lookup.
|
||||
const FROM_KIND_LOOKUP: [Self; 49] = [
|
||||
const FROM_KIND_LOOKUP: [Self; 50] = [
|
||||
Self::Deepseek,
|
||||
Self::DeepseekAnthropic,
|
||||
Self::NvidiaNim,
|
||||
@@ -466,6 +469,7 @@ impl ApiProvider {
|
||||
Self::ModelstudioCodingPlan,
|
||||
Self::ModelstudioCodingPlanAnthropic,
|
||||
Self::Antigravity,
|
||||
Self::Modelscope,
|
||||
Self::Google,
|
||||
Self::Edenai,
|
||||
Self::Concentrate,
|
||||
@@ -1591,6 +1595,7 @@ pub fn model_completion_names_for_provider(provider: ApiProvider) -> Vec<&'stati
|
||||
ApiProvider::Huggingface => {
|
||||
vec![DEFAULT_HUGGINGFACE_MODEL, DEFAULT_HUGGINGFACE_FLASH_MODEL]
|
||||
}
|
||||
ApiProvider::Modelscope => vec![DEFAULT_MODELSCOPE_MODEL],
|
||||
ApiProvider::Deepinfra => vec![DEFAULT_DEEPINFRA_MODEL, DEFAULT_DEEPINFRA_FLASH_MODEL],
|
||||
ApiProvider::WanjieArk => {
|
||||
vec![
|
||||
@@ -3890,6 +3895,8 @@ pub struct ProvidersConfig {
|
||||
pub ollama_cloud: ProviderConfig,
|
||||
#[serde(default, alias = "hugging-face", alias = "hf")]
|
||||
pub huggingface: ProviderConfig,
|
||||
#[serde(default, alias = "model-scope", alias = "model_scope")]
|
||||
pub modelscope: ProviderConfig,
|
||||
#[serde(default, alias = "deep-infra", alias = "deep_infra")]
|
||||
pub deepinfra: ProviderConfig,
|
||||
#[serde(default, alias = "together-ai")]
|
||||
@@ -5854,6 +5861,7 @@ impl Config {
|
||||
ApiProvider::OllamaCloud => &providers.ollama_cloud,
|
||||
ApiProvider::Volcengine => &providers.volcengine,
|
||||
ApiProvider::Huggingface => &providers.huggingface,
|
||||
ApiProvider::Modelscope => &providers.modelscope,
|
||||
ApiProvider::Deepinfra => &providers.deepinfra,
|
||||
ApiProvider::Together => &providers.together,
|
||||
ApiProvider::Qianfan => &providers.qianfan,
|
||||
@@ -5955,6 +5963,7 @@ impl Config {
|
||||
ApiProvider::OllamaCloud => &mut providers.ollama_cloud,
|
||||
ApiProvider::Volcengine => &mut providers.volcengine,
|
||||
ApiProvider::Huggingface => &mut providers.huggingface,
|
||||
ApiProvider::Modelscope => &mut providers.modelscope,
|
||||
ApiProvider::Deepinfra => &mut providers.deepinfra,
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::Qianfan => &mut providers.qianfan,
|
||||
@@ -6417,6 +6426,7 @@ impl Config {
|
||||
ApiProvider::OllamaCloud => DEFAULT_OLLAMA_CLOUD_MODEL,
|
||||
ApiProvider::Volcengine => DEFAULT_VOLCENGINE_MODEL,
|
||||
ApiProvider::Huggingface => DEFAULT_HUGGINGFACE_MODEL,
|
||||
ApiProvider::Modelscope => DEFAULT_MODELSCOPE_MODEL,
|
||||
ApiProvider::Deepinfra => DEFAULT_DEEPINFRA_MODEL,
|
||||
ApiProvider::Together => DEFAULT_TOGETHER_MODEL,
|
||||
ApiProvider::Qianfan => DEFAULT_QIANFAN_MODEL,
|
||||
@@ -6556,6 +6566,7 @@ impl Config {
|
||||
| ApiProvider::OllamaCloud
|
||||
| ApiProvider::Volcengine
|
||||
| ApiProvider::Huggingface
|
||||
| ApiProvider::Modelscope
|
||||
| ApiProvider::Deepinfra
|
||||
| ApiProvider::Together
|
||||
| ApiProvider::Qianfan
|
||||
@@ -6665,6 +6676,7 @@ impl Config {
|
||||
ApiProvider::OllamaCloud => DEFAULT_OLLAMA_CLOUD_BASE_URL,
|
||||
ApiProvider::Volcengine => DEFAULT_VOLCENGINE_BASE_URL,
|
||||
ApiProvider::Huggingface => DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
ApiProvider::Modelscope => DEFAULT_MODELSCOPE_BASE_URL,
|
||||
ApiProvider::Deepinfra => DEFAULT_DEEPINFRA_BASE_URL,
|
||||
ApiProvider::Together => DEFAULT_TOGETHER_BASE_URL,
|
||||
ApiProvider::Qianfan => DEFAULT_QIANFAN_BASE_URL,
|
||||
@@ -8608,6 +8620,7 @@ fn provider_env_base_url_override(provider: ApiProvider) -> Option<String> {
|
||||
ApiProvider::Ollama => &["OLLAMA_BASE_URL"],
|
||||
ApiProvider::OllamaCloud => &["OLLAMA_CLOUD_BASE_URL"],
|
||||
ApiProvider::Huggingface => &["HUGGINGFACE_BASE_URL", "HF_BASE_URL"],
|
||||
ApiProvider::Modelscope => &["MODELSCOPE_BASE_URL"],
|
||||
ApiProvider::Meta => &["META_MODEL_API_BASE_URL", "MODEL_API_BASE_URL"],
|
||||
ApiProvider::Xai => &["XAI_BASE_URL"],
|
||||
ApiProvider::Mistral => &["MISTRAL_BASE_URL"],
|
||||
@@ -8874,6 +8887,13 @@ fn apply_env_overrides_unlocked(config: &mut Config, policy: ConfigEnvironmentPo
|
||||
.huggingface
|
||||
.base_url = Some(value);
|
||||
}
|
||||
ApiProvider::Modelscope => {
|
||||
config
|
||||
.providers
|
||||
.get_or_insert_with(ProvidersConfig::default)
|
||||
.modelscope
|
||||
.base_url = Some(value);
|
||||
}
|
||||
ApiProvider::Deepinfra => {
|
||||
config
|
||||
.providers
|
||||
@@ -9190,6 +9210,16 @@ fn apply_env_overrides_unlocked(config: &mut Config, policy: ConfigEnvironmentPo
|
||||
.huggingface
|
||||
.base_url = Some(value);
|
||||
}
|
||||
if matches!(config.api_provider(), ApiProvider::Modelscope)
|
||||
&& let Ok(value) = std::env::var("MODELSCOPE_BASE_URL")
|
||||
&& !value.trim().is_empty()
|
||||
{
|
||||
config
|
||||
.providers
|
||||
.get_or_insert_with(ProvidersConfig::default)
|
||||
.modelscope
|
||||
.base_url = Some(value);
|
||||
}
|
||||
if matches!(config.api_provider(), ApiProvider::Moonshot)
|
||||
&& let Ok(value) =
|
||||
std::env::var("MOONSHOT_BASE_URL").or_else(|_| std::env::var("KIMI_BASE_URL"))
|
||||
@@ -9366,6 +9396,7 @@ fn apply_env_overrides_unlocked(config: &mut Config, policy: ConfigEnvironmentPo
|
||||
ApiProvider::OllamaCloud => &mut providers.ollama_cloud,
|
||||
ApiProvider::Volcengine => &mut providers.volcengine,
|
||||
ApiProvider::Huggingface => &mut providers.huggingface,
|
||||
ApiProvider::Modelscope => &mut providers.modelscope,
|
||||
ApiProvider::Deepinfra => &mut providers.deepinfra,
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::Qianfan => &mut providers.qianfan,
|
||||
@@ -9589,6 +9620,17 @@ fn apply_env_overrides_unlocked(config: &mut Config, policy: ConfigEnvironmentPo
|
||||
.model = Some(value);
|
||||
config.environment_model_applied = true;
|
||||
}
|
||||
if matches!(config.api_provider(), ApiProvider::Modelscope)
|
||||
&& let Ok(value) = std::env::var("MODELSCOPE_MODEL")
|
||||
&& !value.trim().is_empty()
|
||||
{
|
||||
config
|
||||
.providers
|
||||
.get_or_insert_with(ProvidersConfig::default)
|
||||
.modelscope
|
||||
.model = Some(value);
|
||||
config.environment_model_applied = true;
|
||||
}
|
||||
if matches!(config.api_provider(), ApiProvider::Meta)
|
||||
&& let Ok(value) =
|
||||
std::env::var("META_MODEL_API_MODEL").or_else(|_| std::env::var("MODEL_API_MODEL"))
|
||||
@@ -10096,6 +10138,7 @@ pub(crate) fn provider_passes_model_through(provider: ApiProvider) -> bool {
|
||||
| ApiProvider::Ollama
|
||||
| ApiProvider::OllamaCloud
|
||||
| ApiProvider::Huggingface
|
||||
| ApiProvider::Modelscope
|
||||
| ApiProvider::Meta
|
||||
| ApiProvider::Xai
|
||||
| ApiProvider::Telecomjs
|
||||
@@ -11360,6 +11403,7 @@ fn merge_providers(
|
||||
ollama_cloud: merge_provider_config(base.ollama_cloud, override_cfg.ollama_cloud),
|
||||
volcengine: merge_provider_config(base.volcengine, override_cfg.volcengine),
|
||||
huggingface: merge_provider_config(base.huggingface, override_cfg.huggingface),
|
||||
modelscope: merge_provider_config(base.modelscope, override_cfg.modelscope),
|
||||
deepinfra: merge_provider_config(base.deepinfra, override_cfg.deepinfra),
|
||||
together: merge_provider_config(base.together, override_cfg.together),
|
||||
qianfan: merge_provider_config(base.qianfan, override_cfg.qianfan),
|
||||
@@ -12061,6 +12105,12 @@ pub fn active_provider_has_config_api_key(config: &Config) -> bool {
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if !custom_endpoint
|
||||
&& matches!(provider, ApiProvider::Modelscope)
|
||||
&& std::env::var("MODELSCOPE_API_KEY").is_ok_and(|k| !k.trim().is_empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if config.config_credentials_are_bound_to_provider_endpoint(provider)
|
||||
&& config
|
||||
@@ -12847,6 +12897,11 @@ fn provider_env_api_key(provider: ApiProvider) -> Option<String> {
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
});
|
||||
}
|
||||
if provider == ApiProvider::Modelscope {
|
||||
return std::env::var("MODELSCOPE_API_KEY")
|
||||
.ok()
|
||||
.filter(|value| !value.trim().is_empty());
|
||||
}
|
||||
|
||||
provider.env_vars().iter().find_map(|var| {
|
||||
std::env::var(var)
|
||||
|
||||
@@ -153,6 +153,8 @@ pub const DEFAULT_OLLAMA_CLOUD_BASE_URL: &str = codewhale_config::provider::OLLA
|
||||
pub const DEFAULT_HUGGINGFACE_MODEL: &str = "deepseek-ai/DeepSeek-V4-Pro";
|
||||
pub const DEFAULT_HUGGINGFACE_FLASH_MODEL: &str = "deepseek-ai/DeepSeek-V4-Flash";
|
||||
pub const DEFAULT_HUGGINGFACE_BASE_URL: &str = "https://router.huggingface.co/v1";
|
||||
pub const DEFAULT_MODELSCOPE_MODEL: &str = "Qwen/Qwen3.5-397B-A17B";
|
||||
pub const DEFAULT_MODELSCOPE_BASE_URL: &str = "https://api-inference.modelscope.cn/v1";
|
||||
pub const DEFAULT_DEEPINFRA_MODEL: &str = "deepseek-ai/DeepSeek-V4-Pro";
|
||||
pub const DEFAULT_DEEPINFRA_FLASH_MODEL: &str = "deepseek-ai/DeepSeek-V4-Flash";
|
||||
pub const DEFAULT_DEEPINFRA_BASE_URL: &str = "https://api.deepinfra.com/v1/openai";
|
||||
|
||||
@@ -12401,6 +12401,43 @@ fn huggingface_provider_scenario() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modelscope_provider_scenario() -> Result<()> {
|
||||
// from modelscope_provider_aliases_parse
|
||||
{
|
||||
for alias in ["modelscope", "modelscope-cn"] {
|
||||
assert_eq!(ApiProvider::parse(alias), Some(ApiProvider::Modelscope));
|
||||
}
|
||||
}
|
||||
// from modelscope_provider_uses_direct_defaults
|
||||
{
|
||||
let _lock = lock_test_env();
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"codewhale-tui-modelscope-defaults-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
fs::create_dir_all(&temp_root)?;
|
||||
let _guard = EnvGuard::new(&temp_root);
|
||||
|
||||
unsafe {
|
||||
env::set_var("CODEWHALE_PROVIDER", "modelscope");
|
||||
env::set_var("MODELSCOPE_API_KEY", "ms-env-key");
|
||||
}
|
||||
|
||||
let config = Config::load(None, None)?;
|
||||
assert_eq!(config.api_provider(), ApiProvider::Modelscope);
|
||||
assert_eq!(config.deepseek_api_key()?, "ms-env-key");
|
||||
assert_eq!(config.deepseek_base_url(), DEFAULT_MODELSCOPE_BASE_URL);
|
||||
assert_eq!(config.default_model(), DEFAULT_MODELSCOPE_MODEL);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn huggingface_hf_token_env_api_key_resolves() -> Result<()> {
|
||||
let _lock = lock_test_env();
|
||||
|
||||
@@ -590,6 +590,7 @@ fn provider_base_url_table_key(provider: ApiProvider) -> anyhow::Result<&'static
|
||||
ApiProvider::Siliconflow | ApiProvider::SiliconflowCn => Ok("siliconflow"),
|
||||
ApiProvider::Arcee => Ok("arcee"),
|
||||
ApiProvider::Huggingface => Ok("huggingface"),
|
||||
ApiProvider::Modelscope => Ok("modelscope"),
|
||||
ApiProvider::Deepinfra => Ok("deepinfra"),
|
||||
ApiProvider::Moonshot => Ok("moonshot"),
|
||||
ApiProvider::Sglang => Ok("sglang"),
|
||||
|
||||
@@ -441,6 +441,7 @@ fn classify(
|
||||
| ApiProvider::Arcee
|
||||
| ApiProvider::OllamaCloud
|
||||
| ApiProvider::Huggingface
|
||||
| ApiProvider::Modelscope
|
||||
| ApiProvider::Together
|
||||
| ApiProvider::Qianfan
|
||||
| ApiProvider::Openmodel
|
||||
@@ -3028,6 +3029,7 @@ mod tests {
|
||||
(ApiProvider::Ollama, BillingPresentation::Local),
|
||||
(ApiProvider::OllamaCloud, BillingPresentation::Unknown),
|
||||
(ApiProvider::Huggingface, BillingPresentation::Metered),
|
||||
(ApiProvider::Modelscope, BillingPresentation::Metered),
|
||||
(ApiProvider::Together, BillingPresentation::Metered),
|
||||
(ApiProvider::Qianfan, BillingPresentation::Metered),
|
||||
(
|
||||
@@ -3082,7 +3084,7 @@ mod tests {
|
||||
];
|
||||
|
||||
/// Default-route billing is a deliberate, audited decision for every
|
||||
/// provider `ApiProvider::all()` exposes — 49 rows covering the primary
|
||||
/// provider `ApiProvider::all()` exposes — 50 rows covering the primary
|
||||
/// route and every dialect/plan-variant alternate identity.
|
||||
#[test]
|
||||
fn default_route_billing_audit_covers_every_provider() {
|
||||
@@ -3114,7 +3116,7 @@ mod tests {
|
||||
}
|
||||
assert_eq!(
|
||||
DEFAULT_ROUTE_BILLING_AUDIT.len(),
|
||||
49,
|
||||
50,
|
||||
"the audit covers every provider identity, primary and alternate"
|
||||
);
|
||||
|
||||
|
||||
@@ -964,6 +964,7 @@ pub(crate) fn mirror_saved_api_key_in_config(
|
||||
ApiProvider::Ollama => &mut providers.ollama,
|
||||
ApiProvider::OllamaCloud => &mut providers.ollama_cloud,
|
||||
ApiProvider::Huggingface => &mut providers.huggingface,
|
||||
ApiProvider::Modelscope => &mut providers.modelscope,
|
||||
ApiProvider::Deepinfra => &mut providers.deepinfra,
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::Qianfan => &mut providers.qianfan,
|
||||
|
||||
@@ -441,7 +441,7 @@ Anthropic providers, set `provider = "<id>"` or pass
|
||||
`nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `volcengine`,
|
||||
`openrouter`, `orcarouter`, `xiaomi-mimo`, `novita`, `fireworks`,
|
||||
`siliconflow`, `arcee`, `siliconflow-CN`, `moonshot`, `sglang`, `vllm`,
|
||||
`ollama`, `ollama-cloud`, `huggingface`, `together`, `qianfan`, `openai-codex`,
|
||||
`ollama`, `ollama-cloud`, `huggingface`, `modelscope`, `together`, `qianfan`, `openai-codex`,
|
||||
`anthropic`, `openmodel`, `zai`, `stepfun`, `minimax`, `deepinfra`,
|
||||
`sakana`, `longcat`, `opencode-go`, `opencode-zen`, `meta`, `xai`,
|
||||
`mistral`, `telecomjs`, `modelstudio-token-plan`, `google`,
|
||||
@@ -1053,7 +1053,7 @@ aliases. When both forms are set the `CODEWHALE_*` value wins; the
|
||||
`DEEPSEEK_*` form is kept for older shells:
|
||||
|
||||
- `CODEWHALE_PROVIDER` (preferred) / `DEEPSEEK_PROVIDER` (legacy alias) —
|
||||
`deepseek|deepseek-anthropic|nvidia-nim|openai|atlascloud|wanjie-ark|volcengine|openrouter|xiaomi-mimo|novita|fireworks|siliconflow|arcee|siliconflow-CN|moonshot|sglang|vllm|ollama|ollama-cloud|huggingface|together|qianfan|openai-codex|anthropic|openmodel|zai|stepfun|minimax|deepinfra|mistral`
|
||||
`deepseek|deepseek-anthropic|nvidia-nim|openai|atlascloud|wanjie-ark|volcengine|openrouter|xiaomi-mimo|novita|fireworks|siliconflow|arcee|siliconflow-CN|moonshot|sglang|vllm|ollama|ollama-cloud|huggingface|modelscope|together|qianfan|openai-codex|anthropic|openmodel|zai|stepfun|minimax|deepinfra|mistral`
|
||||
- `CODEWHALE_MODEL` (preferred) / `DEEPSEEK_MODEL` (legacy alias) — default model for the active provider
|
||||
- `CODEWHALE_BASE_URL` (preferred) / `DEEPSEEK_BASE_URL` (legacy alias) — base URL for the active provider
|
||||
|
||||
@@ -1109,6 +1109,7 @@ Remaining variables:
|
||||
- `FIREWORKS_BASE_URL`
|
||||
- `FIREWORKS_MODEL`
|
||||
- `HUGGINGFACE_API_KEY` or `HF_TOKEN` (`HF_TOKEN` is a fallback alias accepted when provider is `huggingface`)
|
||||
- `MODELSCOPE_API_KEY`
|
||||
- `HUGGINGFACE_BASE_URL` or `HF_BASE_URL`
|
||||
- `HUGGINGFACE_MODEL` or `HF_MODEL`
|
||||
- `SILICONFLOW_API_KEY`
|
||||
@@ -1869,12 +1870,12 @@ reasoning contract, and all four membership ids omit generic sampling fields.
|
||||
|
||||
### Core keys (used by the TUI/engine)
|
||||
|
||||
- `provider` (string, optional): `deepseek` (default), `deepseek-anthropic`, `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `volcengine`, `openrouter`, `xiaomi-mimo`, `novita`, `fireworks`, `siliconflow`, `arcee`, `siliconflow-CN`, `moonshot`, `sglang`, `vllm`, `ollama`, `ollama-cloud`, `huggingface`, `together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`, `stepfun`, `minimax`, `deepinfra`, `sakana`, `longcat`, `opencode-go`, `meta`, `mistral`, `telecomjs`, `xai`, `orcarouter`, `modelstudio-token-plan`, `google`, `edenai`, or `custom`. Legacy `deepseek-cn` configs are still accepted as an alias for `deepseek`; DeepSeek uses the same official host [`https://api.deepseek.com`](https://api-docs.deepseek.com/) worldwide. `deepseek-anthropic` targets DeepSeek's Anthropic Messages-compatible endpoint at `https://api.deepseek.com/anthropic` using `DEEPSEEK_API_KEY`; `nvidia-nim` targets NVIDIA's NIM-hosted DeepSeek endpoints through `https://integrate.api.nvidia.com/v1`; `openai` targets a generic OpenAI-compatible endpoint, defaulting to `https://api.openai.com/v1`; `atlascloud` targets AtlasCloud's OpenAI-compatible endpoint at `https://api.atlascloud.ai/v1`; `wanjie-ark` targets Wanjie Ark's OpenAI-compatible endpoint at `https://maas-openapi.wanjiedata.com/api/v1`; `volcengine` targets Volcengine Ark's OpenAI-compatible coding endpoint at `https://ark.cn-beijing.volces.com/api/coding/v3`; `openrouter` targets `https://openrouter.ai/api/v1`; `xiaomi-mimo` targets Xiaomi MiMo's OpenAI-compatible endpoint, using `https://token-plan-sgp.xiaomimimo.com/v1` by default for Token Plan keys (`tp-...`) and `https://api.xiaomimimo.com/v1` for pay-as-you-go keys. For Token Plan accounts outside the Singapore default, set `base_url` explicitly or use `mode = "token-plan-cn"` for China and `mode = "token-plan-ams"` for Europe/Amsterdam; `novita` targets `https://api.novita.ai/openai/v1`; `fireworks` targets `https://api.fireworks.ai/inference/v1`; `siliconflow` targets SiliconFlow, defaulting to `https://api.siliconflow.com/v1`; `arcee` targets Arcee AI's OpenAI-compatible endpoint at `https://api.arcee.ai/api/v1`; `siliconflow-CN` targets the SiliconFlow China regional endpoint through `[providers.siliconflow_cn]`; `moonshot` targets Moonshot/Kimi, defaulting to `https://api.moonshot.ai/v1`; `sglang` targets a self-hosted OpenAI-compatible endpoint, defaulting to `http://localhost:30000/v1`; `vllm` targets a self-hosted vLLM OpenAI-compatible endpoint, defaulting to `http://localhost:8000/v1`; `ollama` targets Ollama's OpenAI-compatible endpoint, defaulting to `http://localhost:11434/v1`; `huggingface` targets Hugging Face Inference Providers at `https://router.huggingface.co/v1`; `together` targets Together AI at `https://api.together.xyz/v1`; `qianfan` targets Baidu Qianfan at `https://api.baiduqianfan.ai/v1`; `openai-codex` targets ChatGPT/Codex OAuth; `anthropic` targets Claude's native Messages API; `openmodel` targets OpenModel's Anthropic-compatible Messages API at `https://api.openmodel.ai`; `zai` targets Z.ai at `https://api.z.ai/api/coding/paas/v4`; `stepfun` targets StepFun at `https://api.stepfun.ai/v1`; `minimax` targets MiniMax at `https://api.minimax.io/v1`; `deepinfra` targets DeepInfra at `https://api.deepinfra.com/v1/openai`; `sakana` targets Sakana AI Fugu at `https://api.sakana.ai/v1`; `longcat` targets Meituan LongCat at `https://api.longcat.chat/openai/v1`; `opencode-go` targets the subscription-backed OpenCode Go model-aware route (Chat Completions, Responses, or Messages according to the documented model) at `https://opencode.ai/zen/go/v1`; `meta` targets Meta Model API; `mistral` targets Mistral AI's OpenAI-compatible endpoint at `https://api.mistral.ai/v1`; `telecomjs` targets TelecomJS TokenHub at `https://aigw.telecomjs.com/v1`; and `xai` targets xAI's API-key or OAuth route.
|
||||
- `provider` (string, optional): `deepseek` (default), `deepseek-anthropic`, `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `volcengine`, `openrouter`, `xiaomi-mimo`, `novita`, `fireworks`, `siliconflow`, `arcee`, `siliconflow-CN`, `moonshot`, `sglang`, `vllm`, `ollama`, `ollama-cloud`, `huggingface`, `modelscope`, `together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`, `stepfun`, `minimax`, `deepinfra`, `sakana`, `longcat`, `opencode-go`, `meta`, `mistral`, `telecomjs`, `xai`, `orcarouter`, `modelstudio-token-plan`, `google`, `edenai`, or `custom`. Legacy `deepseek-cn` configs are still accepted as an alias for `deepseek`; DeepSeek uses the same official host [`https://api.deepseek.com`](https://api-docs.deepseek.com/) worldwide. `deepseek-anthropic` targets DeepSeek's Anthropic Messages-compatible endpoint at `https://api.deepseek.com/anthropic` using `DEEPSEEK_API_KEY`; `nvidia-nim` targets NVIDIA's NIM-hosted DeepSeek endpoints through `https://integrate.api.nvidia.com/v1`; `openai` targets a generic OpenAI-compatible endpoint, defaulting to `https://api.openai.com/v1`; `atlascloud` targets AtlasCloud's OpenAI-compatible endpoint at `https://api.atlascloud.ai/v1`; `wanjie-ark` targets Wanjie Ark's OpenAI-compatible endpoint at `https://maas-openapi.wanjiedata.com/api/v1`; `volcengine` targets Volcengine Ark's OpenAI-compatible coding endpoint at `https://ark.cn-beijing.volces.com/api/coding/v3`; `openrouter` targets `https://openrouter.ai/api/v1`; `xiaomi-mimo` targets Xiaomi MiMo's OpenAI-compatible endpoint, using `https://token-plan-sgp.xiaomimimo.com/v1` by default for Token Plan keys (`tp-...`) and `https://api.xiaomimimo.com/v1` for pay-as-you-go keys. For Token Plan accounts outside the Singapore default, set `base_url` explicitly or use `mode = "token-plan-cn"` for China and `mode = "token-plan-ams"` for Europe/Amsterdam; `novita` targets `https://api.novita.ai/openai/v1`; `fireworks` targets `https://api.fireworks.ai/inference/v1`; `siliconflow` targets SiliconFlow, defaulting to `https://api.siliconflow.com/v1`; `arcee` targets Arcee AI's OpenAI-compatible endpoint at `https://api.arcee.ai/api/v1`; `siliconflow-CN` targets the SiliconFlow China regional endpoint through `[providers.siliconflow_cn]`; `moonshot` targets Moonshot/Kimi, defaulting to `https://api.moonshot.ai/v1`; `sglang` targets a self-hosted OpenAI-compatible endpoint, defaulting to `http://localhost:30000/v1`; `vllm` targets a self-hosted vLLM OpenAI-compatible endpoint, defaulting to `http://localhost:8000/v1`; `ollama` targets Ollama's OpenAI-compatible endpoint, defaulting to `http://localhost:11434/v1`; `huggingface` targets Hugging Face Inference Providers at `https://router.huggingface.co/v1`; `modelscope` targets ModelScope's OpenAI-compatible inference API at `https://api-inference.modelscope.cn/v1`; `together` targets Together AI at `https://api.together.xyz/v1`; `qianfan` targets Baidu Qianfan at `https://api.baiduqianfan.ai/v1`; `openai-codex` targets ChatGPT/Codex OAuth; `anthropic` targets Claude's native Messages API; `openmodel` targets OpenModel's Anthropic-compatible Messages API at `https://api.openmodel.ai`; `zai` targets Z.ai at `https://api.z.ai/api/coding/paas/v4`; `stepfun` targets StepFun at `https://api.stepfun.ai/v1`; `minimax` targets MiniMax at `https://api.minimax.io/v1`; `deepinfra` targets DeepInfra at `https://api.deepinfra.com/v1/openai`; `sakana` targets Sakana AI Fugu at `https://api.sakana.ai/v1`; `longcat` targets Meituan LongCat at `https://api.longcat.chat/openai/v1`; `opencode-go` targets the subscription-backed OpenCode Go model-aware route (Chat Completions, Responses, or Messages according to the documented model) at `https://opencode.ai/zen/go/v1`; `meta` targets Meta Model API; `mistral` targets Mistral AI's OpenAI-compatible endpoint at `https://api.mistral.ai/v1`; `telecomjs` targets TelecomJS TokenHub at `https://aigw.telecomjs.com/v1`; and `xai` targets xAI's API-key or OAuth route.
|
||||
- `opencode-zen` (string provider value): selects the model-aware OpenCode Zen gateway through `[providers.opencode_zen]`. The default base URL is `https://opencode.ai/zen/v1`, the default model is `gpt-5.6`, and credentials come from `api_key`, `OPENCODE_ZEN_API_KEY`, or fallback `OPENCODE_API_KEY`—never ChatGPT/Codex OAuth. `OPENCODE_ZEN_BASE_URL` and `OPENCODE_ZEN_MODEL` are accepted. The selected model is resolved through the curated Zen catalog: GPT uses Responses, Claude/Qwen use Anthropic Messages, and the documented DeepSeek/MiniMax/GLM/Kimi/Grok/free rows use Chat Completions. Gemini and unknown models fail closed because Codewhale has no proven supported wire contract for them. See the exact current model groups in [`PROVIDERS.md`](PROVIDERS.md#opencode-zen-protocol-catalog).
|
||||
- `minimax-anthropic` (string provider value): selects MiniMax's Anthropic-compatible Messages route through `[providers.minimax_anthropic]`. The default Base URL is `https://api.minimax.io/anthropic`; set `https://api.minimaxi.com/anthropic` for China. Keep the `/anthropic` suffix because Codewhale appends `/v1/messages`. The route uses `MINIMAX_API_KEY` and defaults to `MiniMax-M3`; `MiniMax-M2.7` is also registered. Official M3 input modalities are text, image, and video, with adaptive or disabled thinking. M2.7 is text-only and always keeps thinking enabled.
|
||||
- `api_key` (string, required for hosted providers): must be non-empty for DeepSeek/hosted providers (or set the provider API key env var). Self-hosted SGLang, vLLM, and local `ollama` can omit it. `ollama-cloud` requires a key saved for that provider or supplied by `OLLAMA_CLOUD_API_KEY`, then `OLLAMA_API_KEY`.
|
||||
- `auth_mode` (string, optional provider-table key): selects a provider-specific authentication contract. Kimi Code membership uses `auth_mode = "api_key"` (or omit the field), a key created in the [Kimi Code console](https://www.kimi.com/code/console), `base_url = "https://api.kimi.com/coding/v1"`, and bare `model = "k3"` for K3. Codewhale gives that route a safe 262,144-token baseline; set `context_window = 1048576` only when the Kimi Code plan includes 1M access (Allegretto and above). `k3[1m]` is a Claude Code-only convention, not an API model ID, and Codewhale rejects it instead of silently changing the wire model or assuming an entitlement. `model = "kimi-for-coding"` remains the valid K2.7 compatibility route available to all Kimi Code members. Legacy `auth_mode = "kimi_oauth"` fails closed with API-key guidance and never probes, reads, refreshes, or rewrites `kimi_cli`/`kimi_code_cli` credential files. First-class OAuth requires Codewhale's own vendor-registered client identity and remains tracked in #4417.
|
||||
- `base_url` (string, optional): defaults to `https://api.deepseek.com/beta` for DeepSeek's OpenAI-compatible Chat Completions API, including legacy `provider = "deepseek-cn"` configs. Other defaults are `https://api.deepseek.com/anthropic` for `deepseek-anthropic`, `https://integrate.api.nvidia.com/v1` for `nvidia-nim`, `https://api.openai.com/v1` for `openai`, `https://api.atlascloud.ai/v1` for `atlascloud`, `https://maas-openapi.wanjiedata.com/api/v1` for `wanjie-ark`, `https://ark.cn-beijing.volces.com/api/coding/v3` for `volcengine`, `https://openrouter.ai/api/v1` for `openrouter`, `https://token-plan-sgp.xiaomimimo.com/v1` for `xiaomi-mimo` when the API key starts with `tp-...` and `https://api.xiaomimimo.com/v1` otherwise, `https://api.novita.ai/openai/v1` for `novita`, `https://api.fireworks.ai/inference/v1` for `fireworks`, `https://api.siliconflow.com/v1` for `siliconflow`, `https://api.siliconflow.cn/v1` for `siliconflow-CN`, `https://api.arcee.ai/api/v1` for `arcee`, `https://api.moonshot.ai/v1` for `moonshot`, `https://api.minimax.io/v1` for `minimax`, `https://api.openmodel.ai` for `openmodel`, `https://api.z.ai/api/coding/paas/v4` for `zai`, `https://api.stepfun.ai/v1` for `stepfun`, `https://api.deepinfra.com/v1/openai` for `deepinfra`, `https://api.sakana.ai/v1` for `sakana`, `https://router.huggingface.co/v1` for `huggingface`, `https://api.together.xyz/v1` for `together`, `https://api.baiduqianfan.ai/v1` for `qianfan`, `https://chatgpt.com/backend-api` for `openai-codex`, `https://api.anthropic.com` for `anthropic`, `https://api.mistral.ai/v1` for `mistral`, `http://localhost:30000/v1` for `sglang`, `http://localhost:8000/v1` for `vllm`, `http://localhost:11434/v1` for `ollama`, and `https://ollama.com/v1` for `ollama-cloud`. Set `base_url = "https://token-plan-cn.xiaomimimo.com/v1"` for China-region Xiaomi MiMo Token Plan accounts or `base_url = "https://token-plan-ams.xiaomimimo.com/v1"` for Europe/Amsterdam accounts. Mistral-specific reasoning fields and polymorphic replay are enabled only on the documented first-party HTTPS `/v1` hosts; a custom Mistral base URL keeps generic Chat semantics. Set `https://api.deepseek.com` or `https://api.deepseek.com/v1` explicitly to opt out of DeepSeek beta features.
|
||||
- `base_url` (string, optional): defaults to `https://api.deepseek.com/beta` for DeepSeek's OpenAI-compatible Chat Completions API, including legacy `provider = "deepseek-cn"` configs. Other defaults are `https://api.deepseek.com/anthropic` for `deepseek-anthropic`, `https://integrate.api.nvidia.com/v1` for `nvidia-nim`, `https://api.openai.com/v1` for `openai`, `https://api.atlascloud.ai/v1` for `atlascloud`, `https://maas-openapi.wanjiedata.com/api/v1` for `wanjie-ark`, `https://ark.cn-beijing.volces.com/api/coding/v3` for `volcengine`, `https://openrouter.ai/api/v1` for `openrouter`, `https://token-plan-sgp.xiaomimimo.com/v1` for `xiaomi-mimo` when the API key starts with `tp-...` and `https://api.xiaomimimo.com/v1` otherwise, `https://api.novita.ai/openai/v1` for `novita`, `https://api.fireworks.ai/inference/v1` for `fireworks`, `https://api.siliconflow.com/v1` for `siliconflow`, `https://api.siliconflow.cn/v1` for `siliconflow-CN`, `https://api.arcee.ai/api/v1` for `arcee`, `https://api.moonshot.ai/v1` for `moonshot`, `https://api.minimax.io/v1` for `minimax`, `https://api.openmodel.ai` for `openmodel`, `https://api.z.ai/api/coding/paas/v4` for `zai`, `https://api.stepfun.ai/v1` for `stepfun`, `https://api.deepinfra.com/v1/openai` for `deepinfra`, `https://api.sakana.ai/v1` for `sakana`, `https://router.huggingface.co/v1` for `huggingface`, `https://api-inference.modelscope.cn/v1` for `modelscope`, `https://api.together.xyz/v1` for `together`, `https://api.baiduqianfan.ai/v1` for `qianfan`, `https://chatgpt.com/backend-api` for `openai-codex`, `https://api.anthropic.com` for `anthropic`, `https://api.mistral.ai/v1` for `mistral`, `http://localhost:30000/v1` for `sglang`, `http://localhost:8000/v1` for `vllm`, `http://localhost:11434/v1` for `ollama`, and `https://ollama.com/v1` for `ollama-cloud`. Set `base_url = "https://token-plan-cn.xiaomimimo.com/v1"` for China-region Xiaomi MiMo Token Plan accounts or `base_url = "https://token-plan-ams.xiaomimimo.com/v1"` for Europe/Amsterdam accounts. Mistral-specific reasoning fields and polymorphic replay are enabled only on the documented first-party HTTPS `/v1` hosts; a custom Mistral base URL keeps generic Chat semantics. Set `https://api.deepseek.com` or `https://api.deepseek.com/v1` explicitly to opt out of DeepSeek beta features.
|
||||
- `ollama-cloud` route: select `provider = "ollama-cloud"`, configure `[providers.ollama_cloud]` when overriding the default `https://ollama.com/v1` / `gpt-oss:120b` tuple, and save a key from [Ollama account settings](https://ollama.com/settings/keys) with `codewhale auth set --provider ollama-cloud`. Ambient precedence is `OLLAMA_CLOUD_API_KEY`, then `OLLAMA_API_KEY`; arbitrary Ollama model IDs pass through unchanged.
|
||||
- Legacy Ollama Cloud migration: a released `provider = "ollama"` config whose normalized `[providers.ollama].base_url` is exactly `https://ollama.com/v1` is upgraded to the `ollama-cloud` runtime identity in memory. Only that exact tuple may read its old `ollama` provider table and secret slot. The config and secrets are never rewritten, and neighboring paths, HTTP downgrades, lookalike hosts, or an explicit `ollama-cloud` selection never consume the fallback.
|
||||
- `telecomjs` base URL and catalog: `[providers.telecomjs]` defaults to `https://aigw.telecomjs.com/v1`; `TELECOMJS_BASE_URL` overrides it. With `TELECOMJS_API_KEY`, `/models` refreshes a key-scoped catalog without mixing rows into another provider.
|
||||
|
||||
+6
-2
@@ -119,7 +119,7 @@ Codewhale observation is retained. The separately attributed Codex-owned
|
||||
native cache keeps its existing freshness policy when no login-file version
|
||||
can be observed; this is not proof of account identity in an external keyring.
|
||||
|
||||
The canonical provider IDs are the 43 entries of `ProviderKind::ALL`
|
||||
The canonical provider IDs are the 44 entries of `ProviderKind::ALL`
|
||||
(`crates/config/src/provider_kind.rs`), in that order:
|
||||
|
||||
`deepseek`, `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `volcengine`,
|
||||
@@ -127,7 +127,7 @@ The canonical provider IDs are the 43 entries of `ProviderKind::ALL`
|
||||
`siliconflow-CN`, `moonshot`, `sglang`, `vllm`, `ollama`, `ollama-cloud`, `huggingface`,
|
||||
`together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`,
|
||||
`stepfun`, `minimax`, `deepinfra`, `sakana`, `longcat`, `opencode-go`,
|
||||
`opencode-zen`, `meta`, `xai`, `mistral`, `telecomjs`, `modelstudio-token-plan`,
|
||||
`opencode-zen`, `meta`, `xai`, `mistral`, `telecomjs`, `modelstudio-token-plan`, `modelscope`,
|
||||
`google`, `edenai`, `concentrate`, `codewhale`, and `custom`.
|
||||
|
||||
`deepseek-anthropic` is *not* on this list — it is a wire dialect of
|
||||
@@ -213,6 +213,7 @@ the listed provider env vars.
|
||||
| `ollama` | `[providers.ollama]` | Local OpenAI-compatible Chat Completions | `OLLAMA_API_KEY` (optional; only for an authenticated local route) |
|
||||
| `ollama-cloud` | `[providers.ollama_cloud]` | Hosted OpenAI-compatible Chat Completions | `OLLAMA_CLOUD_API_KEY`, `OLLAMA_API_KEY` |
|
||||
| `huggingface` | `[providers.huggingface]` | OpenAI Chat Completions | `HUGGINGFACE_API_KEY`, `HF_TOKEN` |
|
||||
| `modelscope` | `[providers.modelscope]` | OpenAI Chat Completions | `MODELSCOPE_API_KEY` |
|
||||
| `together` | `[providers.together]` | OpenAI Chat Completions | `TOGETHER_API_KEY` |
|
||||
| `qianfan` | `[providers.qianfan]` | OpenAI Chat Completions | `QIANFAN_API_KEY`, `BAIDU_QIANFAN_API_KEY` |
|
||||
| `openai-codex` | `[providers.openai_codex]` | OpenAI Responses | Native ChatGPT PKCE (`codewhale auth chatgpt`), `OPENAI_CODEX_ACCESS_TOKEN`, `CODEX_ACCESS_TOKEN`, or explicit Codex CLI consent |
|
||||
@@ -572,6 +573,7 @@ configuration path instead of guessing a vendor page.
|
||||
| `stepfun` | [StepFun Open Platform](https://platform.stepfun.ai/) |
|
||||
| `minimax`, `minimax-anthropic` | [MiniMax interface keys](https://platform.minimax.io/user-center/basic-information/interface-key) |
|
||||
| `huggingface` | [Hugging Face tokens](https://huggingface.co/settings/tokens) |
|
||||
| `modelscope` | [ModelScope API Keys](https://modelscope.cn/my/settings/token) |
|
||||
| `deepinfra` | [DeepInfra API keys](https://deepinfra.com/dash/api_keys) |
|
||||
| `together` | [Together API keys](https://api.together.ai/settings/api-keys) |
|
||||
| `qianfan` | [Baidu Cloud access keys](https://console.bce.baidu.com/iam/#/iam/accesslist) |
|
||||
@@ -681,6 +683,7 @@ overlay and lets DSH resolve its own keys.
|
||||
| `ollama` | `[providers.ollama]` | Local optional `OLLAMA_API_KEY` | `OLLAMA_BASE_URL`; default `http://localhost:11434/v1` | live tag from the local catalog; pre-refresh placeholder `unknown`; provider-hinted custom tags pass through | Local Ollama is keyless by default. `OLLAMA_MODEL` is accepted. The header must not paint a hosted id the local daemon did not list. |
|
||||
| `ollama-cloud` | `[providers.ollama_cloud]` | `OLLAMA_CLOUD_API_KEY`, then `OLLAMA_API_KEY` | `OLLAMA_CLOUD_BASE_URL`; default `https://ollama.com/v1` | `gpt-oss:120b`; arbitrary provider-owned IDs pass through | Hosted OpenAI-compatible `/v1/chat/completions` route. Save credentials under `ollama-cloud`; the exact released `ollama` + Cloud URL tuple has bounded read-only in-memory compatibility with its legacy table and secret slot. `OLLAMA_CLOUD_MODEL` is accepted. |
|
||||
| `huggingface` | `[providers.huggingface]` | `HUGGINGFACE_API_KEY`, `HF_TOKEN` | `HUGGINGFACE_BASE_URL`, `HF_BASE_URL`; default `https://router.huggingface.co/v1` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | Hugging Face Inference Providers OpenAI-compatible router route. Accepted aliases: `huggingface`, `hugging-face`, `hugging_face`, `hf`. Org-prefixed model IDs pass through. `HUGGINGFACE_MODEL` and `HF_MODEL` are accepted. Hub browsing/export are separate future features. |
|
||||
| `modelscope` | `[providers.modelscope]` | `MODELSCOPE_API_KEY` | `MODELSCOPE_BASE_URL`; default `https://api-inference.modelscope.cn/v1` | `Qwen/Qwen3.5-397B-A17B` (default), `Qwen/Qwen3.5-122B-A10B`, `Qwen/Qwen3.5-27B`, `Qwen/Qwen3.5-35B-A3B`, `Qwen/Qwen3.8-27B`, `Qwen/Qwen3.8-Flash-Next`, `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Pro-0813`, `deepseek-ai/DeepSeek-V4.1-Flash`, `ZhipuAI/GLM-4.7-Flash`, `ZhipuAI/GLM-5.2` | ModelScope OpenAI-compatible inference route. Org-prefixed model IDs pass through. `MODELSCOPE_MODEL` is accepted. |
|
||||
| `deepinfra` | `[providers.deepinfra]` | `DEEPINFRA_API_KEY`, `DEEPINFRA_TOKEN` | `DEEPINFRA_BASE_URL`; default `https://api.deepinfra.com/v1/openai` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | DeepInfra OpenAI-compatible route. Drop-in replacement for OpenAI SDK. |
|
||||
| `together` | `[providers.together]` | `TOGETHER_API_KEY` | `TOGETHER_BASE_URL`; default `https://api.together.xyz/v1` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash`, `thinkingmachines/inkling` | Together AI OpenAI-compatible route. `TOGETHER_MODEL` is accepted. Model aliases `deepseek-v4-pro` and `deepseek-v4-flash` normalize to Together's org-prefixed IDs; `inkling` and `together-inkling` normalize to Together's published lowercase Inkling wire ID. Inkling uses the exact `none`/`minimal`/`low`/`medium`/`high`/`max` reasoning vocabulary from Thinking Machines' [official model repository](https://huggingface.co/thinkingmachines/Inkling). Together's [launch post](https://www.together.ai/blog/together-ai-brings-thinking-machines-labs-new-model-inkling-on-day-0) currently says Inkling is live with 1M context, while its [model detail page](https://www.together.ai/models/inkling) says coming soon with 256K context and publishes no price. Until Together's active `/models` endpoint and the Models.dev catalog resolve that conflict, Inkling is not seeded into Codewhale's offline picker and no route-specific context or cost is inferred. |
|
||||
| `qianfan` | `[providers.qianfan]` | `QIANFAN_API_KEY`, `BAIDU_QIANFAN_API_KEY` | `QIANFAN_BASE_URL`, `BAIDU_QIANFAN_BASE_URL`; default `https://api.baiduqianfan.ai/v1` | `ernie-4.0-turbo-8k`; provider-scoped custom Qianfan service/model IDs pass through | Baidu Qianfan OpenAI-compatible route. Requests use Bearer auth and Chat Completions payloads. `QIANFAN_MODEL` and `BAIDU_QIANFAN_MODEL` are accepted; aliases `baidu-qianfan`, `baidu_qianfan`, and `baidu` resolve to this provider. Tool/function calling is model-scoped in Qianfan docs, so Codewhale preserves the selected wire model and leaves live capability proof to follow-up route/capability work. |
|
||||
@@ -918,6 +921,7 @@ endpoint when the endpoint supports model listing.
|
||||
| `ollama` | live local tag; custom tags pass through when provider hint is `ollama` | yes | no |
|
||||
| `ollama-cloud` | `gpt-oss:120b`; arbitrary provider-owned model IDs pass through | yes | yes |
|
||||
| `huggingface` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | yes | no |
|
||||
| `modelscope` | `Qwen/Qwen3.5-397B-A17B`, `Qwen/Qwen3.5-122B-A10B`, `Qwen/Qwen3.5-27B`, `Qwen/Qwen3.5-35B-A3B`, `Qwen/Qwen3.8-27B`, `Qwen/Qwen3.8-Flash-Next`, `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Pro-0813`, `deepseek-ai/DeepSeek-V4.1-Flash`, `ZhipuAI/GLM-4.7-Flash`, `ZhipuAI/GLM-5.2` | yes | no |
|
||||
| `deepinfra` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | yes | yes |
|
||||
| `together` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash`, `thinkingmachines/inkling` | yes | yes |
|
||||
| `openai-codex` | `gpt-5.5` | yes | yes |
|
||||
|
||||
@@ -202,7 +202,7 @@ allow_shell = true
|
||||
|
||||
运行 `codewhale auth status` 检查活动 provider 的配置文件、OS 钥匙串后端、环境变量、胜出来源和末四位标签,而不打印 key 本身。该命令只探测活动 provider 的钥匙串条目。
|
||||
|
||||
对于托管、通用 OpenAI 兼容、自托管、OpenAI Responses 或原生 Anthropic provider,设置 `provider = "<id>"` 或传 `codewhale --provider <id>`。规范的 provider ID 是 `deepseek`、`nvidia-nim`、`openai`、`atlascloud`、`wanjie-ark`、`volcengine`、`openrouter`、`orcarouter`、`xiaomi-mimo`、`novita`、`fireworks`、`siliconflow`、`arcee`、`siliconflow-CN`、`moonshot`、`sglang`、`vllm`、`ollama`、`ollama-cloud`、`huggingface`、`together`、`qianfan`、`openai-codex`、`anthropic`、`openmodel`、`zai`、`stepfun`、`minimax`、`deepinfra`、`sakana`、`longcat`、`opencode-go`、`opencode-zen`、`meta`、`xai`、`mistral`、`telecomjs`、`modelstudio-token-plan`、`google`、`antigravity`、`edenai` 和 `custom`(通过 `[providers.<name>]` 定义的用户自定义 OpenAI 兼容端点)。逐 provider 的 registry,包括线协议、认证变量、默认 base URL、模型 ID 和能力元数据,见 [PROVIDERS.md](PROVIDERS.md)。facade 把 provider 凭据保存到共享用户配置,并把解析后的 key、base URL、provider 和模型转发给 TUI 进程。使用 `codewhale auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY"` 或 `codewhale auth set --provider openai --api-key "YOUR_OPENAI_COMPATIBLE_API_KEY"` 或 `codewhale auth set --provider atlascloud --api-key "YOUR_ATLASCLOUD_API_KEY"` 或 `codewhale auth set --provider wanjie-ark --api-key "YOUR_WANJIE_API_KEY"` 或 `codewhale auth set --provider xiaomi-mimo --api-key "YOUR_XIAOMI_KEY"` 或 `codewhale auth set --provider fireworks --api-key "YOUR_FIREWORKS_API_KEY"` 或 `codewhale auth set --provider siliconflow --api-key "YOUR_SILICONFLOW_API_KEY"` 或 `codewhale auth set --provider arcee --api-key "YOUR_ARCEE_API_KEY"` 或 [PROVIDERS.md](PROVIDERS.md) 中匹配的 provider ID,通过 facade 保存 provider key。通用 `openai` provider 默认 `https://api.openai.com/v1`,接受 `OPENAI_BASE_URL`,默认 `gpt-5.6`。自定义 OpenAI 兼容网关仍可显式选择自己的模型。`atlascloud` 默认 `https://api.atlascloud.ai/v1`,接受 `ATLASCLOUD_BASE_URL`,默认模型是 `deepseek-ai/deepseek-v4-flash`。`wanjie-ark` 指向 Wanjie Ark 的 OpenAI 兼容端点 `https://maas-openapi.wanjiedata.com/api/v1`,默认 `deepseek-reasoner`,并原样透传模型 ID,因为 Wanjie 模型访问按账号作用域。SGLang、vLLM 和 Ollama 是自托管的,默认可以不用 API key 运行。Ollama 默认 `http://localhost:11434/v1`,并原样发送 `codewhale-coder:1.3b` 或 `qwen2.5-coder:7b` 这样的模型标签。自托管 provider 和 loopback 自定义 URL(`localhost`、`127.0.0.1`、`[::1]`、`0.0.0.0`)不读取 secret store,除非显式请求 API key 认证;当本地服务器确实需要 bearer 认证时,用环境变量或配置文件 key。Ollama Cloud 是独立的托管 `ollama-cloud` provider。它默认 `https://ollama.com/v1` 和 `gpt-oss:120b`;用 `codewhale auth set --provider ollama-cloud` 保存它的 key。环境认证先读 `OLLAMA_CLOUD_API_KEY`,然后是 Ollama 官方的 `OLLAMA_API_KEY`。SiliconFlow 默认 `https://api.siliconflow.com/v1`,接受 `SILICONFLOW_BASE_URL`,默认用 `deepseek-ai/DeepSeek-V4-Pro`。`provider = "siliconflow-CN"` 选择中国区域默认 `https://api.siliconflow.cn/v1`,配 `[providers.siliconflow_cn]` 表和 `SILICONFLOW_API_KEY` 凭据槽位。Arcee AI 默认 `https://api.arcee.ai/api/v1`,接受 `ARCEE_BASE_URL`,对 Codewhale 智能体工作默认用 `trinity-large-thinking`。`trinity-large-preview` 也作为直接的 Arcee API 模型列出;OpenRouter 的 `arcee-ai/trinity-large-thinking` 仍是 OpenRouter 命名空间形式,而直接 Arcee provider 用裸的 `trinity-large-thinking` ID。直接的 Arcee 大模型 API 调用按 256K 上下文 BF16 服务跟踪;Thinking 具备推理能力,而 Preview 未标记为推理模型。
|
||||
对于托管、通用 OpenAI 兼容、自托管、OpenAI Responses 或原生 Anthropic provider,设置 `provider = "<id>"` 或传 `codewhale --provider <id>`。规范的 provider ID 是 `deepseek`、`nvidia-nim`、`openai`、`atlascloud`、`wanjie-ark`、`volcengine`、`openrouter`、`orcarouter`、`xiaomi-mimo`、`novita`、`fireworks`、`siliconflow`、`arcee`、`siliconflow-CN`、`moonshot`、`sglang`、`vllm`、`ollama`、`ollama-cloud`、`huggingface`、`modelscope`、`together`、`qianfan`、`openai-codex`、`anthropic`、`openmodel`、`zai`、`stepfun`、`minimax`、`deepinfra`、`sakana`、`longcat`、`opencode-go`、`opencode-zen`、`meta`、`xai`、`mistral`、`telecomjs`、`modelstudio-token-plan`、`google`、`antigravity`、`edenai` 和 `custom`(通过 `[providers.<name>]` 定义的用户自定义 OpenAI 兼容端点)。逐 provider 的 registry,包括线协议、认证变量、默认 base URL、模型 ID 和能力元数据,见 [PROVIDERS.md](PROVIDERS.md)。facade 把 provider 凭据保存到共享用户配置,并把解析后的 key、base URL、provider 和模型转发给 TUI 进程。使用 `codewhale auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY"` 或 `codewhale auth set --provider openai --api-key "YOUR_OPENAI_COMPATIBLE_API_KEY"` 或 `codewhale auth set --provider atlascloud --api-key "YOUR_ATLASCLOUD_API_KEY"` 或 `codewhale auth set --provider wanjie-ark --api-key "YOUR_WANJIE_API_KEY"` 或 `codewhale auth set --provider xiaomi-mimo --api-key "YOUR_XIAOMI_KEY"` 或 `codewhale auth set --provider fireworks --api-key "YOUR_FIREWORKS_API_KEY"` 或 `codewhale auth set --provider siliconflow --api-key "YOUR_SILICONFLOW_API_KEY"` 或 `codewhale auth set --provider arcee --api-key "YOUR_ARCEE_API_KEY"` 或 [PROVIDERS.md](PROVIDERS.md) 中匹配的 provider ID,通过 facade 保存 provider key。通用 `openai` provider 默认 `https://api.openai.com/v1`,接受 `OPENAI_BASE_URL`,默认 `gpt-5.6`。自定义 OpenAI 兼容网关仍可显式选择自己的模型。`atlascloud` 默认 `https://api.atlascloud.ai/v1`,接受 `ATLASCLOUD_BASE_URL`,默认模型是 `deepseek-ai/deepseek-v4-flash`。`wanjie-ark` 指向 Wanjie Ark 的 OpenAI 兼容端点 `https://maas-openapi.wanjiedata.com/api/v1`,默认 `deepseek-reasoner`,并原样透传模型 ID,因为 Wanjie 模型访问按账号作用域。SGLang、vLLM 和 Ollama 是自托管的,默认可以不用 API key 运行。Ollama 默认 `http://localhost:11434/v1`,并原样发送 `codewhale-coder:1.3b` 或 `qwen2.5-coder:7b` 这样的模型标签。自托管 provider 和 loopback 自定义 URL(`localhost`、`127.0.0.1`、`[::1]`、`0.0.0.0`)不读取 secret store,除非显式请求 API key 认证;当本地服务器确实需要 bearer 认证时,用环境变量或配置文件 key。Ollama Cloud 是独立的托管 `ollama-cloud` provider。它默认 `https://ollama.com/v1` 和 `gpt-oss:120b`;用 `codewhale auth set --provider ollama-cloud` 保存它的 key。环境认证先读 `OLLAMA_CLOUD_API_KEY`,然后是 Ollama 官方的 `OLLAMA_API_KEY`。SiliconFlow 默认 `https://api.siliconflow.com/v1`,接受 `SILICONFLOW_BASE_URL`,默认用 `deepseek-ai/DeepSeek-V4-Pro`。`provider = "siliconflow-CN"` 选择中国区域默认 `https://api.siliconflow.cn/v1`,配 `[providers.siliconflow_cn]` 表和 `SILICONFLOW_API_KEY` 凭据槽位。Arcee AI 默认 `https://api.arcee.ai/api/v1`,接受 `ARCEE_BASE_URL`,对 Codewhale 智能体工作默认用 `trinity-large-thinking`。`trinity-large-preview` 也作为直接的 Arcee API 模型列出;OpenRouter 的 `arcee-ai/trinity-large-thinking` 仍是 OpenRouter 命名空间形式,而直接 Arcee provider 用裸的 `trinity-large-thinking` ID。直接的 Arcee 大模型 API 调用按 256K 上下文 BF16 服务跟踪;Thinking 具备推理能力,而 Preview 未标记为推理模型。
|
||||
|
||||
### 自定义 OpenAI 兼容网关
|
||||
|
||||
@@ -538,7 +538,7 @@ harness profile 里未知的姿态名或未知键会让配置反序列化失败
|
||||
|
||||
三个面向用户的槽位——provider、模型、base URL——暴露 `CODEWHALE_*` 别名。两种形式都设置时 `CODEWHALE_*` 值胜出;`DEEPSEEK_*` 形式为更老的 shell 保留:
|
||||
|
||||
- `CODEWHALE_PROVIDER`(首选)/ `DEEPSEEK_PROVIDER`(旧别名)——`deepseek|deepseek-anthropic|nvidia-nim|openai|atlascloud|wanjie-ark|volcengine|openrouter|xiaomi-mimo|novita|fireworks|siliconflow|arcee|siliconflow-CN|moonshot|sglang|vllm|ollama|ollama-cloud|huggingface|together|qianfan|openai-codex|anthropic|openmodel|zai|stepfun|minimax|deepinfra|mistral`
|
||||
- `CODEWHALE_PROVIDER`(首选)/ `DEEPSEEK_PROVIDER`(旧别名)——`deepseek|deepseek-anthropic|nvidia-nim|openai|atlascloud|wanjie-ark|volcengine|openrouter|xiaomi-mimo|novita|fireworks|siliconflow|arcee|siliconflow-CN|moonshot|sglang|vllm|ollama|ollama-cloud|huggingface|modelscope|together|qianfan|openai-codex|anthropic|openmodel|zai|stepfun|minimax|deepinfra|mistral`
|
||||
- `CODEWHALE_MODEL`(首选)/ `DEEPSEEK_MODEL`(旧别名)——活动 provider 的默认模型
|
||||
- `CODEWHALE_BASE_URL`(首选)/ `DEEPSEEK_BASE_URL`(旧别名)——活动 provider 的 base URL
|
||||
|
||||
@@ -582,6 +582,7 @@ harness profile 里未知的姿态名或未知键会让配置反序列化失败
|
||||
- `FIREWORKS_BASE_URL`
|
||||
- `FIREWORKS_MODEL`
|
||||
- `HUGGINGFACE_API_KEY` 或 `HF_TOKEN`(`HF_TOKEN` 是 provider 为 `huggingface` 时接受的回退别名)
|
||||
- `MODELSCOPE_API_KEY`
|
||||
- `HUGGINGFACE_BASE_URL` 或 `HF_BASE_URL`
|
||||
- `HUGGINGFACE_MODEL` 或 `HF_MODEL`
|
||||
- `SILICONFLOW_API_KEY`
|
||||
@@ -1008,12 +1009,12 @@ DeepSeek V4 前缀缓存让 token 标签变得重要。这些数量保持分离
|
||||
|
||||
### 核心键(供 TUI/引擎使用)
|
||||
|
||||
- `provider`(字符串,可选):`deepseek`(默认)、`deepseek-anthropic`、`nvidia-nim`、`openai`、`atlascloud`、`wanjie-ark`、`volcengine`、`openrouter`、`xiaomi-mimo`、`novita`、`fireworks`、`siliconflow`、`arcee`、`siliconflow-CN`、`moonshot`、`sglang`、`vllm`、`ollama`、`ollama-cloud`、`huggingface`、`together`、`qianfan`、`openai-codex`、`anthropic`、`openmodel`、`zai`、`stepfun`、`minimax`、`deepinfra`、`sakana`、`longcat`、`opencode-go`、`meta`、`mistral`、`telecomjs`、`xai`、`orcarouter`、`modelstudio-token-plan`、`google`、`antigravity`、`edenai` 或 `custom`。旧 `deepseek-cn` 配置仍作为 `deepseek` 的别名被接受;DeepSeek 在全球使用同一个官方主机 [`https://api.deepseek.com`](https://api-docs.deepseek.com/)。`deepseek-anthropic` 使用 `DEEPSEEK_API_KEY` 指向 DeepSeek 的 Anthropic Messages 兼容端点 `https://api.deepseek.com/anthropic`;`nvidia-nim` 通过 `https://integrate.api.nvidia.com/v1` 指向 NVIDIA NIM 托管的 DeepSeek 端点;`openai` 指向通用 OpenAI 兼容端点,默认 `https://api.openai.com/v1`;`atlascloud` 指向 AtlasCloud 的 OpenAI 兼容端点 `https://api.atlascloud.ai/v1`;`wanjie-ark` 指向 Wanjie Ark 的 OpenAI 兼容端点 `https://maas-openapi.wanjiedata.com/api/v1`;`volcengine` 指向火山方舟(Volcengine Ark)的 OpenAI 兼容编码端点 `https://ark.cn-beijing.volces.com/api/coding/v3`;`openrouter` 指向 `https://openrouter.ai/api/v1`;`xiaomi-mimo` 指向小米 MiMo 的 OpenAI 兼容端点,Token Plan key(`tp-...`)默认用 `https://token-plan-sgp.xiaomimimo.com/v1`,按量付费 key 用 `https://api.xiaomimimo.com/v1`。对于新加坡默认之外的 Token Plan 账号,显式设置 `base_url` 或对中国的账号用 `mode = "token-plan-cn"`,对欧洲/阿姆斯特丹用 `mode = "token-plan-ams"`;`novita` 指向 `https://api.novita.ai/openai/v1`;`fireworks` 指向 `https://api.fireworks.ai/inference/v1`;`siliconflow` 指向 SiliconFlow,默认 `https://api.siliconflow.com/v1`;`arcee` 指向 Arcee AI 的 OpenAI 兼容端点 `https://api.arcee.ai/api/v1`;`siliconflow-CN` 通过 `[providers.siliconflow_cn]` 指向 SiliconFlow 中国区域端点;`moonshot` 指向 Moonshot/Kimi,默认 `https://api.moonshot.ai/v1`;`sglang` 指向自托管 OpenAI 兼容端点,默认 `http://localhost:30000/v1`;`vllm` 指向自托管 vLLM OpenAI 兼容端点,默认 `http://localhost:8000/v1`;`ollama` 指向 Ollama 的 OpenAI 兼容端点,默认 `http://localhost:11434/v1`;`huggingface` 指向 Hugging Face Inference Providers `https://router.huggingface.co/v1`;`together` 指向 Together AI `https://api.together.xyz/v1`;`qianfan` 指向百度千帆 `https://api.baiduqianfan.ai/v1`;`openai-codex` 指向 ChatGPT/Codex OAuth;`anthropic` 指向 Claude 的原生 Messages API;`openmodel` 指向 OpenModel 的 Anthropic 兼容 Messages API `https://api.openmodel.ai`;`zai` 指向 Z.ai `https://api.z.ai/api/coding/paas/v4`;`stepfun` 指向 StepFun `https://api.stepfun.ai/v1`;`minimax` 指向 MiniMax `https://api.minimax.io/v1`;`deepinfra` 指向 DeepInfra `https://api.deepinfra.com/v1/openai`;`sakana` 指向 Sakana AI Fugu `https://api.sakana.ai/v1`;`longcat` 指向美团 LongCat `https://api.longcat.chat/openai/v1`;`opencode-go` 指向订阅支撑的 OpenCode Go Chat Completions 路由 `https://opencode.ai/zen/go/v1`;`meta` 指向 Meta Model API;`mistral` 指向 Mistral AI 的 OpenAI 兼容端点 `https://api.mistral.ai/v1`;`telecomjs` 指向 TelecomJS TokenHub `https://aigw.telecomjs.com/v1`;`xai` 指向 xAI 的 API key 或 OAuth 路由。
|
||||
- `provider`(字符串,可选):`deepseek`(默认)、`deepseek-anthropic`、`nvidia-nim`、`openai`、`atlascloud`、`wanjie-ark`、`volcengine`、`openrouter`、`xiaomi-mimo`、`novita`、`fireworks`、`siliconflow`、`arcee`、`siliconflow-CN`、`moonshot`、`sglang`、`vllm`、`ollama`、`ollama-cloud`、`huggingface`、`modelscope`、`together`、`qianfan`、`openai-codex`、`anthropic`、`openmodel`、`zai`、`stepfun`、`minimax`、`deepinfra`、`sakana`、`longcat`、`opencode-go`、`meta`、`mistral`、`telecomjs`、`xai`、`orcarouter`、`modelstudio-token-plan`、`google`、`antigravity`、`edenai` 或 `custom`。旧 `deepseek-cn` 配置仍作为 `deepseek` 的别名被接受;DeepSeek 在全球使用同一个官方主机 [`https://api.deepseek.com`](https://api-docs.deepseek.com/)。`deepseek-anthropic` 使用 `DEEPSEEK_API_KEY` 指向 DeepSeek 的 Anthropic Messages 兼容端点 `https://api.deepseek.com/anthropic`;`nvidia-nim` 通过 `https://integrate.api.nvidia.com/v1` 指向 NVIDIA NIM 托管的 DeepSeek 端点;`openai` 指向通用 OpenAI 兼容端点,默认 `https://api.openai.com/v1`;`atlascloud` 指向 AtlasCloud 的 OpenAI 兼容端点 `https://api.atlascloud.ai/v1`;`wanjie-ark` 指向 Wanjie Ark 的 OpenAI 兼容端点 `https://maas-openapi.wanjiedata.com/api/v1`;`volcengine` 指向火山方舟(Volcengine Ark)的 OpenAI 兼容编码端点 `https://ark.cn-beijing.volces.com/api/coding/v3`;`openrouter` 指向 `https://openrouter.ai/api/v1`;`xiaomi-mimo` 指向小米 MiMo 的 OpenAI 兼容端点,Token Plan key(`tp-...`)默认用 `https://token-plan-sgp.xiaomimimo.com/v1`,按量付费 key 用 `https://api.xiaomimimo.com/v1`。对于新加坡默认之外的 Token Plan 账号,显式设置 `base_url` 或对中国的账号用 `mode = "token-plan-cn"`,对欧洲/阿姆斯特丹用 `mode = "token-plan-ams"`;`novita` 指向 `https://api.novita.ai/openai/v1`;`fireworks` 指向 `https://api.fireworks.ai/inference/v1`;`siliconflow` 指向 SiliconFlow,默认 `https://api.siliconflow.com/v1`;`arcee` 指向 Arcee AI 的 OpenAI 兼容端点 `https://api.arcee.ai/api/v1`;`siliconflow-CN` 通过 `[providers.siliconflow_cn]` 指向 SiliconFlow 中国区域端点;`moonshot` 指向 Moonshot/Kimi,默认 `https://api.moonshot.ai/v1`;`sglang` 指向自托管 OpenAI 兼容端点,默认 `http://localhost:30000/v1`;`vllm` 指向自托管 vLLM OpenAI 兼容端点,默认 `http://localhost:8000/v1`;`ollama` 指向 Ollama 的 OpenAI 兼容端点,默认 `http://localhost:11434/v1`;`huggingface` 指向 Hugging Face Inference Providers `https://router.huggingface.co/v1`;`modelscope` 指向魔搭社区的 OpenAI 兼容推理 API `https://api-inference.modelscope.cn/v1`;`together` 指向 Together AI `https://api.together.xyz/v1`;`qianfan` 指向百度千帆 `https://api.baiduqianfan.ai/v1`;`openai-codex` 指向 ChatGPT/Codex OAuth;`anthropic` 指向 Claude 的原生 Messages API;`openmodel` 指向 OpenModel 的 Anthropic 兼容 Messages API `https://api.openmodel.ai`;`zai` 指向 Z.ai `https://api.z.ai/api/coding/paas/v4`;`stepfun` 指向 StepFun `https://api.stepfun.ai/v1`;`minimax` 指向 MiniMax `https://api.minimax.io/v1`;`deepinfra` 指向 DeepInfra `https://api.deepinfra.com/v1/openai`;`sakana` 指向 Sakana AI Fugu `https://api.sakana.ai/v1`;`longcat` 指向美团 LongCat `https://api.longcat.chat/openai/v1`;`opencode-go` 指向订阅支撑的 OpenCode Go Chat Completions 路由 `https://opencode.ai/zen/go/v1`;`meta` 指向 Meta Model API;`mistral` 指向 Mistral AI 的 OpenAI 兼容端点 `https://api.mistral.ai/v1`;`telecomjs` 指向 TelecomJS TokenHub `https://aigw.telecomjs.com/v1`;`xai` 指向 xAI 的 API key 或 OAuth 路由。
|
||||
- `opencode-zen`(字符串 provider 值):通过 `[providers.opencode_zen]` 选择模型感知的 OpenCode Zen 网关。默认 base URL 是 `https://opencode.ai/zen/v1`,默认模型 `gpt-5.6`,凭据来自 `api_key`、`OPENCODE_ZEN_API_KEY` 或回退 `OPENCODE_API_KEY`——绝不是 ChatGPT/Codex OAuth。接受 `OPENCODE_ZEN_BASE_URL` 和 `OPENCODE_ZEN_MODEL`。所选模型通过精选的 Zen 目录解析:GPT 用 Responses,Claude/Qwen 用 Anthropic Messages,记录的 DeepSeek/MiniMax/GLM/Kimi/Grok/free 行用 Chat Completions。Gemini 和未知模型失败关闭,因为 Codewhale 对它们没有经过验证的受支持线契约。确切当前模型组见 [`PROVIDERS.md`](PROVIDERS.md#opencode-zen-protocol-catalog)。
|
||||
- `minimax-anthropic`(字符串 provider 值):通过 `[providers.minimax_anthropic]` 选择 MiniMax 的 Anthropic 兼容 Messages 路由。默认 Base URL 是 `https://api.minimax.io/anthropic`;中国区域设置 `https://api.minimaxi.com/anthropic`。保留 `/anthropic` 后缀,因为 Codewhale 会追加 `/v1/messages`。该路由使用 `MINIMAX_API_KEY`,默认 `MiniMax-M3`;`MiniMax-M2.7` 也已注册。官方 M3 输入模态是文本、图像和视频,带自适应或禁用思考。M2.7 仅文本,总是保持思考启用。
|
||||
- `api_key`(字符串,托管 provider 必填):对 DeepSeek/托管 provider 必须非空(或设置 provider API key 环境变量)。自托管 SGLang、vLLM 和本地 `ollama` 可省略。`ollama-cloud` 需要为该 provider 保存的密钥或由 `OLLAMA_CLOUD_API_KEY` 提供,然后 `OLLAMA_API_KEY`。
|
||||
- `auth_mode`(字符串,可选的 provider 表键):选择 provider 特有的认证契约。Kimi Code 会员使用 `auth_mode = "api_key"`(或省略该字段),在 [Kimi Code 控制台](https://www.kimi.com/code/console) 创建的 key,`base_url = "https://api.kimi.com/coding/v1"`,K3 用裸 `model = "k3"`。Codewhale 给该路由安全的 262,144-token 基线;只有 Kimi Code 计划包含 1M 访问(Allegretto 及以上)时才设置 `context_window = 1048576`。`k3[1m]` 是仅 Claude Code 的约定,不是 API 模型 ID,Codewhale 会拒绝它,而不是静默改变线模型或假定资格。`model = "kimi-for-coding"` 仍是所有 Kimi Code 会员可用的有效 K2.7 兼容路由。旧 `auth_mode = "kimi_oauth"` 以 API key 指引失败关闭,从不探测、读取、刷新或重写 `kimi_cli`/`kimi_code_cli` 凭据文件。一等 OAuth 需要 Codewhale 自己的厂商注册客户端身份,仍在 #4417 跟踪。
|
||||
- `base_url`(字符串,可选):DeepSeek 的 OpenAI 兼容 Chat Completions API 默认 `https://api.deepseek.com/beta`,包括旧 `provider = "deepseek-cn"` 配置。其他默认:`deepseek-anthropic` 是 `https://api.deepseek.com/anthropic`,`nvidia-nim` 是 `https://integrate.api.nvidia.com/v1`,`openai` 是 `https://api.openai.com/v1`,`atlascloud` 是 `https://api.atlascloud.ai/v1`,`wanjie-ark` 是 `https://maas-openapi.wanjiedata.com/api/v1`,`volcengine` 是 `https://ark.cn-beijing.volces.com/api/coding/v3`,`openrouter` 是 `https://openrouter.ai/api/v1`,`xiaomi-mimo` 在 API key 以 `tp-...` 开头时是 `https://token-plan-sgp.xiaomimimo.com/v1`,否则 `https://api.xiaomimimo.com/v1`,`novita` 是 `https://api.novita.ai/openai/v1`,`fireworks` 是 `https://api.fireworks.ai/inference/v1`,`siliconflow` 是 `https://api.siliconflow.com/v1`,`siliconflow-CN` 是 `https://api.siliconflow.cn/v1`,`arcee` 是 `https://api.arcee.ai/api/v1`,`moonshot` 是 `https://api.moonshot.ai/v1`,`minimax` 是 `https://api.minimax.io/v1`,`openmodel` 是 `https://api.openmodel.ai`,`zai` 是 `https://api.z.ai/api/coding/paas/v4`,`stepfun` 是 `https://api.stepfun.ai/v1`,`deepinfra` 是 `https://api.deepinfra.com/v1/openai`,`sakana` 是 `https://api.sakana.ai/v1`,`huggingface` 是 `https://router.huggingface.co/v1`,`together` 是 `https://api.together.xyz/v1`,`qianfan` 是 `https://api.baiduqianfan.ai/v1`,`openai-codex` 是 `https://chatgpt.com/backend-api`,`anthropic` 是 `https://api.anthropic.com`,`mistral` 是 `https://api.mistral.ai/v1`,`sglang` 是 `http://localhost:30000/v1`,`vllm` 是 `http://localhost:8000/v1`,`ollama` 是 `http://localhost:11434/v1`,`ollama-cloud` 是 `https://ollama.com/v1`。中国区域小米 MiMo Token Plan 账号设置 `base_url = "https://token-plan-cn.xiaomimimo.com/v1"`,欧洲/阿姆斯特丹账号设置 `base_url = "https://token-plan-ams.xiaomimimo.com/v1"`。Mistral 特有的推理字段和多态重放只在记录的官方 HTTPS `/v1` 主机上启用;自定义 Mistral base URL 保持通用 Chat 语义。显式设置 `https://api.deepseek.com` 或 `https://api.deepseek.com/v1` 可选择退出 DeepSeek beta 功能。
|
||||
- `base_url`(字符串,可选):DeepSeek 的 OpenAI 兼容 Chat Completions API 默认 `https://api.deepseek.com/beta`,包括旧 `provider = "deepseek-cn"` 配置。其他默认:`deepseek-anthropic` 是 `https://api.deepseek.com/anthropic`,`nvidia-nim` 是 `https://integrate.api.nvidia.com/v1`,`openai` 是 `https://api.openai.com/v1`,`atlascloud` 是 `https://api.atlascloud.ai/v1`,`wanjie-ark` 是 `https://maas-openapi.wanjiedata.com/api/v1`,`volcengine` 是 `https://ark.cn-beijing.volces.com/api/coding/v3`,`openrouter` 是 `https://openrouter.ai/api/v1`,`xiaomi-mimo` 在 API key 以 `tp-...` 开头时是 `https://token-plan-sgp.xiaomimimo.com/v1`,否则 `https://api.xiaomimimo.com/v1`,`novita` 是 `https://api.novita.ai/openai/v1`,`fireworks` 是 `https://api.fireworks.ai/inference/v1`,`siliconflow` 是 `https://api.siliconflow.com/v1`,`siliconflow-CN` 是 `https://api.siliconflow.cn/v1`,`arcee` 是 `https://api.arcee.ai/api/v1`,`moonshot` 是 `https://api.moonshot.ai/v1`,`minimax` 是 `https://api.minimax.io/v1`,`openmodel` 是 `https://api.openmodel.ai`,`zai` 是 `https://api.z.ai/api/coding/paas/v4`,`stepfun` 是 `https://api.stepfun.ai/v1`,`deepinfra` 是 `https://api.deepinfra.com/v1/openai`,`sakana` 是 `https://api.sakana.ai/v1`,`huggingface` 是 `https://router.huggingface.co/v1`,`modelscope` 是 `https://api-inference.modelscope.cn/v1`,`together` 是 `https://api.together.xyz/v1`,`qianfan` 是 `https://api.baiduqianfan.ai/v1`,`openai-codex` 是 `https://chatgpt.com/backend-api`,`anthropic` 是 `https://api.anthropic.com`,`mistral` 是 `https://api.mistral.ai/v1`,`sglang` 是 `http://localhost:30000/v1`,`vllm` 是 `http://localhost:8000/v1`,`ollama` 是 `http://localhost:11434/v1`,`ollama-cloud` 是 `https://ollama.com/v1`。中国区域小米 MiMo Token Plan 账号设置 `base_url = "https://token-plan-cn.xiaomimimo.com/v1"`,欧洲/阿姆斯特丹账号设置 `base_url = "https://token-plan-ams.xiaomimimo.com/v1"`。Mistral 特有的推理字段和多态重放只在记录的官方 HTTPS `/v1` 主机上启用;自定义 Mistral base URL 保持通用 Chat 语义。显式设置 `https://api.deepseek.com` 或 `https://api.deepseek.com/v1` 可选择退出 DeepSeek beta 功能。
|
||||
- `ollama-cloud` 路由:选择 `provider = "ollama-cloud"`,覆盖默认 `https://ollama.com/v1` / `gpt-oss:120b` 元组时配置 `[providers.ollama_cloud]`,并用 `codewhale auth set --provider ollama-cloud` 从 [Ollama 账号设置](https://ollama.com/settings/keys) 保存 key。环境优先级是 `OLLAMA_CLOUD_API_KEY`,然后 `OLLAMA_API_KEY`;任意 Ollama 模型 ID 原样透传。
|
||||
- 旧 Ollama Cloud 迁移:规范化的 `[providers.ollama].base_url` 恰好是 `https://ollama.com/v1` 的已发布 `provider = "ollama"` 配置,会在内存中升级为 `ollama-cloud` 运行时身份。只有那个确切元组可以读取其旧的 `ollama` provider 表和秘密槽位。配置和秘密从不被重写,相邻路径、HTTP 降级、仿冒主机或显式 `ollama-cloud` 选择从不消费该回退。
|
||||
- `telecomjs` base URL 与目录:`[providers.telecomjs]` 默认 `https://aigw.telecomjs.com/v1`;`TELECOMJS_BASE_URL` 覆盖它。有 `TELECOMJS_API_KEY` 时,`/models` 刷新 key 作用域的目录,而不会把行混入另一个 provider。
|
||||
|
||||
@@ -18,14 +18,14 @@ DeepSeek 仍是默认提供商,但 `ProviderKind::ALL` 中的每个条目都
|
||||
|
||||
## 提供商选择
|
||||
|
||||
规范的提供商 ID 是 `ProviderKind::ALL`(`crates/config/src/provider_kind.rs`)的 42 个条目,按该顺序排列:
|
||||
规范的提供商 ID 是 `ProviderKind::ALL`(`crates/config/src/provider_kind.rs`)的 44 个条目,按该顺序排列:
|
||||
|
||||
`deepseek`, `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `volcengine`,
|
||||
`openrouter`, `orcarouter`, `xiaomi-mimo`, `novita`, `fireworks`, `siliconflow`, `arcee`,
|
||||
`siliconflow-CN`, `moonshot`, `sglang`, `vllm`, `ollama`, `ollama-cloud`, `huggingface`,
|
||||
`together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`,
|
||||
`stepfun`, `minimax`, `deepinfra`, `sakana`, `longcat`, `opencode-go`,
|
||||
`opencode-zen`, `meta`, `xai`, `mistral`, `telecomjs`, `modelstudio-token-plan`,
|
||||
`opencode-zen`, `meta`, `xai`, `mistral`, `telecomjs`, `modelstudio-token-plan`, `modelscope`,
|
||||
`google`, `antigravity`, `edenai`, 和 `custom`。
|
||||
|
||||
`deepseek-anthropic` *不在*此列表中——它是 `deepseek` 的线协议方言,通过 `wire = "anthropic"` 访问,而不是一个可单独选择的路由。
|
||||
@@ -75,6 +75,7 @@ DeepSeek 仍是默认提供商,但 `ProviderKind::ALL` 中的每个条目都
|
||||
| `ollama` | `[providers.ollama]` | 本地 OpenAI 兼容 Chat Completions | `OLLAMA_API_KEY`(可选;仅用于需要认证的本地路由) |
|
||||
| `ollama-cloud` | `[providers.ollama_cloud]` | 托管 OpenAI 兼容 Chat Completions | `OLLAMA_CLOUD_API_KEY`, `OLLAMA_API_KEY` |
|
||||
| `huggingface` | `[providers.huggingface]` | OpenAI Chat Completions | `HUGGINGFACE_API_KEY`, `HF_TOKEN` |
|
||||
| `modelscope` | `[providers.modelscope]` | OpenAI Chat Completions | `MODELSCOPE_API_KEY` |
|
||||
| `together` | `[providers.together]` | OpenAI Chat Completions | `TOGETHER_API_KEY` |
|
||||
| `qianfan` | `[providers.qianfan]` | OpenAI Chat Completions | `QIANFAN_API_KEY`, `BAIDU_QIANFAN_API_KEY` |
|
||||
| `openai-codex` | `[providers.openai_codex]` | OpenAI Responses | `OPENAI_CODEX_ACCESS_TOKEN`, `CODEX_ACCESS_TOKEN` |
|
||||
@@ -324,6 +325,7 @@ model = "qwen3:8b" # 默认是 deepseek-v4-flash
|
||||
| `stepfun` | [StepFun 开放平台](https://platform.stepfun.ai/) |
|
||||
| `minimax`, `minimax-anthropic` | [MiniMax 接口密钥](https://platform.minimax.io/user-center/basic-information/interface-key) |
|
||||
| `huggingface` | [Hugging Face token](https://huggingface.co/settings/tokens) |
|
||||
| `modelscope` | [ModelScope API 密钥](https://modelscope.cn/my/settings/token) |
|
||||
| `deepinfra` | [DeepInfra API 密钥](https://deepinfra.com/dash/api_keys) |
|
||||
| `together` | [Together API 密钥](https://api.together.ai/settings/api-keys) |
|
||||
| `qianfan` | [百度云访问密钥](https://console.bce.baidu.com/iam/#/iam/accesslist) |
|
||||
@@ -404,6 +406,7 @@ Kimi 仍然仅支持 API 密钥;对 Kimi 的外部授权被拒绝。
|
||||
| `ollama` | `[providers.ollama]` | 本地可选 `OLLAMA_API_KEY` | `OLLAMA_BASE_URL`;默认 `http://localhost:11434/v1` | 本地目录的实况标签;刷新前占位 `unknown`;提供商提示的自定义标签直通 | 本地 Ollama 默认无密钥。接受 `OLLAMA_MODEL`。标题栏不得绘制本机守护进程未列出的托管 id。 |
|
||||
| `ollama-cloud` | `[providers.ollama_cloud]` | `OLLAMA_CLOUD_API_KEY`, 然后是 `OLLAMA_API_KEY` | `OLLAMA_CLOUD_BASE_URL`;默认 `https://ollama.com/v1` | `gpt-oss:120b`;任意的提供商自有 ID 直通 | 托管 OpenAI 兼容 `/v1/chat/completions` 路由。在 `ollama-cloud` 下保存凭据;精确的已发布 `ollama` + Cloud URL 元组与其遗留表和秘密槽位具有有界的只读内存兼容性。接受 `OLLAMA_CLOUD_MODEL`。 |
|
||||
| `huggingface` | `[providers.huggingface]` | `HUGGINGFACE_API_KEY`, `HF_TOKEN` | `HUGGINGFACE_BASE_URL`, `HF_BASE_URL`;默认 `https://router.huggingface.co/v1` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | Hugging Face Inference Providers OpenAI 兼容路由器路由。接受的别名:`huggingface`, `hugging-face`, `hugging_face`, `hf`。Org 前缀的模型 ID 直通。接受 `HUGGINGFACE_MODEL` 和 `HF_MODEL`。Hub 浏览/导出是未来的独立功能。 |
|
||||
| `modelscope` | `[providers.modelscope]` | `MODELSCOPE_API_KEY` | `MODELSCOPE_BASE_URL`;默认 `https://api-inference.modelscope.cn/v1` | `Qwen/Qwen3.5-397B-A17B`(默认), `Qwen/Qwen3.5-122B-A10B`, `Qwen/Qwen3.5-27B`, `Qwen/Qwen3.5-35B-A3B`, `Qwen/Qwen3.8-27B`, `Qwen/Qwen3.8-Flash-Next`, `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Pro-0813`, `deepseek-ai/DeepSeek-V4.1-Flash`, `ZhipuAI/GLM-4.7-Flash`, `ZhipuAI/GLM-5.2` | 阿里魔搭社区 OpenAI 兼容推理 API 路由。模型 ID 直通。接受 `MODELSCOPE_MODEL`。 |
|
||||
| `deepinfra` | `[providers.deepinfra]` | `DEEPINFRA_API_KEY`, `DEEPINFRA_TOKEN` | `DEEPINFRA_BASE_URL`;默认 `https://api.deepinfra.com/v1/openai` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | DeepInfra OpenAI 兼容路由。OpenAI SDK 的直接替代品。 |
|
||||
| `together` | `[providers.together]` | `TOGETHER_API_KEY` | `TOGETHER_BASE_URL`;默认 `https://api.together.xyz/v1` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash`, `thinkingmachines/inkling` | Together AI OpenAI 兼容路由。接受 `TOGETHER_MODEL`。模型别名 `deepseek-v4-pro` 和 `deepseek-v4-flash` 规范化为 Together 的 org 前缀 ID;`inkling` 和 `together-inkling` 规范化为 Together 发布的小写 Inkling 线协议 ID。Inkling 使用 Thinking Machines 的[官方模型仓库](https://huggingface.co/thinkingmachines/Inkling)中精确的 `none`/`minimal`/`low`/`medium`/`high`/`max` 推理词汇。Together 的[发布帖](https://www.together.ai/blog/together-ai-brings-thinking-machines-labs-new-model-inkling-on-day-0)目前称 Inkling 以 1M 上下文上线,而其[模型详情页](https://www.together.ai/models/inkling)称即将推出 256K 上下文且不公布价格。在 Together 活跃的 `/models` 端点和 Models.dev 目录解决该冲突之前,Inkling 不会种入 Codewhale 的离线选择器,也不会推断路由特定的上下文或成本。 |
|
||||
| `qianfan` | `[providers.qianfan]` | `QIANFAN_API_KEY`, `BAIDU_QIANFAN_API_KEY` | `QIANFAN_BASE_URL`, `BAIDU_QIANFAN_BASE_URL`;默认 `https://api.baiduqianfan.ai/v1` | `ernie-4.0-turbo-8k`;提供商范围的定制 Qianfan 服务/模型 ID 直通 | 百度千帆 OpenAI 兼容路由。请求使用 Bearer 认证和 Chat Completions 负载。接受 `QIANFAN_MODEL` 和 `BAIDU_QIANFAN_MODEL`;别名 `baidu-qianfan`, `baidu_qianfan` 和 `baidu` 解析到该提供商。千帆文档中工具/函数调用按模型范围限定,因此 Codewhale 保留所选线协议模型,把实时能力证明留给后续的路由/能力工作。 |
|
||||
@@ -517,6 +520,7 @@ OpenRouter completions 和静态注册表行包括自 2026 年 4 月起通过 Op
|
||||
| `ollama` | 实况本地标签;提供商提示为 `ollama` 时自定义标签原样通过 | 是 | 否 |
|
||||
| `ollama-cloud` | `gpt-oss:120b`;任意的提供商自有模型 ID 原样通过 | 是 | 是 |
|
||||
| `huggingface` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | 是 | 否 |
|
||||
| `modelscope` | `Qwen/Qwen3.5-397B-A17B`, `Qwen/Qwen3.5-122B-A10B`, `Qwen/Qwen3.5-27B`, `Qwen/Qwen3.5-35B-A3B`, `Qwen/Qwen3.8-27B`, `Qwen/Qwen3.8-Flash-Next`, `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Pro-0813`, `deepseek-ai/DeepSeek-V4.1-Flash`, `ZhipuAI/GLM-4.7-Flash`, `ZhipuAI/GLM-5.2` | 是 | 否 |
|
||||
| `deepinfra` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash` | 是 | 是 |
|
||||
| `together` | `deepseek-ai/DeepSeek-V4-Pro`, `deepseek-ai/DeepSeek-V4-Flash`, `thinkingmachines/inkling` | 是 | 是 |
|
||||
| `openai-codex` | `gpt-5.5` | 是 | 是 |
|
||||
@@ -651,7 +655,7 @@ DeepSeek 将于 2026-07-24 15:59 UTC 退役 `deepseek-chat` 和 `deepseek-reason
|
||||
| 配置为 `zai` 的兼容网关 | omitted;生效值不可用 | omitted;生效值不可用 | omitted;生效值不可用 |
|
||||
| `nvidia-nim` | `chat_template_kwargs.thinking: false` | `chat_template_kwargs`:`thinking: true` + `reasoning_effort: "high"` | `chat_template_kwargs`:`thinking: true` + `reasoning_effort: "max"` |
|
||||
| `vllm` | `chat_template_kwargs.enable_thinking: false` | `chat_template_kwargs.enable_thinking: true` + `reasoning_effort` low/medium/high | `chat_template_kwargs.enable_thinking: true` + `reasoning_effort: "high"`(vLLM 没有 max 层级) |
|
||||
| `arcee`, `huggingface` | omitted | `reasoning_effort` 直通 | `reasoning_effort: "high"` |
|
||||
| `arcee`, `huggingface`, `modelscope` | omitted | `reasoning_effort` 直通 | `reasoning_effort: "high"` |
|
||||
| `fireworks` | omitted | `reasoning_effort: "high"` | `reasoning_effort: "max"` |
|
||||
| `openai`, `wanjie-ark`, `telecomjs` | omitted | omitted | omitted |
|
||||
| `openmodel` | Anthropic Messages 适配器处理思考/输出配置 | Anthropic Messages 适配器处理思考/输出配置 | Anthropic Messages 适配器处理思考/输出配置 |
|
||||
|
||||
@@ -68,6 +68,7 @@ codewhale auth set --provider deepseek
|
||||
| `xiaomi-mimo` | 小米 | MiMo 系列 |
|
||||
| `siliconflow` | 硅基流动 | 模型聚合平台 |
|
||||
| `siliconflow-CN` | 硅基流动(中国区) | 国内域名 |
|
||||
| `modelscope` | 魔搭社区 | 大模型开源社区 |
|
||||
| `longcat` | 美团 | LongCat 系列 |
|
||||
| `telecomjs` | 中国电信 | 天翼 AI 网关 |
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ const PROVIDER_LABEL_MAP = {
|
||||
Ollama: { id: "ollama", label: "Ollama", env: "OLLAMA_API_KEY" },
|
||||
OllamaCloud: { id: "ollama-cloud", label: "Ollama Cloud", env: "OLLAMA_CLOUD_API_KEY / OLLAMA_API_KEY" },
|
||||
Huggingface: { id: "huggingface", label: "Hugging Face", env: "HUGGINGFACE_API_KEY / HF_TOKEN" },
|
||||
Modelscope: { id: "modelscope", label: "ModelScope", env: "MODELSCOPE_API_KEY" },
|
||||
Deepinfra: { id: "deepinfra", label: "DeepInfra", env: "DEEPINFRA_API_KEY / DEEPINFRA_TOKEN" },
|
||||
Together: { id: "together", label: "Together AI", env: "TOGETHER_API_KEY" },
|
||||
Qianfan: { id: "qianfan", label: "Baidu Qianfan", env: "QIANFAN_API_KEY / BAIDU_QIANFAN_API_KEY" },
|
||||
|
||||
Reference in new issue
Block a user