* feat(config): add first-party Z.ai and StepFlash/StepFun provider routes (#3187) Add Z.ai (GLM Coding Plan) and StepFun/StepFlash as first-party providers: Provider: Z.ai - Default model: GLM-5.1 (200K context, 128K output, thinking, function calling) - Base URL: https://api.z.ai/api/coding/paas/v4 - Auth: ZAI_API_KEY / Z_AI_API_KEY - Config key: [providers.zai] Provider: StepFun - Default model: step-3.7-flash (256K context, 256K output, 3-level reasoning) - Base URL: https://api.stepfun.ai/v1 - Auth: STEPFUN_API_KEY / STEP_API_KEY - Config key: [providers.stepfun] Both providers use OpenAI-compatible Chat Completions transport. Includes: - ProviderKind enum variants with serde aliases - ProvidersToml config fields with aliases - Provider trait implementations in provider.rs - PROVIDER_REGISTRY, ALL, KIND_LOOKUP, FROM_KIND_LOOKUP updates - EnvRuntimeOverrides fields and env-var loading - TUI ApiProvider enum and lookup table updates - CLI provider_slot and provider_env_vars additions - Exhaustive match arms across client.rs, config.rs, engine.rs, main.rs, provider_picker.rs, ui.rs, and config_persistence.rs Verified: cargo check passes, config provider tests (67/67) pass. * test(tui): update provider picker tests for Zai and Stepfun additions
This commit is contained in:
@@ -780,6 +780,8 @@ fn provider_slot(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Together => "together",
|
||||
ProviderKind::OpenaiCodex => "openai-codex",
|
||||
ProviderKind::Anthropic => "anthropic",
|
||||
ProviderKind::Zai => "zai",
|
||||
ProviderKind::Stepfun => "stepfun",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,6 +917,8 @@ fn provider_env_vars(provider: ProviderKind) -> &'static [&'static str] {
|
||||
ProviderKind::Together => &["TOGETHER_API_KEY"],
|
||||
ProviderKind::OpenaiCodex => &["OPENAI_CODEX_ACCESS_TOKEN", "CODEX_ACCESS_TOKEN"],
|
||||
ProviderKind::Anthropic => &["ANTHROPIC_API_KEY"],
|
||||
ProviderKind::Zai => &["ZAI_API_KEY", "Z_AI_API_KEY"],
|
||||
ProviderKind::Stepfun => &["STEPFUN_API_KEY", "STEP_API_KEY"],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,13 @@ const DEFAULT_VLLM_BASE_URL: &str = "http://localhost:8000/v1";
|
||||
const DEFAULT_OLLAMA_MODEL: &str = "deepseek-coder:1.3b";
|
||||
const DEFAULT_OLLAMA_BASE_URL: &str = "http://localhost:11434/v1";
|
||||
|
||||
// Z.ai (GLM Coding Plan) defaults
|
||||
const DEFAULT_ZAI_MODEL: &str = "GLM-5.1";
|
||||
const DEFAULT_ZAI_BASE_URL: &str = "https://api.z.ai/api/coding/paas/v4";
|
||||
// StepFun / StepFlash defaults
|
||||
const DEFAULT_STEPFUN_MODEL: &str = "step-3.7-flash";
|
||||
const DEFAULT_STEPFUN_BASE_URL: &str = "https://api.stepfun.ai/v1";
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ProviderKind {
|
||||
@@ -158,10 +165,25 @@ pub enum ProviderKind {
|
||||
OpenaiCodex,
|
||||
#[serde(alias = "claude")]
|
||||
Anthropic,
|
||||
#[serde(
|
||||
alias = "z-ai",
|
||||
alias = "z_ai",
|
||||
alias = "z.ai"
|
||||
)]
|
||||
Zai,
|
||||
#[serde(
|
||||
alias = "step-fun",
|
||||
alias = "step_fun",
|
||||
alias = "stepfun",
|
||||
alias = "stepflash",
|
||||
alias = "step-flash",
|
||||
alias = "step_flash"
|
||||
)]
|
||||
Stepfun,
|
||||
}
|
||||
|
||||
impl ProviderKind {
|
||||
pub const ALL: [Self; 21] = [
|
||||
pub const ALL: [Self; 23] = [
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
@@ -183,6 +205,8 @@ impl ProviderKind {
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
Self::Zai,
|
||||
Self::Stepfun,
|
||||
];
|
||||
|
||||
#[must_use]
|
||||
@@ -206,6 +230,11 @@ impl ProviderKind {
|
||||
Self::Vllm,
|
||||
Self::Ollama,
|
||||
Self::Huggingface,
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
Self::Zai,
|
||||
Self::Stepfun,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -314,6 +343,23 @@ pub struct ProvidersToml {
|
||||
pub openai_codex: ProviderConfigToml,
|
||||
#[serde(default)]
|
||||
pub anthropic: ProviderConfigToml,
|
||||
#[serde(
|
||||
default,
|
||||
alias = "z-ai",
|
||||
alias = "z_ai",
|
||||
alias = "z.ai"
|
||||
)]
|
||||
pub zai: ProviderConfigToml,
|
||||
#[serde(
|
||||
default,
|
||||
alias = "step-fun",
|
||||
alias = "step_fun",
|
||||
alias = "stepfun",
|
||||
alias = "stepflash",
|
||||
alias = "step-flash",
|
||||
alias = "step_flash"
|
||||
)]
|
||||
pub stepfun: ProviderConfigToml,
|
||||
}
|
||||
|
||||
/// Sibling `permissions.toml` schema.
|
||||
@@ -365,6 +411,8 @@ impl ProvidersToml {
|
||||
ProviderKind::Together => &self.together,
|
||||
ProviderKind::OpenaiCodex => &self.openai_codex,
|
||||
ProviderKind::Anthropic => &self.anthropic,
|
||||
ProviderKind::Zai => &self.zai,
|
||||
ProviderKind::Stepfun => &self.stepfun,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +439,8 @@ impl ProvidersToml {
|
||||
ProviderKind::Together => &mut self.together,
|
||||
ProviderKind::OpenaiCodex => &mut self.openai_codex,
|
||||
ProviderKind::Anthropic => &mut self.anthropic,
|
||||
ProviderKind::Zai => &mut self.zai,
|
||||
ProviderKind::Stepfun => &mut self.stepfun,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2114,6 +2164,8 @@ impl ConfigToml {
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_BASE_URL.to_string(),
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL.to_string(),
|
||||
ProviderKind::Anthropic => DEFAULT_ANTHROPIC_BASE_URL.to_string(),
|
||||
ProviderKind::Zai => DEFAULT_ZAI_BASE_URL.to_string(),
|
||||
ProviderKind::Stepfun => DEFAULT_STEPFUN_BASE_URL.to_string(),
|
||||
})
|
||||
};
|
||||
// CLI flag wins outright. Otherwise: config-file → injected secrets/env.
|
||||
@@ -2580,6 +2632,8 @@ fn default_model_for_provider(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_MODEL,
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_MODEL,
|
||||
ProviderKind::Anthropic => DEFAULT_ANTHROPIC_MODEL,
|
||||
ProviderKind::Zai => DEFAULT_ZAI_MODEL,
|
||||
ProviderKind::Stepfun => DEFAULT_STEPFUN_MODEL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2606,6 +2660,8 @@ fn default_base_url_for_provider(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_BASE_URL,
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL,
|
||||
ProviderKind::Anthropic => DEFAULT_ANTHROPIC_BASE_URL,
|
||||
ProviderKind::Zai => DEFAULT_ZAI_BASE_URL,
|
||||
ProviderKind::Stepfun => DEFAULT_STEPFUN_BASE_URL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3387,6 +3443,10 @@ struct EnvRuntimeOverrides {
|
||||
openai_codex_model: Option<String>,
|
||||
anthropic_base_url: Option<String>,
|
||||
anthropic_model: Option<String>,
|
||||
zai_base_url: Option<String>,
|
||||
zai_model: Option<String>,
|
||||
stepfun_base_url: Option<String>,
|
||||
stepfun_model: Option<String>,
|
||||
}
|
||||
|
||||
impl EnvRuntimeOverrides {
|
||||
@@ -3558,6 +3618,22 @@ impl EnvRuntimeOverrides {
|
||||
anthropic_model: std::env::var("ANTHROPIC_MODEL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
zai_base_url: std::env::var("ZAI_BASE_URL")
|
||||
.or_else(|_| std::env::var("Z_AI_BASE_URL"))
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
zai_model: std::env::var("ZAI_MODEL")
|
||||
.or_else(|_| std::env::var("Z_AI_MODEL"))
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
stepfun_base_url: std::env::var("STEPFUN_BASE_URL")
|
||||
.or_else(|_| std::env::var("STEP_BASE_URL"))
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
stepfun_model: std::env::var("STEPFUN_MODEL")
|
||||
.or_else(|_| std::env::var("STEP_MODEL"))
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3601,6 +3677,8 @@ impl EnvRuntimeOverrides {
|
||||
ProviderKind::Together => self.together_base_url.clone(),
|
||||
ProviderKind::OpenaiCodex => self.openai_codex_base_url.clone(),
|
||||
ProviderKind::Anthropic => self.anthropic_base_url.clone(),
|
||||
ProviderKind::Zai => self.zai_base_url.clone(),
|
||||
ProviderKind::Stepfun => self.stepfun_base_url.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,12 @@ use super::{
|
||||
DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_CODEX_BASE_URL, DEFAULT_OPENAI_CODEX_MODEL,
|
||||
DEFAULT_OPENAI_MODEL, DEFAULT_OPENROUTER_BASE_URL, DEFAULT_OPENROUTER_MODEL,
|
||||
DEFAULT_SGLANG_BASE_URL, DEFAULT_SGLANG_MODEL, DEFAULT_SILICONFLOW_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_CN_BASE_URL, DEFAULT_SILICONFLOW_MODEL, DEFAULT_TOGETHER_BASE_URL,
|
||||
DEFAULT_TOGETHER_MODEL, DEFAULT_VLLM_BASE_URL, DEFAULT_VLLM_MODEL, DEFAULT_VOLCENGINE_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_CN_BASE_URL, DEFAULT_SILICONFLOW_MODEL, DEFAULT_STEPFUN_BASE_URL,
|
||||
DEFAULT_STEPFUN_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_XIAOMI_MIMO_BASE_URL, DEFAULT_XIAOMI_MIMO_MODEL, ProviderKind,
|
||||
DEFAULT_XIAOMI_MIMO_BASE_URL, DEFAULT_XIAOMI_MIMO_MODEL, DEFAULT_ZAI_BASE_URL,
|
||||
DEFAULT_ZAI_MODEL, ProviderKind,
|
||||
};
|
||||
|
||||
/// Wire protocol spoken by a provider.
|
||||
@@ -434,6 +436,30 @@ impl Provider for Anthropic {
|
||||
}
|
||||
}
|
||||
|
||||
provider!(
|
||||
Zai,
|
||||
Zai,
|
||||
"zai",
|
||||
"Z.ai (GLM Coding)",
|
||||
DEFAULT_ZAI_BASE_URL,
|
||||
DEFAULT_ZAI_MODEL,
|
||||
["ZAI_API_KEY", "Z_AI_API_KEY"],
|
||||
"zai",
|
||||
aliases: ["z-ai", "z_ai", "z.ai"]
|
||||
);
|
||||
|
||||
provider!(
|
||||
Stepfun,
|
||||
Stepfun,
|
||||
"stepfun",
|
||||
"StepFun / StepFlash",
|
||||
DEFAULT_STEPFUN_BASE_URL,
|
||||
DEFAULT_STEPFUN_MODEL,
|
||||
["STEPFUN_API_KEY", "STEP_API_KEY"],
|
||||
"stepfun",
|
||||
aliases: ["step-fun", "step_fun", "stepflash", "step-flash", "step_flash"]
|
||||
);
|
||||
|
||||
static DEEPSEEK: Deepseek = Deepseek;
|
||||
static NVIDIA_NIM: NvidiaNim = NvidiaNim;
|
||||
static OPENAI: Openai = Openai;
|
||||
@@ -455,8 +481,10 @@ static HUGGINGFACE: Huggingface = Huggingface;
|
||||
static TOGETHER: Together = Together;
|
||||
static OPENAI_CODEX: OpenaiCodex = OpenaiCodex;
|
||||
static ANTHROPIC: Anthropic = Anthropic;
|
||||
static ZAI: Zai = Zai;
|
||||
static STEPFUN: Stepfun = Stepfun;
|
||||
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 21] = [
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 23] = [
|
||||
&DEEPSEEK,
|
||||
&NVIDIA_NIM,
|
||||
&OPENAI,
|
||||
@@ -478,6 +506,8 @@ static PROVIDER_REGISTRY: [&dyn Provider; 21] = [
|
||||
&TOGETHER,
|
||||
&OPENAI_CODEX,
|
||||
&ANTHROPIC,
|
||||
&ZAI,
|
||||
&STEPFUN,
|
||||
];
|
||||
|
||||
/// Return all built-in provider metadata entries in `ProviderKind::ALL` order.
|
||||
|
||||
@@ -1294,6 +1294,7 @@ pub(super) fn apply_reasoning_effort(
|
||||
"thinking": false,
|
||||
});
|
||||
}
|
||||
ApiProvider::Zai | ApiProvider::Stepfun => {}
|
||||
},
|
||||
"low" | "minimal" | "medium" | "mid" | "high" | "" => match provider {
|
||||
// DeepSeek compatibility: low/medium both map to high
|
||||
@@ -1367,6 +1368,7 @@ pub(super) fn apply_reasoning_effort(
|
||||
"reasoning_effort": "high",
|
||||
});
|
||||
}
|
||||
ApiProvider::Zai | ApiProvider::Stepfun => {}
|
||||
},
|
||||
"xhigh" | "max" | "highest" => match provider {
|
||||
ApiProvider::Deepseek
|
||||
@@ -1420,6 +1422,7 @@ pub(super) fn apply_reasoning_effort(
|
||||
"reasoning_effort": "max",
|
||||
});
|
||||
}
|
||||
ApiProvider::Zai | ApiProvider::Stepfun => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,10 @@ pub const COMMON_DEEPSEEK_MODELS: &[&str] = &[
|
||||
"deepseek/deepseek-v4-flash",
|
||||
];
|
||||
pub const OFFICIAL_DEEPSEEK_MODELS: &[&str] = &["deepseek-v4-pro", "deepseek-v4-flash"];
|
||||
pub const DEFAULT_ZAI_MODEL: &str = "GLM-5.1";
|
||||
pub const DEFAULT_ZAI_BASE_URL: &str = "https://api.z.ai/api/coding/paas/v4";
|
||||
pub const DEFAULT_STEPFUN_MODEL: &str = "step-3.7-flash";
|
||||
pub const DEFAULT_STEPFUN_BASE_URL: &str = "https://api.stepfun.ai/v1";
|
||||
pub const DEFAULT_ANTHROPIC_MODEL: &str = "claude-sonnet-4-6";
|
||||
pub const ANTHROPIC_OPUS_MODEL: &str = "claude-opus-4-8";
|
||||
pub const ANTHROPIC_HAIKU_MODEL: &str = "claude-haiku-4-5";
|
||||
@@ -201,6 +205,8 @@ pub enum ApiProvider {
|
||||
Together,
|
||||
OpenaiCodex,
|
||||
Anthropic,
|
||||
Zai,
|
||||
Stepfun,
|
||||
}
|
||||
|
||||
impl ApiProvider {
|
||||
@@ -258,7 +264,7 @@ impl ApiProvider {
|
||||
|
||||
/// `ApiProvider` discriminant → `ProviderKind` lookup.
|
||||
/// Index 1 is `None` for the legacy `DeepseekCN` variant.
|
||||
const KIND_LOOKUP: [Option<codewhale_config::ProviderKind>; 22] = [
|
||||
const KIND_LOOKUP: [Option<codewhale_config::ProviderKind>; 24] = [
|
||||
Some(codewhale_config::ProviderKind::Deepseek),
|
||||
None, // DeepseekCN
|
||||
Some(codewhale_config::ProviderKind::NvidiaNim),
|
||||
@@ -281,10 +287,12 @@ impl ApiProvider {
|
||||
Some(codewhale_config::ProviderKind::Together),
|
||||
Some(codewhale_config::ProviderKind::OpenaiCodex),
|
||||
Some(codewhale_config::ProviderKind::Anthropic),
|
||||
Some(codewhale_config::ProviderKind::Zai),
|
||||
Some(codewhale_config::ProviderKind::Stepfun),
|
||||
];
|
||||
|
||||
/// `ProviderKind` discriminant → `ApiProvider` lookup.
|
||||
const FROM_KIND_LOOKUP: [Self; 21] = [
|
||||
const FROM_KIND_LOOKUP: [Self; 23] = [
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
@@ -306,6 +314,8 @@ impl ApiProvider {
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
Self::Zai,
|
||||
Self::Stepfun,
|
||||
];
|
||||
|
||||
/// Map to the config-level `ProviderKind`.
|
||||
@@ -889,6 +899,8 @@ pub fn model_completion_names_for_provider(provider: ApiProvider) -> Vec<&'stati
|
||||
ApiProvider::Openai | ApiProvider::Atlascloud => OFFICIAL_DEEPSEEK_MODELS.to_vec(),
|
||||
ApiProvider::Together => vec![DEFAULT_TOGETHER_MODEL],
|
||||
ApiProvider::OpenaiCodex => vec![DEFAULT_OPENAI_CODEX_MODEL],
|
||||
ApiProvider::Zai => vec![DEFAULT_ZAI_MODEL],
|
||||
ApiProvider::Stepfun => vec![DEFAULT_STEPFUN_MODEL],
|
||||
ApiProvider::Anthropic => vec![
|
||||
ANTHROPIC_OPUS_MODEL,
|
||||
DEFAULT_ANTHROPIC_MODEL,
|
||||
@@ -2050,6 +2062,10 @@ pub struct ProvidersConfig {
|
||||
pub openai_codex: ProviderConfig,
|
||||
#[serde(default, alias = "claude")]
|
||||
pub anthropic: ProviderConfig,
|
||||
#[serde(default)]
|
||||
pub zai: ProviderConfig,
|
||||
#[serde(default)]
|
||||
pub stepfun: ProviderConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Default)]
|
||||
@@ -2217,6 +2233,8 @@ impl Config {
|
||||
ApiProvider::Together => "providers.together",
|
||||
ApiProvider::OpenaiCodex => "providers.openai_codex",
|
||||
ApiProvider::Anthropic => "providers.anthropic",
|
||||
ApiProvider::Zai => "providers.zai",
|
||||
ApiProvider::Stepfun => "providers.stepfun",
|
||||
ApiProvider::Deepseek | ApiProvider::DeepseekCN => return,
|
||||
};
|
||||
tracing::warn!(
|
||||
@@ -2374,6 +2392,8 @@ impl Config {
|
||||
ApiProvider::Together => &providers.together,
|
||||
ApiProvider::OpenaiCodex => &providers.openai_codex,
|
||||
ApiProvider::Anthropic => &providers.anthropic,
|
||||
ApiProvider::Zai => &providers.zai,
|
||||
ApiProvider::Stepfun => &providers.stepfun,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2402,6 +2422,8 @@ impl Config {
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::OpenaiCodex => &mut providers.openai_codex,
|
||||
ApiProvider::Anthropic => &mut providers.anthropic,
|
||||
ApiProvider::Zai => &mut providers.zai,
|
||||
ApiProvider::Stepfun => &mut providers.stepfun,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2543,6 +2565,8 @@ impl Config {
|
||||
ApiProvider::Huggingface => DEFAULT_HUGGINGFACE_MODEL,
|
||||
ApiProvider::Together => DEFAULT_TOGETHER_MODEL,
|
||||
ApiProvider::OpenaiCodex => DEFAULT_OPENAI_CODEX_MODEL,
|
||||
ApiProvider::Zai => DEFAULT_ZAI_MODEL,
|
||||
ApiProvider::Stepfun => DEFAULT_STEPFUN_MODEL,
|
||||
ApiProvider::Anthropic => DEFAULT_ANTHROPIC_MODEL,
|
||||
}
|
||||
.to_string()
|
||||
@@ -2583,7 +2607,9 @@ impl Config {
|
||||
| ApiProvider::Volcengine
|
||||
| ApiProvider::Huggingface
|
||||
| ApiProvider::Together
|
||||
| ApiProvider::OpenaiCodex => None,
|
||||
| ApiProvider::OpenaiCodex
|
||||
| ApiProvider::Zai
|
||||
| ApiProvider::Stepfun => None,
|
||||
};
|
||||
let configured_base_url = provider_base.or(root_base);
|
||||
let base = if provider == ApiProvider::XiaomiMimo {
|
||||
@@ -2630,6 +2656,8 @@ impl Config {
|
||||
ApiProvider::Huggingface => DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
ApiProvider::Together => DEFAULT_TOGETHER_BASE_URL,
|
||||
ApiProvider::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL,
|
||||
ApiProvider::Zai => DEFAULT_ZAI_BASE_URL,
|
||||
ApiProvider::Stepfun => DEFAULT_STEPFUN_BASE_URL,
|
||||
ApiProvider::Anthropic => DEFAULT_ANTHROPIC_BASE_URL,
|
||||
}
|
||||
.to_string()
|
||||
@@ -2680,6 +2708,8 @@ impl Config {
|
||||
ApiProvider::Huggingface => "huggingface",
|
||||
ApiProvider::Together => "together",
|
||||
ApiProvider::OpenaiCodex => "openai_codex",
|
||||
ApiProvider::Zai => "zai",
|
||||
ApiProvider::Stepfun => "stepfun",
|
||||
ApiProvider::Anthropic => "anthropic",
|
||||
};
|
||||
|
||||
@@ -2851,6 +2881,14 @@ impl Config {
|
||||
"Together AI API key not found. Run 'codewhale auth set --provider together', \
|
||||
set TOGETHER_API_KEY, or add [providers.together] api_key in ~/.codewhale/config.toml."
|
||||
),
|
||||
ApiProvider::Zai => anyhow::bail!(
|
||||
"Z.ai (GLM Coding) API key not found. Run 'codewhale auth set --provider zai', \
|
||||
set ZAI_API_KEY, or add [providers.zai] api_key in ~/.codewhale/config.toml."
|
||||
),
|
||||
ApiProvider::Stepfun => anyhow::bail!(
|
||||
"StepFun API key not found. Run 'codewhale auth set --provider stepfun', \
|
||||
set STEPFUN_API_KEY, or add [providers.stepfun] api_key in ~/.codewhale/config.toml."
|
||||
),
|
||||
ApiProvider::Anthropic => anyhow::bail!(
|
||||
"Anthropic API key not found. Run 'codewhale auth set --provider anthropic', \
|
||||
set ANTHROPIC_API_KEY, or add [providers.anthropic] api_key in ~/.codewhale/config.toml. \
|
||||
@@ -3707,6 +3745,20 @@ fn apply_env_overrides(config: &mut Config) {
|
||||
.openai_codex
|
||||
.base_url = Some(value);
|
||||
}
|
||||
ApiProvider::Zai => {
|
||||
config
|
||||
.providers
|
||||
.get_or_insert_with(ProvidersConfig::default)
|
||||
.zai
|
||||
.base_url = Some(value);
|
||||
}
|
||||
ApiProvider::Stepfun => {
|
||||
config
|
||||
.providers
|
||||
.get_or_insert_with(ProvidersConfig::default)
|
||||
.stepfun
|
||||
.base_url = Some(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches!(config.api_provider(), ApiProvider::NvidiaNim)
|
||||
@@ -3914,6 +3966,8 @@ fn apply_env_overrides(config: &mut Config) {
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::OpenaiCodex => &mut providers.openai_codex,
|
||||
ApiProvider::Anthropic => &mut providers.anthropic,
|
||||
ApiProvider::Zai => &mut providers.zai,
|
||||
ApiProvider::Stepfun => &mut providers.stepfun,
|
||||
};
|
||||
let mut provider_headers = entry.http_headers.clone().unwrap_or_default();
|
||||
provider_headers.extend(headers);
|
||||
@@ -4109,6 +4163,8 @@ fn apply_env_overrides(config: &mut Config) {
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::OpenaiCodex => &mut providers.openai_codex,
|
||||
ApiProvider::Anthropic => &mut providers.anthropic,
|
||||
ApiProvider::Zai => &mut providers.zai,
|
||||
ApiProvider::Stepfun => &mut providers.stepfun,
|
||||
};
|
||||
entry.model = Some(value);
|
||||
}
|
||||
@@ -4448,6 +4504,8 @@ fn default_base_url_for_provider(provider: ApiProvider) -> &'static str {
|
||||
ApiProvider::Huggingface => DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
ApiProvider::Together => DEFAULT_TOGETHER_BASE_URL,
|
||||
ApiProvider::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL,
|
||||
ApiProvider::Zai => DEFAULT_ZAI_BASE_URL,
|
||||
ApiProvider::Stepfun => DEFAULT_STEPFUN_BASE_URL,
|
||||
ApiProvider::Anthropic => DEFAULT_ANTHROPIC_BASE_URL,
|
||||
}
|
||||
}
|
||||
@@ -4894,6 +4952,8 @@ fn merge_providers(
|
||||
huggingface: merge_provider_config(base.huggingface, override_cfg.huggingface),
|
||||
together: merge_provider_config(base.together, override_cfg.together),
|
||||
openai_codex: merge_provider_config(base.openai_codex, override_cfg.openai_codex),
|
||||
zai: merge_provider_config(base.zai, override_cfg.zai),
|
||||
stepfun: merge_provider_config(base.stepfun, override_cfg.stepfun),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -5400,6 +5460,14 @@ pub fn active_provider_has_env_api_key(config: &Config) -> bool {
|
||||
std::env::var("OPENAI_CODEX_ACCESS_TOKEN").is_ok_and(|k| !k.trim().is_empty())
|
||||
|| std::env::var("CODEX_ACCESS_TOKEN").is_ok_and(|k| !k.trim().is_empty())
|
||||
}
|
||||
ApiProvider::Zai => {
|
||||
std::env::var("ZAI_API_KEY").is_ok_and(|k| !k.trim().is_empty())
|
||||
|| std::env::var("Z_AI_API_KEY").is_ok_and(|k| !k.trim().is_empty())
|
||||
}
|
||||
ApiProvider::Stepfun => {
|
||||
std::env::var("STEPFUN_API_KEY").is_ok_and(|k| !k.trim().is_empty())
|
||||
|| std::env::var("STEP_API_KEY").is_ok_and(|k| !k.trim().is_empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5434,6 +5502,8 @@ pub fn has_api_key_for(config: &Config, provider: ApiProvider) -> bool {
|
||||
ApiProvider::Vllm => "VLLM_API_KEY",
|
||||
ApiProvider::Ollama => "OLLAMA_API_KEY",
|
||||
ApiProvider::Volcengine => "VOLCENGINE_API_KEY",
|
||||
ApiProvider::Zai => "ZAI_API_KEY",
|
||||
ApiProvider::Stepfun => "STEPFUN_API_KEY",
|
||||
};
|
||||
if std::env::var(env_var).is_ok_and(|k| !k.trim().is_empty()) {
|
||||
return true;
|
||||
@@ -5561,6 +5631,8 @@ pub fn save_api_key_for(provider: ApiProvider, api_key: &str) -> Result<PathBuf>
|
||||
ApiProvider::Volcengine => "providers.volcengine",
|
||||
ApiProvider::Together => "providers.together",
|
||||
ApiProvider::OpenaiCodex => "providers.openai_codex",
|
||||
ApiProvider::Zai => "providers.zai",
|
||||
ApiProvider::Stepfun => "providers.stepfun",
|
||||
};
|
||||
|
||||
// Parse existing TOML (or start fresh) so we can edit the right table
|
||||
@@ -5607,6 +5679,8 @@ pub fn save_api_key_for(provider: ApiProvider, api_key: &str) -> Result<PathBuf>
|
||||
ApiProvider::Volcengine => "volcengine",
|
||||
ApiProvider::Together => "together",
|
||||
ApiProvider::OpenaiCodex => "openai_codex",
|
||||
ApiProvider::Zai => "zai",
|
||||
ApiProvider::Stepfun => "stepfun",
|
||||
};
|
||||
let entry = providers
|
||||
.entry(key_inside.to_string())
|
||||
@@ -5705,6 +5779,8 @@ fn provider_config_key(provider: ApiProvider) -> Result<&'static str> {
|
||||
ApiProvider::Ollama => Ok("ollama"),
|
||||
ApiProvider::Together => Ok("together"),
|
||||
ApiProvider::OpenaiCodex => Ok("openai_codex"),
|
||||
ApiProvider::Zai => Ok("zai"),
|
||||
ApiProvider::Stepfun => Ok("stepfun"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,8 @@ fn provider_base_url_table_key(provider: ApiProvider) -> anyhow::Result<&'static
|
||||
ApiProvider::Ollama => Ok("ollama"),
|
||||
ApiProvider::Together => Ok("together"),
|
||||
ApiProvider::OpenaiCodex => Ok("openai_codex"),
|
||||
ApiProvider::Zai => Ok("zai"),
|
||||
ApiProvider::Stepfun => Ok("stepfun"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -614,7 +614,7 @@ impl Engine {
|
||||
let env_var = match provider {
|
||||
ApiProvider::Deepseek | ApiProvider::DeepseekCN => "DEEPSEEK_API_KEY",
|
||||
ApiProvider::NvidiaNim => "NVIDIA_API_KEY/NVIDIA_NIM_API_KEY",
|
||||
ApiProvider::Openai => "OPENAI_API_KEY",
|
||||
ApiProvider::Openai | ApiProvider::Zai | ApiProvider::Stepfun => "OPENAI_API_KEY",
|
||||
ApiProvider::Anthropic => "ANTHROPIC_API_KEY",
|
||||
ApiProvider::Atlascloud => "ATLASCLOUD_API_KEY",
|
||||
ApiProvider::WanjieArk => "WANJIE_ARK_API_KEY/WANJIE_API_KEY/WANJIE_MAAS_API_KEY",
|
||||
|
||||
@@ -2600,6 +2600,14 @@ fn run_setup_status(config: &Config, workspace: &Path) -> Result<()> {
|
||||
crate::config::ApiProvider::Deepseek | crate::config::ApiProvider::DeepseekCN => {
|
||||
("DEEPSEEK_API_KEY", "codewhale auth set --provider deepseek")
|
||||
}
|
||||
crate::config::ApiProvider::Zai => (
|
||||
"OPENAI_API_KEY",
|
||||
"codewhale auth set --provider zai --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Stepfun => (
|
||||
"OPENAI_API_KEY",
|
||||
"codewhale auth set --provider stepfun --api-key \"...\"",
|
||||
),
|
||||
};
|
||||
println!(
|
||||
" {} api_key: missing (set {env_var} or `[providers.{}].api_key` in ~/.codewhale/config.toml; or run `{login_hint}`)",
|
||||
@@ -2627,6 +2635,8 @@ fn run_setup_status(config: &Config, workspace: &Path) -> Result<()> {
|
||||
crate::config::ApiProvider::OpenaiCodex => "openai_codex",
|
||||
crate::config::ApiProvider::Deepseek
|
||||
| crate::config::ApiProvider::DeepseekCN => "deepseek",
|
||||
crate::config::ApiProvider::Zai => "zai",
|
||||
crate::config::ApiProvider::Stepfun => "stepfun",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ impl ProviderPickerView {
|
||||
ApiProvider::Huggingface => "HUGGINGFACE_API_KEY / HF_TOKEN",
|
||||
ApiProvider::Together => "TOGETHER_API_KEY",
|
||||
ApiProvider::OpenaiCodex => "OPENAI_CODEX_ACCESS_TOKEN / CODEX_ACCESS_TOKEN",
|
||||
ApiProvider::Zai | ApiProvider::Stepfun => "OPENAI_API_KEY",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,7 +515,9 @@ mod tests {
|
||||
"Hugging Face",
|
||||
"Together AI",
|
||||
"OpenAI Codex (ChatGPT)",
|
||||
"Anthropic"
|
||||
"Anthropic",
|
||||
"Z.ai (GLM Coding)",
|
||||
"StepFun / StepFlash"
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -549,7 +552,7 @@ mod tests {
|
||||
let mut picker = ProviderPickerView::new(ApiProvider::Deepseek, &config);
|
||||
|
||||
picker.handle_key(key(KeyCode::Up));
|
||||
assert_eq!(picker.selected_provider(), ApiProvider::Anthropic);
|
||||
assert_eq!(picker.selected_provider(), ApiProvider::Stepfun);
|
||||
|
||||
picker.handle_key(key(KeyCode::Down));
|
||||
assert_eq!(picker.selected_provider(), ApiProvider::Deepseek);
|
||||
|
||||
@@ -7593,6 +7593,8 @@ fn render(f: &mut Frame, app: &mut App) {
|
||||
crate::config::ApiProvider::Huggingface => Some("HF"),
|
||||
crate::config::ApiProvider::Together => Some("Together"),
|
||||
crate::config::ApiProvider::OpenaiCodex => Some("Codex"),
|
||||
crate::config::ApiProvider::Zai => Some("Z.ai"),
|
||||
crate::config::ApiProvider::Stepfun => Some("StepFun"),
|
||||
};
|
||||
let status_indicator_started_at = if app.low_motion {
|
||||
None
|
||||
@@ -8651,6 +8653,8 @@ async fn apply_provider_picker_api_key(
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::OpenaiCodex => &mut providers.openai_codex,
|
||||
ApiProvider::Anthropic => &mut providers.anthropic,
|
||||
ApiProvider::Zai => &mut providers.zai,
|
||||
ApiProvider::Stepfun => &mut providers.stepfun,
|
||||
};
|
||||
entry.api_key = Some(api_key);
|
||||
}
|
||||
@@ -8711,6 +8715,8 @@ fn set_provider_auth_mode_in_memory(config: &mut Config, provider: ApiProvider,
|
||||
ApiProvider::Together => &mut providers.together,
|
||||
ApiProvider::OpenaiCodex => &mut providers.openai_codex,
|
||||
ApiProvider::Anthropic => &mut providers.anthropic,
|
||||
ApiProvider::Zai => &mut providers.zai,
|
||||
ApiProvider::Stepfun => &mut providers.stepfun,
|
||||
};
|
||||
entry.auth_mode = Some(auth_mode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user