Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# toggle without having to re-enter keys. Top-level `api_key` / `base_url` are
# still read as DeepSeek defaults when `[providers.deepseek]` is absent
# (backward compatibility).
provider = "deepseek" # deepseek | deepseek-cn | nvidia-nim | openai | atlascloud | wanjie-ark | openrouter | novita | fireworks | sglang | vllm | ollama
provider = "deepseek" # deepseek | deepseek-cn | nvidia-nim | openai | atlascloud | wanjie-ark | openrouter | novita | fireworks | sglang | vllm | ollama | siliconflow
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)
Expand Down Expand Up @@ -168,6 +168,7 @@ max_subagents = 10 # optional (1-20)
# SGLang: SGLANG_BASE_URL, SGLANG_MODEL, optional SGLANG_API_KEY
# vLLM: VLLM_BASE_URL, VLLM_MODEL, optional VLLM_API_KEY
# Ollama: OLLAMA_BASE_URL, OLLAMA_MODEL, optional OLLAMA_API_KEY
# SiliconFlow: SILICONFLOW_API_KEY, SILICONFLOW_BASE_URL, SILICONFLOW_MODEL

# DeepSeek Platform (https://platform.deepseek.com)
[providers.deepseek]
Expand Down Expand Up @@ -227,6 +228,12 @@ max_subagents = 10 # optional (1-20)
# base_url = "http://localhost:11434/v1"
# model = "deepseek-coder:1.3b" # or any local Ollama tag

# SiliconFlow (https://siliconflow.com)
[providers.siliconflow]
# api_key = "YOUR_SILICONFLOW_API_KEY"
# base_url = "https://api.siliconflow.com/v1"
# model = "deepseek-ai/DeepSeek-V4-Pro"

# ─────────────────────────────────────────────────────────────────────────────────
# Web Search Provider
# ─────────────────────────────────────────────────────────────────────────────────
Expand Down
5 changes: 4 additions & 1 deletion crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,11 +694,12 @@ fn provider_slot(provider: ProviderKind) -> &'static str {
ProviderKind::Sglang => "sglang",
ProviderKind::Vllm => "vllm",
ProviderKind::Ollama => "ollama",
ProviderKind::SiliconFlow => "siliconflow",
}
}

