diff --git a/docs/commands-reference.md b/docs/commands-reference.md index 5622fdcf7..10cb24bae 100644 --- a/docs/commands-reference.md +++ b/docs/commands-reference.md @@ -2,7 +2,7 @@ This reference is derived from the current CLI surface (`zeroclaw --help`). -Last verified: **February 25, 2026**. +Last verified: **February 28, 2026**. ## Top-Level Commands @@ -116,7 +116,7 @@ Notes: - `zeroclaw models refresh --provider ` - `zeroclaw models refresh --force` -`models refresh` currently supports live catalog refresh for provider IDs: `openrouter`, `openai`, `anthropic`, `groq`, `mistral`, `deepseek`, `xai`, `together-ai`, `gemini`, `ollama`, `llamacpp`, `sglang`, `vllm`, `astrai`, `venice`, `fireworks`, `cohere`, `moonshot`, `glm`, `zai`, `qwen`, and `nvidia`. +`models refresh` currently supports live catalog refresh for provider IDs: `openrouter`, `openai`, `anthropic`, `groq`, `mistral`, `deepseek`, `xai`, `together-ai`, `gemini`, `ollama`, `llamacpp`, `sglang`, `vllm`, `astrai`, `venice`, `fireworks`, `cohere`, `moonshot`, `glm`, `zai`, `qwen`, `volcengine` (`doubao`/`ark` aliases), `siliconflow`, and `nvidia`. ### `doctor` diff --git a/docs/i18n/vi/commands-reference.md b/docs/i18n/vi/commands-reference.md index fa6fc7986..8ef5e598a 100644 --- a/docs/i18n/vi/commands-reference.md +++ b/docs/i18n/vi/commands-reference.md @@ -2,7 +2,7 @@ Dựa trên CLI hiện tại (`zeroclaw --help`). -Xác minh lần cuối: **2026-02-20**. +Xác minh lần cuối: **2026-02-28**. ## Lệnh cấp cao nhất @@ -77,7 +77,7 @@ Xác minh lần cuối: **2026-02-20**. - `zeroclaw models refresh --provider ` - `zeroclaw models refresh --force` -`models refresh` hiện hỗ trợ làm mới danh mục trực tiếp cho các provider: `openrouter`, `openai`, `anthropic`, `groq`, `mistral`, `deepseek`, `xai`, `together-ai`, `gemini`, `ollama`, `astrai`, `venice`, `fireworks`, `cohere`, `moonshot`, `glm`, `zai`, `qwen` và `nvidia`. +`models refresh` hiện hỗ trợ làm mới danh mục trực tiếp cho các provider: `openrouter`, `openai`, `anthropic`, `groq`, `mistral`, `deepseek`, `xai`, `together-ai`, `gemini`, `ollama`, `llamacpp`, `sglang`, `vllm`, `astrai`, `venice`, `fireworks`, `cohere`, `moonshot`, `glm`, `zai`, `qwen`, `volcengine` (alias `doubao`/`ark`), `siliconflow` và `nvidia`. ### `channel` diff --git a/docs/i18n/vi/providers-reference.md b/docs/i18n/vi/providers-reference.md index f4b18cb2d..32b347644 100644 --- a/docs/i18n/vi/providers-reference.md +++ b/docs/i18n/vi/providers-reference.md @@ -2,7 +2,7 @@ Tài liệu này liệt kê các provider ID, alias và biến môi trường chứa thông tin xác thực. -Cập nhật lần cuối: **2026-02-19**. +Cập nhật lần cuối: **2026-02-28**. ## Cách liệt kê các Provider @@ -41,6 +41,8 @@ Với chuỗi provider dự phòng (`reliability.fallback_providers`), mỗi pro | `minimax` | `minimax-intl`, `minimax-io`, `minimax-global`, `minimax-cn`, `minimaxi`, `minimax-oauth`, `minimax-oauth-cn`, `minimax-portal`, `minimax-portal-cn` | Không | `MINIMAX_OAUTH_TOKEN`, `MINIMAX_API_KEY` | | `bedrock` | `aws-bedrock` | Không | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` (tùy chọn: `AWS_REGION`) | | `qianfan` | `baidu` | Không | `QIANFAN_API_KEY` | +| `doubao` | `volcengine`, `ark`, `doubao-cn` | Không | `ARK_API_KEY`, `DOUBAO_API_KEY` | +| `siliconflow` | `silicon-cloud`, `siliconcloud` | Không | `SILICONFLOW_API_KEY` | | `qwen` | `dashscope`, `qwen-intl`, `dashscope-intl`, `qwen-us`, `dashscope-us`, `qwen-code`, `qwen-oauth`, `qwen_oauth` | Không | `QWEN_OAUTH_TOKEN`, `DASHSCOPE_API_KEY` | | `groq` | — | Không | `GROQ_API_KEY` | | `mistral` | — | Không | `MISTRAL_API_KEY` | @@ -61,6 +63,53 @@ Với chuỗi provider dự phòng (`reliability.fallback_providers`), mỗi pro - Request bằng API key dùng endpoint `generativelanguage.googleapis.com/v1beta` - Request OAuth qua Gemini CLI dùng endpoint `cloudcode-pa.googleapis.com/v1internal` theo chuẩn Code Assist request envelope +### Ghi chú về Volcengine ARK (Doubao) + +- Runtime provider ID: `doubao` (alias: `volcengine`, `ark`, `doubao-cn`) +- Tên hiển thị/canonical trong onboarding: `volcengine` +- Base API URL: `https://ark.cn-beijing.volces.com/api/v3` +- Chat endpoint: `/chat/completions` +- Model discovery endpoint: `/models` +- Xác thực: `ARK_API_KEY` (fallback: `DOUBAO_API_KEY`) +- Model mặc định: `doubao-1-5-pro-32k-250115` + +Ví dụ thiết lập nhanh: + +```bash +export ARK_API_KEY="your-ark-api-key" +zeroclaw onboard --provider volcengine --api-key "$ARK_API_KEY" --model doubao-1-5-pro-32k-250115 --force +``` + +Kiểm tra nhanh: + +```bash +zeroclaw models refresh --provider volcengine +zeroclaw agent --provider volcengine --model doubao-1-5-pro-32k-250115 -m "ping" +``` + +### Ghi chú về SiliconFlow + +- Provider ID: `siliconflow` (alias: `silicon-cloud`, `siliconcloud`) +- Base API URL: `https://api.siliconflow.cn/v1` +- Chat endpoint: `/chat/completions` +- Model discovery endpoint: `/models` +- Xác thực: `SILICONFLOW_API_KEY` +- Model mặc định: `Pro/zai-org/GLM-4.7` + +Ví dụ thiết lập nhanh: + +```bash +export SILICONFLOW_API_KEY="your-siliconflow-api-key" +zeroclaw onboard --provider siliconflow --api-key "$SILICONFLOW_API_KEY" --model Pro/zai-org/GLM-4.7 --force +``` + +Kiểm tra nhanh: + +```bash +zeroclaw models refresh --provider siliconflow +zeroclaw agent --provider siliconflow --model Pro/zai-org/GLM-4.7 -m "ping" +``` + ### Ghi chú về Ollama Vision - Provider ID: `ollama` diff --git a/docs/providers-reference.md b/docs/providers-reference.md index f8b47c677..deed2423a 100644 --- a/docs/providers-reference.md +++ b/docs/providers-reference.md @@ -2,7 +2,7 @@ This document maps provider IDs, aliases, and credential environment variables. -Last verified: **February 24, 2026**. +Last verified: **February 28, 2026**. ## How to List Providers @@ -44,6 +44,7 @@ credential is not reused for fallback providers. | `bedrock` | `aws-bedrock` | No | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` (optional: `AWS_REGION`) | | `qianfan` | `baidu` | No | `QIANFAN_API_KEY` | | `doubao` | `volcengine`, `ark`, `doubao-cn` | No | `ARK_API_KEY`, `DOUBAO_API_KEY` | +| `siliconflow` | `silicon-cloud`, `siliconcloud` | No | `SILICONFLOW_API_KEY` | | `hunyuan` | `tencent` | No | `HUNYUAN_API_KEY` | | `qwen` | `dashscope`, `qwen-intl`, `dashscope-intl`, `qwen-us`, `dashscope-us`, `qwen-code`, `qwen-oauth`, `qwen_oauth` | No | `QWEN_OAUTH_TOKEN`, `DASHSCOPE_API_KEY` | | `groq` | — | No | `GROQ_API_KEY` | @@ -101,6 +102,53 @@ credential is not reused for fallback providers. - OAuth free tier limited to 1 model and 1000 requests/day - See test report: `docs/qwen-provider-test-report.md` +### Volcengine ARK (Doubao) Notes + +- Runtime provider ID: `doubao` (aliases: `volcengine`, `ark`, `doubao-cn`) +- Onboarding display/canonical name: `volcengine` +- Base API URL: `https://ark.cn-beijing.volces.com/api/v3` +- Chat endpoint: `/chat/completions` +- Model discovery endpoint: `/models` +- Authentication: `ARK_API_KEY` (fallback: `DOUBAO_API_KEY`) +- Default model preset: `doubao-1-5-pro-32k-250115` + +Minimal setup example: + +```bash +export ARK_API_KEY="your-ark-api-key" +zeroclaw onboard --provider volcengine --api-key "$ARK_API_KEY" --model doubao-1-5-pro-32k-250115 --force +``` + +Quick validation: + +```bash +zeroclaw models refresh --provider volcengine +zeroclaw agent --provider volcengine --model doubao-1-5-pro-32k-250115 -m "ping" +``` + +### SiliconFlow Notes + +- Provider ID: `siliconflow` (aliases: `silicon-cloud`, `siliconcloud`) +- Base API URL: `https://api.siliconflow.cn/v1` +- Chat endpoint: `/chat/completions` +- Model discovery endpoint: `/models` +- Authentication: `SILICONFLOW_API_KEY` +- Default model preset: `Pro/zai-org/GLM-4.7` + +Minimal setup example: + +```bash +export SILICONFLOW_API_KEY="your-siliconflow-api-key" +zeroclaw onboard --provider siliconflow --api-key "$SILICONFLOW_API_KEY" --model Pro/zai-org/GLM-4.7 --force +``` + +Quick validation: + +```bash +zeroclaw models refresh --provider siliconflow +zeroclaw agent --provider siliconflow --model Pro/zai-org/GLM-4.7 -m "ping" +``` + ### Ollama Vision Notes - Provider ID: `ollama` diff --git a/src/integrations/registry.rs b/src/integrations/registry.rs index b3df9e531..959dd3ddd 100644 --- a/src/integrations/registry.rs +++ b/src/integrations/registry.rs @@ -1,7 +1,7 @@ use super::{IntegrationCategory, IntegrationEntry, IntegrationStatus}; use crate::providers::{ - is_glm_alias, is_minimax_alias, is_moonshot_alias, is_qianfan_alias, is_qwen_alias, - is_zai_alias, + is_doubao_alias, is_glm_alias, is_minimax_alias, is_moonshot_alias, is_qianfan_alias, + is_qwen_alias, is_siliconflow_alias, is_zai_alias, }; /// Returns the full catalog of integrations @@ -436,6 +436,33 @@ pub fn all_integrations() -> Vec { } }, }, + IntegrationEntry { + name: "Volcengine ARK", + description: "Doubao and ARK model catalog", + category: IntegrationCategory::AiModel, + status_fn: |c| { + if c.default_provider.as_deref().is_some_and(is_doubao_alias) { + IntegrationStatus::Active + } else { + IntegrationStatus::Available + } + }, + }, + IntegrationEntry { + name: "SiliconFlow", + description: "OpenAI-compatible hosted models and reasoning", + category: IntegrationCategory::AiModel, + status_fn: |c| { + if c.default_provider + .as_deref() + .is_some_and(is_siliconflow_alias) + { + IntegrationStatus::Active + } else { + IntegrationStatus::Available + } + }, + }, IntegrationEntry { name: "Groq", description: "Llama 3.3 70B Versatile and low-latency models", @@ -991,5 +1018,31 @@ mod tests { (qianfan.status_fn)(&config), IntegrationStatus::Active )); + + config.default_provider = Some("ark".to_string()); + let volcengine = entries.iter().find(|e| e.name == "Volcengine ARK").unwrap(); + assert!(matches!( + (volcengine.status_fn)(&config), + IntegrationStatus::Active + )); + + config.default_provider = Some("volcengine".to_string()); + assert!(matches!( + (volcengine.status_fn)(&config), + IntegrationStatus::Active + )); + + config.default_provider = Some("siliconflow".to_string()); + let siliconflow = entries.iter().find(|e| e.name == "SiliconFlow").unwrap(); + assert!(matches!( + (siliconflow.status_fn)(&config), + IntegrationStatus::Active + )); + + config.default_provider = Some("silicon-cloud".to_string()); + assert!(matches!( + (siliconflow.status_fn)(&config), + IntegrationStatus::Active + )); } } diff --git a/src/onboard/wizard.rs b/src/onboard/wizard.rs index 8c5534d83..2d1d2f099 100644 --- a/src/onboard/wizard.rs +++ b/src/onboard/wizard.rs @@ -18,9 +18,9 @@ use crate::memory::{ selectable_memory_backends, MemoryBackendKind, }; use crate::providers::{ - canonical_china_provider_name, is_glm_alias, is_glm_cn_alias, is_minimax_alias, - is_moonshot_alias, is_qianfan_alias, is_qwen_alias, is_qwen_oauth_alias, is_zai_alias, - is_zai_cn_alias, + canonical_china_provider_name, is_doubao_alias, is_glm_alias, is_glm_cn_alias, + is_minimax_alias, is_moonshot_alias, is_qianfan_alias, is_qwen_alias, is_qwen_oauth_alias, + is_siliconflow_alias, is_zai_alias, is_zai_cn_alias, }; use anyhow::{bail, Context, Result}; use console::style; @@ -710,6 +710,9 @@ fn canonical_provider_name(provider_name: &str) -> &str { } if let Some(canonical) = canonical_china_provider_name(provider_name) { + if canonical == "doubao" { + return "volcengine"; + } return canonical; } @@ -775,6 +778,8 @@ fn default_model_for_provider(provider: &str) -> String { "glm" | "zai" => "glm-5".into(), "minimax" => "MiniMax-M2.5".into(), "qwen" => "qwen-plus".into(), + "volcengine" => "doubao-1-5-pro-32k-250115".into(), + "siliconflow" => "Pro/zai-org/GLM-4.7".into(), "qwen-code" => "qwen3-coder-plus".into(), "ollama" => "llama3.2".into(), "llamacpp" => "ggml-org/gpt-oss-20b-GGUF".into(), @@ -1089,6 +1094,31 @@ fn curated_models_for_provider(provider_name: &str) -> Vec<(String, String)> { "Qwen Turbo (fast and cost-efficient)".to_string(), ), ], + "volcengine" => vec![ + ( + "doubao-1-5-pro-32k-250115".to_string(), + "Doubao 1.5 Pro 32K (official sample model)".to_string(), + ), + ( + "doubao-seed-1-6-250615".to_string(), + "Doubao Seed 1.6 (reasoning flagship)".to_string(), + ), + ( + "deepseek-v3.2".to_string(), + "DeepSeek V3.2 (available in ARK catalog)".to_string(), + ), + ], + "siliconflow" => vec![ + ( + "Pro/zai-org/GLM-4.7".to_string(), + "GLM-4.7 Pro (official API example)".to_string(), + ), + ( + "Pro/deepseek-ai/DeepSeek-V3.2".to_string(), + "DeepSeek V3.2 Pro".to_string(), + ), + ("Qwen/Qwen3-32B".to_string(), "Qwen3 32B".to_string()), + ], "qwen-code" => vec![ ( "qwen3-coder-plus".to_string(), @@ -1265,6 +1295,8 @@ fn supports_live_model_fetch(provider_name: &str) -> bool { | "glm" | "zai" | "qwen" + | "volcengine" + | "siliconflow" | "nvidia" ) } @@ -1277,6 +1309,9 @@ fn models_endpoint_for_provider(provider_name: &str) -> Option<&'static str> { "moonshot-cn" | "kimi-cn" => Some("https://api.moonshot.cn/v1/models"), "glm-cn" | "bigmodel" => Some("https://open.bigmodel.cn/api/paas/v4/models"), "zai-cn" | "z.ai-cn" => Some("https://open.bigmodel.cn/api/coding/paas/v4/models"), + "volcengine" | "ark" | "doubao" | "doubao-cn" => { + Some("https://ark.cn-beijing.volces.com/api/v3/models") + } _ => match canonical_provider_name(provider_name) { "openai-codex" | "openai" => Some("https://api.openai.com/v1/models"), "venice" => Some("https://api.venice.ai/api/v1/models"), @@ -1292,6 +1327,7 @@ fn models_endpoint_for_provider(provider_name: &str) -> Option<&'static str> { "glm" => Some("https://api.z.ai/api/paas/v4/models"), "zai" => Some("https://api.z.ai/api/coding/paas/v4/models"), "qwen" => Some("https://dashscope.aliyuncs.com/compatible-mode/v1/models"), + "siliconflow" => Some("https://api.siliconflow.cn/v1/models"), "nvidia" => Some("https://integrate.api.nvidia.com/v1/models"), "astrai" => Some("https://as-trai.com/v1/models"), "llamacpp" => Some("http://localhost:8080/v1/models"), @@ -2304,6 +2340,11 @@ async fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, ("qwen-us", "Qwen — DashScope US endpoint"), ("hunyuan", "Hunyuan — Tencent large models (T1, Turbo, Pro)"), ("qianfan", "Qianfan — Baidu AI models (China endpoint)"), + ("volcengine", "Volcengine ARK — Doubao model family"), + ( + "siliconflow", + "SiliconFlow — OpenAI-compatible hosted models", + ), ("zai", "Z.AI — global coding endpoint"), ("zai-cn", "Z.AI — China coding endpoint (open.bigmodel.cn)"), ("synthetic", "Synthetic — Synthetic AI models"), @@ -2698,6 +2739,10 @@ async fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, "https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key" } else if is_qianfan_alias(provider_name) { "https://cloud.baidu.com/doc/WENXINWORKSHOP/s/7lm0vxo78" + } else if is_doubao_alias(provider_name) { + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey" + } else if is_siliconflow_alias(provider_name) { + "https://cloud.siliconflow.cn/account/ak" } else { match provider_name { "openrouter" => "https://openrouter.ai/keys", @@ -3006,6 +3051,8 @@ fn provider_env_var(name: &str) -> &'static str { "glm" => "GLM_API_KEY", "minimax" => "MINIMAX_API_KEY", "qwen" => "DASHSCOPE_API_KEY", + "volcengine" => "ARK_API_KEY", + "siliconflow" => "SILICONFLOW_API_KEY", "hunyuan" => "HUNYUAN_API_KEY", "qianfan" => "QIANFAN_API_KEY", "zai" => "ZAI_API_KEY", @@ -7306,6 +7353,14 @@ mod tests { assert_eq!(default_model_for_provider("moonshot"), "kimi-k2.5"); assert_eq!(default_model_for_provider("hunyuan"), "hunyuan-t1-latest"); assert_eq!(default_model_for_provider("tencent"), "hunyuan-t1-latest"); + assert_eq!( + default_model_for_provider("siliconflow"), + "Pro/zai-org/GLM-4.7" + ); + assert_eq!( + default_model_for_provider("volcengine"), + "doubao-1-5-pro-32k-250115" + ); assert_eq!( default_model_for_provider("nvidia"), "meta/llama-3.3-70b-instruct" @@ -7344,6 +7399,10 @@ mod tests { assert_eq!(canonical_provider_name("minimax-cn"), "minimax"); assert_eq!(canonical_provider_name("zai-cn"), "zai"); assert_eq!(canonical_provider_name("z.ai-global"), "zai"); + assert_eq!(canonical_provider_name("doubao"), "volcengine"); + assert_eq!(canonical_provider_name("ark"), "volcengine"); + assert_eq!(canonical_provider_name("silicon-cloud"), "siliconflow"); + assert_eq!(canonical_provider_name("siliconcloud"), "siliconflow"); assert_eq!(canonical_provider_name("nvidia-nim"), "nvidia"); assert_eq!(canonical_provider_name("aws-bedrock"), "bedrock"); assert_eq!(canonical_provider_name("build.nvidia.com"), "nvidia"); @@ -7486,6 +7545,23 @@ mod tests { assert!(ids.contains(&"qwen3-max-2026-01-23".to_string())); } + #[test] + fn curated_models_for_volcengine_and_siliconflow_include_expected_defaults() { + let volcengine_ids: Vec = curated_models_for_provider("volcengine") + .into_iter() + .map(|(id, _)| id) + .collect(); + assert!(volcengine_ids.contains(&"doubao-1-5-pro-32k-250115".to_string())); + assert!(volcengine_ids.contains(&"doubao-seed-1-6-250615".to_string())); + + let siliconflow_ids: Vec = curated_models_for_provider("siliconflow") + .into_iter() + .map(|(id, _)| id) + .collect(); + assert!(siliconflow_ids.contains(&"Pro/zai-org/GLM-4.7".to_string())); + assert!(siliconflow_ids.contains(&"Pro/deepseek-ai/DeepSeek-V3.2".to_string())); + } + #[test] fn supports_live_model_fetch_for_supported_and_unsupported_providers() { assert!(supports_live_model_fetch("openai")); @@ -7507,6 +7583,11 @@ mod tests { assert!(supports_live_model_fetch("glm-cn")); assert!(supports_live_model_fetch("qwen-intl")); assert!(supports_live_model_fetch("qwen-coding-plan")); + assert!(supports_live_model_fetch("siliconflow")); + assert!(supports_live_model_fetch("silicon-cloud")); + assert!(supports_live_model_fetch("volcengine")); + assert!(supports_live_model_fetch("doubao")); + assert!(supports_live_model_fetch("ark")); assert!(!supports_live_model_fetch("minimax-cn")); assert!(!supports_live_model_fetch("unknown-provider")); } @@ -7565,6 +7646,22 @@ mod tests { curated_models_for_provider("bedrock"), curated_models_for_provider("aws-bedrock") ); + assert_eq!( + curated_models_for_provider("volcengine"), + curated_models_for_provider("doubao") + ); + assert_eq!( + curated_models_for_provider("volcengine"), + curated_models_for_provider("ark") + ); + assert_eq!( + curated_models_for_provider("siliconflow"), + curated_models_for_provider("silicon-cloud") + ); + assert_eq!( + curated_models_for_provider("siliconflow"), + curated_models_for_provider("siliconcloud") + ); } #[test] @@ -7597,6 +7694,18 @@ mod tests { models_endpoint_for_provider("qwen-coding-plan"), Some("https://coding.dashscope.aliyuncs.com/v1/models") ); + assert_eq!( + models_endpoint_for_provider("volcengine"), + Some("https://ark.cn-beijing.volces.com/api/v3/models") + ); + assert_eq!( + models_endpoint_for_provider("doubao"), + Some("https://ark.cn-beijing.volces.com/api/v3/models") + ); + assert_eq!( + models_endpoint_for_provider("ark"), + Some("https://ark.cn-beijing.volces.com/api/v3/models") + ); } #[test] @@ -7617,6 +7726,14 @@ mod tests { models_endpoint_for_provider("moonshot"), Some("https://api.moonshot.ai/v1/models") ); + assert_eq!( + models_endpoint_for_provider("siliconflow"), + Some("https://api.siliconflow.cn/v1/models") + ); + assert_eq!( + models_endpoint_for_provider("silicon-cloud"), + Some("https://api.siliconflow.cn/v1/models") + ); assert_eq!( models_endpoint_for_provider("llamacpp"), Some("http://localhost:8080/v1/models") @@ -7915,6 +8032,12 @@ mod tests { assert_eq!(provider_env_var("minimax-oauth-cn"), "MINIMAX_API_KEY"); assert_eq!(provider_env_var("moonshot-intl"), "MOONSHOT_API_KEY"); assert_eq!(provider_env_var("zai-cn"), "ZAI_API_KEY"); + assert_eq!(provider_env_var("doubao"), "ARK_API_KEY"); + assert_eq!(provider_env_var("volcengine"), "ARK_API_KEY"); + assert_eq!(provider_env_var("ark"), "ARK_API_KEY"); + assert_eq!(provider_env_var("siliconflow"), "SILICONFLOW_API_KEY"); + assert_eq!(provider_env_var("silicon-cloud"), "SILICONFLOW_API_KEY"); + assert_eq!(provider_env_var("siliconcloud"), "SILICONFLOW_API_KEY"); assert_eq!(provider_env_var("nvidia"), "NVIDIA_API_KEY"); assert_eq!(provider_env_var("nvidia-nim"), "NVIDIA_API_KEY"); // alias assert_eq!(provider_env_var("build.nvidia.com"), "NVIDIA_API_KEY"); // alias diff --git a/src/providers/mod.rs b/src/providers/mod.rs index b9aec3b85..bc698664e 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -74,6 +74,7 @@ const QWEN_OAUTH_DEFAULT_CLIENT_ID: &str = "f0304373b74a44d2b584a3fb70ca9e56"; const QWEN_OAUTH_CREDENTIAL_FILE: &str = ".qwen/oauth_creds.json"; const ZAI_GLOBAL_BASE_URL: &str = "https://api.z.ai/api/coding/paas/v4"; const ZAI_CN_BASE_URL: &str = "https://open.bigmodel.cn/api/coding/paas/v4"; +const SILICONFLOW_BASE_URL: &str = "https://api.siliconflow.cn/v1"; const VERCEL_AI_GATEWAY_BASE_URL: &str = "https://ai-gateway.vercel.sh/v1"; pub(crate) fn is_minimax_intl_alias(name: &str) -> bool { @@ -179,6 +180,10 @@ pub(crate) fn is_doubao_alias(name: &str) -> bool { matches!(name, "doubao" | "volcengine" | "ark" | "doubao-cn") } +pub(crate) fn is_siliconflow_alias(name: &str) -> bool { + matches!(name, "siliconflow" | "silicon-cloud" | "siliconcloud") +} + #[derive(Clone, Copy, Debug)] enum MinimaxOauthRegion { Global, @@ -618,6 +623,8 @@ pub(crate) fn canonical_china_provider_name(name: &str) -> Option<&'static str> Some("qianfan") } else if is_doubao_alias(name) { Some("doubao") + } else if is_siliconflow_alias(name) { + Some("siliconflow") } else if matches!(name, "hunyuan" | "tencent") { Some("hunyuan") } else { @@ -874,6 +881,7 @@ fn resolve_provider_credential(name: &str, credential_override: Option<&str>) -> "hunyuan" | "tencent" => vec!["HUNYUAN_API_KEY"], name if is_qianfan_alias(name) => vec!["QIANFAN_API_KEY"], name if is_doubao_alias(name) => vec!["ARK_API_KEY", "DOUBAO_API_KEY"], + name if is_siliconflow_alias(name) => vec!["SILICONFLOW_API_KEY"], name if is_qwen_alias(name) => vec!["DASHSCOPE_API_KEY"], name if is_zai_alias(name) => vec!["ZAI_API_KEY"], "nvidia" | "nvidia-nim" | "build.nvidia.com" => vec!["NVIDIA_API_KEY"], @@ -1183,6 +1191,13 @@ fn create_provider_with_url_and_options( key, AuthStyle::Bearer, ))), + name if is_siliconflow_alias(name) => Ok(Box::new(OpenAiCompatibleProvider::new_with_vision( + "SiliconFlow", + SILICONFLOW_BASE_URL, + key, + AuthStyle::Bearer, + true, + ))), name if qwen_base_url(name).is_some() => Ok(Box::new(OpenAiCompatibleProvider::new_with_vision( "Qwen", qwen_base_url(name).expect("checked in guard"), @@ -1711,6 +1726,12 @@ pub fn list_providers() -> Vec { aliases: &["volcengine", "ark", "doubao-cn"], local: false, }, + ProviderInfo { + name: "siliconflow", + display_name: "SiliconFlow", + aliases: &["silicon-cloud", "siliconcloud"], + local: false, + }, ProviderInfo { name: "qwen", display_name: "Qwen (DashScope / Qwen Code OAuth)", @@ -2068,6 +2089,9 @@ mod tests { assert!(is_doubao_alias("volcengine")); assert!(is_doubao_alias("ark")); assert!(is_doubao_alias("doubao-cn")); + assert!(is_siliconflow_alias("siliconflow")); + assert!(is_siliconflow_alias("silicon-cloud")); + assert!(is_siliconflow_alias("siliconcloud")); assert!(!is_moonshot_alias("openrouter")); assert!(!is_glm_alias("openai")); @@ -2075,6 +2099,7 @@ mod tests { assert!(!is_zai_alias("anthropic")); assert!(!is_qianfan_alias("cohere")); assert!(!is_doubao_alias("deepseek")); + assert!(!is_siliconflow_alias("volcengine")); } #[test] @@ -2098,6 +2123,14 @@ mod tests { assert_eq!(canonical_china_provider_name("baidu"), Some("qianfan")); assert_eq!(canonical_china_provider_name("doubao"), Some("doubao")); assert_eq!(canonical_china_provider_name("volcengine"), Some("doubao")); + assert_eq!( + canonical_china_provider_name("siliconflow"), + Some("siliconflow") + ); + assert_eq!( + canonical_china_provider_name("silicon-cloud"), + Some("siliconflow") + ); assert_eq!(canonical_china_provider_name("hunyuan"), Some("hunyuan")); assert_eq!(canonical_china_provider_name("tencent"), Some("hunyuan")); assert_eq!(canonical_china_provider_name("openai"), None); @@ -2315,6 +2348,13 @@ mod tests { assert!(create_provider("doubao-cn", Some("key")).is_ok()); } + #[test] + fn factory_siliconflow() { + assert!(create_provider("siliconflow", Some("key")).is_ok()); + assert!(create_provider("silicon-cloud", Some("key")).is_ok()); + assert!(create_provider("siliconcloud", Some("key")).is_ok()); + } + #[test] fn factory_qwen() { assert!(create_provider("qwen", Some("key")).is_ok()); @@ -2775,6 +2815,8 @@ mod tests { "bedrock", "qianfan", "doubao", + "volcengine", + "siliconflow", "qwen", "qwen-intl", "qwen-cn", diff --git a/tests/provider_resolution.rs b/tests/provider_resolution.rs index d057e6718..1cb99dad7 100644 --- a/tests/provider_resolution.rs +++ b/tests/provider_resolution.rs @@ -333,6 +333,19 @@ fn factory_resolves_doubao_provider() { assert_provider_ok("doubao", Some("test-key"), None); } +#[test] +fn factory_resolves_volcengine_provider() { + assert_provider_ok("volcengine", Some("test-key"), None); + assert_provider_ok("ark", Some("test-key"), None); +} + +#[test] +fn factory_resolves_siliconflow_provider() { + assert_provider_ok("siliconflow", Some("test-key"), None); + assert_provider_ok("silicon-cloud", Some("test-key"), None); + assert_provider_ok("siliconcloud", Some("test-key"), None); +} + #[test] fn factory_resolves_qianfan_provider() { assert_provider_ok("qianfan", Some("test-key"), None);