/// Provider order used by the `auth list` and `auth status` outputs.
const PROVIDER_LIST: [ProviderKind; 11] = [
const PROVIDER_LIST: [ProviderKind; 12] = [
ProviderKind::Deepseek,
ProviderKind::NvidiaNim,
ProviderKind::Openai,
Expand All @@ -710,6 +711,7 @@ const PROVIDER_LIST: [ProviderKind; 11] = [
ProviderKind::Sglang,
ProviderKind::Vllm,
ProviderKind::Ollama,
ProviderKind::SiliconFlow,
];

#[cfg(test)]
Expand Down Expand Up @@ -764,6 +766,7 @@ fn provider_env_vars(provider: ProviderKind) -> &'static [&'static str] {
ProviderKind::Sglang => &["SGLANG_API_KEY"],
ProviderKind::Vllm => &["VLLM_API_KEY"],
ProviderKind::Ollama => &["OLLAMA_API_KEY"],
ProviderKind::SiliconFlow => &["SILICONFLOW_API_KEY"],
ProviderKind::Openai => &["OPENAI_API_KEY"],
ProviderKind::Atlascloud => &["ATLASCLOUD_API_KEY"],
ProviderKind::WanjieArk => &[
Expand Down
63 changes: 62 additions & 1 deletion crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const DEFAULT_VLLM_FLASH_MODEL: &str = "deepseek-ai/DeepSeek-V4-Flash";
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";
const DEFAULT_SILICONFLOW_MODEL: &str = "deepseek-ai/DeepSeek-V4-Pro";
const DEFAULT_SILICONFLOW_BASE_URL: &str = "https://api.siliconflow.com/v1";

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case")]
Expand Down Expand Up @@ -71,6 +73,7 @@ pub enum ProviderKind {
Sglang,
Vllm,
Ollama,
SiliconFlow,
}

impl ProviderKind {
Expand All @@ -88,6 +91,7 @@ impl ProviderKind {
Self::Sglang => "sglang",
Self::Vllm => "vllm",
Self::Ollama => "ollama",
Self::SiliconFlow => "siliconflow",
}
}

Expand All @@ -107,6 +111,7 @@ impl ProviderKind {
"sglang" | "sg-lang" => Some(Self::Sglang),
"vllm" | "v-llm" => Some(Self::Vllm),
"ollama" | "ollama-local" => Some(Self::Ollama),
"siliconflow" | "sf" => Some(Self::SiliconFlow),
_ => None,
}
}
Expand Down Expand Up @@ -145,6 +150,8 @@ pub struct ProvidersToml {
pub vllm: ProviderConfigToml,
#[serde(default)]
pub ollama: ProviderConfigToml,
#[serde(default)]
pub siliconflow: ProviderConfigToml,
}

impl ProvidersToml {
Expand All @@ -162,6 +169,7 @@ impl ProvidersToml {
ProviderKind::Sglang => &self.sglang,
ProviderKind::Vllm => &self.vllm,
ProviderKind::Ollama => &self.ollama,
ProviderKind::SiliconFlow => &self.siliconflow,
}
}

Expand All @@ -178,6 +186,7 @@ impl ProvidersToml {
ProviderKind::Sglang => &mut self.sglang,
ProviderKind::Vllm => &mut self.vllm,
ProviderKind::Ollama => &mut self.ollama,
ProviderKind::SiliconFlow => &mut self.siliconflow,
}
}
}
Expand Down Expand Up @@ -400,6 +409,10 @@ impl ConfigToml {
merge_provider_config(&mut self.providers.sglang, &project.providers.sglang);
merge_provider_config(&mut self.providers.vllm, &project.providers.vllm);
merge_provider_config(&mut self.providers.ollama, &project.providers.ollama);
merge_provider_config(
&mut self.providers.siliconflow,
&project.providers.siliconflow,
);

if project.network.is_some() {
self.network = project.network;
Expand Down Expand Up @@ -501,6 +514,12 @@ impl ConfigToml {
"providers.ollama.http_headers" => {
serialize_http_headers(&self.providers.ollama.http_headers)
}
"providers.siliconflow.api_key" => self.providers.siliconflow.api_key.clone(),
"providers.siliconflow.base_url" => self.providers.siliconflow.base_url.clone(),
"providers.siliconflow.model" => self.providers.siliconflow.model.clone(),
"providers.siliconflow.http_headers" => {
serialize_http_headers(&self.providers.siliconflow.http_headers)
}
_ => self.extras.get(key).map(toml::Value::to_string),
}
}
Expand Down Expand Up @@ -671,6 +690,18 @@ impl ConfigToml {
"providers.ollama.http_headers" => {
self.providers.ollama.http_headers = parse_http_headers(value)?;
}
"providers.siliconflow.api_key" => {
self.providers.siliconflow.api_key = Some(value.to_string());
}
"providers.siliconflow.base_url" => {
self.providers.siliconflow.base_url = Some(value.to_string());
}
"providers.siliconflow.model" => {
self.providers.siliconflow.model = Some(value.to_string());
}
"providers.siliconflow.http_headers" => {
self.providers.siliconflow.http_headers = parse_http_headers(value)?;
}
_ => {
self.extras
.insert(key.to_string(), toml::Value::String(value.to_string()));
Expand Down Expand Up @@ -753,6 +784,10 @@ impl ConfigToml {
"providers.ollama.base_url" => self.providers.ollama.base_url = None,
"providers.ollama.model" => self.providers.ollama.model = None,
"providers.ollama.http_headers" => self.providers.ollama.http_headers.clear(),
"providers.siliconflow.api_key" => self.providers.siliconflow.api_key = None,
"providers.siliconflow.base_url" => self.providers.siliconflow.base_url = None,
"providers.siliconflow.model" => self.providers.siliconflow.model = None,
"providers.siliconflow.http_headers" => self.providers.siliconflow.http_headers.clear(),
_ => {
self.extras.remove(key);
}
Expand Down Expand Up @@ -936,6 +971,21 @@ impl ConfigToml {
if let Some(v) = serialize_http_headers(&self.providers.ollama.http_headers) {
out.insert("providers.ollama.http_headers".to_string(), v);
}
if let Some(v) = self.providers.siliconflow.api_key.as_ref() {
out.insert(
"providers.siliconflow.api_key".to_string(),
redact_secret(v),
);
}
if let Some(v) = self.providers.siliconflow.base_url.as_ref() {
out.insert("providers.siliconflow.base_url".to_string(), v.clone());
}
if let Some(v) = self.providers.siliconflow.model.as_ref() {
out.insert("providers.siliconflow.model".to_string(), v.clone());
}
if let Some(v) = serialize_http_headers(&self.providers.siliconflow.http_headers) {
out.insert("providers.siliconflow.http_headers".to_string(), v);
}

for (k, v) in &self.extras {
out.insert(k.clone(), v.to_string());
Expand Down Expand Up @@ -997,6 +1047,7 @@ impl ConfigToml {
ProviderKind::Sglang => DEFAULT_SGLANG_BASE_URL.to_string(),
ProviderKind::Vllm => DEFAULT_VLLM_BASE_URL.to_string(),
ProviderKind::Ollama => DEFAULT_OLLAMA_BASE_URL.to_string(),
ProviderKind::SiliconFlow => DEFAULT_SILICONFLOW_BASE_URL.to_string(),
});
let auth_mode = cli
.auth_mode
Expand Down Expand Up @@ -1134,7 +1185,10 @@ pub fn load_project_config(workspace: &Path) -> Option<ConfigToml> {
fn normalize_model_for_provider(provider: ProviderKind, model: &str) -> String {
if matches!(
provider,
ProviderKind::Atlascloud | ProviderKind::WanjieArk | ProviderKind::Ollama
ProviderKind::Atlascloud
| ProviderKind::WanjieArk
| ProviderKind::Ollama
| ProviderKind::SiliconFlow
) {
return model.to_string();
}
Expand Down Expand Up @@ -1201,6 +1255,7 @@ fn default_model_for_provider(provider: ProviderKind) -> &'static str {
ProviderKind::Sglang => DEFAULT_SGLANG_MODEL,
ProviderKind::Vllm => DEFAULT_VLLM_MODEL,
ProviderKind::Ollama => DEFAULT_OLLAMA_MODEL,
ProviderKind::SiliconFlow => DEFAULT_SILICONFLOW_MODEL,
}
}

Expand All @@ -1217,6 +1272,7 @@ fn default_base_url_for_provider(provider: ProviderKind) -> &'static str {
ProviderKind::Sglang => DEFAULT_SGLANG_BASE_URL,
ProviderKind::Vllm => DEFAULT_VLLM_BASE_URL,
ProviderKind::Ollama => DEFAULT_OLLAMA_BASE_URL,
ProviderKind::SiliconFlow => DEFAULT_SILICONFLOW_BASE_URL,
}
}

Expand Down Expand Up @@ -1577,6 +1633,7 @@ struct EnvRuntimeOverrides {
sglang_base_url: Option<String>,
vllm_base_url: Option<String>,
ollama_base_url: Option<String>,
siliconflow_base_url: Option<String>,
}

impl EnvRuntimeOverrides {
Expand Down Expand Up @@ -1643,6 +1700,9 @@ impl EnvRuntimeOverrides {
ollama_base_url: std::env::var("OLLAMA_BASE_URL")
.ok()
.filter(|v| !v.trim().is_empty()),
siliconflow_base_url: std::env::var("SILICONFLOW_BASE_URL")
.ok()
.filter(|v| !v.trim().is_empty()),
}
}

Expand All @@ -1661,6 +1721,7 @@ impl EnvRuntimeOverrides {
ProviderKind::Sglang => self.sglang_base_url.clone(),
ProviderKind::Vllm => self.vllm_base_url.clone(),
ProviderKind::Ollama => self.ollama_base_url.clone(),
ProviderKind::SiliconFlow => self.siliconflow_base_url.clone(),
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/secrets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ pub fn env_for(name: &str) -> Option<String> {
"WANJIE_API_KEY",
"WANJIE_MAAS_API_KEY",
],
"siliconflow" | "sf" => &["SILICONFLOW_API_KEY"],
_ => return None,
};
for var in candidates {
Expand Down
9 changes: 6 additions & 3 deletions crates/tui/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,8 @@ pub(super) fn apply_reasoning_effort(
| ApiProvider::DeepseekCN
| ApiProvider::Openrouter
| ApiProvider::Novita
| ApiProvider::Sglang => {
| ApiProvider::Sglang
| ApiProvider::SiliconFlow => {
body["thinking"] = json!({ "type": "disabled" });
}
ApiProvider::Fireworks => {}
Expand Down Expand Up @@ -920,7 +921,8 @@ pub(super) fn apply_reasoning_effort(
| ApiProvider::DeepseekCN
| ApiProvider::Openrouter
| ApiProvider::Novita
| ApiProvider::Sglang => {
| ApiProvider::Sglang
| ApiProvider::SiliconFlow => {
body["reasoning_effort"] = json!("high");
body["thinking"] = json!({ "type": "enabled" });
}
Expand Down Expand Up @@ -949,7 +951,8 @@ pub(super) fn apply_reasoning_effort(
| ApiProvider::DeepseekCN
| ApiProvider::Openrouter
| ApiProvider::Novita
| ApiProvider::Sglang => {
| ApiProvider::Sglang
| ApiProvider::SiliconFlow => {
body["reasoning_effort"] = json!("max");
body["thinking"] = json!({ "type": "enabled" });
}
Expand Down
1 change: 1 addition & 0 deletions crates/tui/src/client/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1669,6 +1669,7 @@ fn provider_accepts_reasoning_content(provider: ApiProvider) -> bool {
| ApiProvider::Novita
| ApiProvider::Fireworks
| ApiProvider::Sglang
| ApiProvider::SiliconFlow
)
}

Expand Down
Loading