feat(providers): add StepFun provider with onboarding and docs parity

This commit is contained in:
chumyin 2026-03-01 12:59:16 +00:00 committed by Chum Yin
parent 37b19365c8
commit 2630486ca8
11 changed files with 301 additions and 4 deletions

View File

@ -3,6 +3,22 @@
This file defines the default working protocol for coding agents in this repository.
Scope: entire repository.
## 0) Session Default Target (Mandatory)
- When operator intent does not explicitly specify another repository/path, treat the active coding target as this repository (`/home/ubuntu/zeroclaw`).
- Do not switch to or implement in other repositories unless the operator explicitly requests that scope in the current conversation.
- Ambiguous wording (for example "这个仓库", "当前项目", "the repo") is resolved to `/home/ubuntu/zeroclaw` by default.
- Context mentioning external repositories does not authorize cross-repo edits; explicit current-turn override is required.
- Before any repo-affecting action, verify target lock (`pwd` + git root) to prevent accidental execution in sibling repositories.
## 0.1) Clean Worktree First Gate (Mandatory)
- Before handling any repository content (analysis, debugging, coding, tests, docs, CI), create a **new clean dedicated git worktree** for the active task.
- Do not perform substantive task work in a dirty workspace.
- Do not reuse a previously dirty worktree for a new task track.
- If the current location is dirty, stop and bootstrap a clean worktree/branch first.
- If worktree bootstrap fails, stop and report the blocker; do not continue in-place.
## 1) Project Snapshot (Read First)
ZeroClaw is a Rust-first autonomous agent runtime optimized for:

View File

@ -20,3 +20,21 @@ Source anglaise:
## Notes de mise à jour
- Ajout d'un réglage `provider.reasoning_level` pour le niveau de raisonnement OpenAI Codex. Voir la source anglaise pour les détails.
- 2026-03-01: ajout de la prise en charge du provider StepFun (`stepfun`, alias `step`, `step-ai`, `step_ai`).
## StepFun (Résumé)
- Provider ID: `stepfun`
- Aliases: `step`, `step-ai`, `step_ai`
- Base API URL: `https://api.stepfun.com/v1`
- Endpoints: `POST /v1/chat/completions`, `GET /v1/models`
- Auth env var: `STEP_API_KEY` (fallback: `STEPFUN_API_KEY`)
- Modèle par défaut: `step-3.5-flash`
Validation rapide:
```bash
export STEP_API_KEY="your-stepfun-api-key"
zeroclaw models refresh --provider stepfun
zeroclaw agent --provider stepfun --model step-3.5-flash -m "ping"
```

View File

@ -16,3 +16,24 @@
- Provider ID と環境変数名は英語のまま保持します。
- 正式な仕様は英語版原文を優先します。
## 更新ノート
- 2026-03-01: StepFun provider 対応を追加(`stepfun`、alias: `step` / `step-ai` / `step_ai`)。
## StepFun クイックガイド
- Provider ID: `stepfun`
- Aliases: `step`, `step-ai`, `step_ai`
- Base API URL: `https://api.stepfun.com/v1`
- Endpoints: `POST /v1/chat/completions`, `GET /v1/models`
- 認証 env var: `STEP_API_KEY`fallback: `STEPFUN_API_KEY`
- 既定モデル: `step-3.5-flash`
クイック検証:
```bash
export STEP_API_KEY="your-stepfun-api-key"
zeroclaw models refresh --provider stepfun
zeroclaw agent --provider stepfun --model step-3.5-flash -m "ping"
```

View File

@ -16,3 +16,24 @@
- Provider ID и имена env переменных не переводятся.
- Нормативное описание поведения — в английском оригинале.
## Обновления
- 2026-03-01: добавлена поддержка провайдера StepFun (`stepfun`, алиасы `step`, `step-ai`, `step_ai`).
## StepFun (Кратко)
- Provider ID: `stepfun`
- Алиасы: `step`, `step-ai`, `step_ai`
- Base API URL: `https://api.stepfun.com/v1`
- Эндпоинты: `POST /v1/chat/completions`, `GET /v1/models`
- Переменная авторизации: `STEP_API_KEY` (fallback: `STEPFUN_API_KEY`)
- Модель по умолчанию: `step-3.5-flash`
Быстрая проверка:
```bash
export STEP_API_KEY="your-stepfun-api-key"
zeroclaw models refresh --provider stepfun
zeroclaw agent --provider stepfun --model step-3.5-flash -m "ping"
```

View File

@ -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-28**.
Cập nhật lần cuối: **2026-03-01**.
## Cách liệt kê các Provider
@ -33,6 +33,7 @@ Với chuỗi provider dự phòng (`reliability.fallback_providers`), mỗi pro
| `vercel` | `vercel-ai` | Không | `VERCEL_API_KEY` |
| `cloudflare` | `cloudflare-ai` | Không | `CLOUDFLARE_API_KEY` |
| `moonshot` | `kimi` | Không | `MOONSHOT_API_KEY` |
| `stepfun` | `step`, `step-ai`, `step_ai` | Không | `STEP_API_KEY`, `STEPFUN_API_KEY` |
| `kimi-code` | `kimi_coding`, `kimi_for_coding` | Không | `KIMI_CODE_API_KEY`, `MOONSHOT_API_KEY` |
| `synthetic` | — | Không | `SYNTHETIC_API_KEY` |
| `opencode` | `opencode-zen` | Không | `OPENCODE_API_KEY` |
@ -87,6 +88,29 @@ zeroclaw models refresh --provider volcengine
zeroclaw agent --provider volcengine --model doubao-1-5-pro-32k-250115 -m "ping"
```
### Ghi chú về StepFun
- Provider ID: `stepfun` (alias: `step`, `step-ai`, `step_ai`)
- Base API URL: `https://api.stepfun.com/v1`
- Chat endpoint: `/chat/completions`
- Model discovery endpoint: `/models`
- Xác thực: `STEP_API_KEY` (fallback: `STEPFUN_API_KEY`)
- Model mặc định: `step-3.5-flash`
Ví dụ thiết lập nhanh:
```bash
export STEP_API_KEY="your-stepfun-api-key"
zeroclaw onboard --provider stepfun --api-key "$STEP_API_KEY" --model step-3.5-flash --force
```
Kiểm tra nhanh:
```bash
zeroclaw models refresh --provider stepfun
zeroclaw agent --provider stepfun --model step-3.5-flash -m "ping"
```
### Ghi chú về SiliconFlow
- Provider ID: `siliconflow` (alias: `silicon-cloud`, `siliconcloud`)

View File

@ -16,3 +16,25 @@
- Provider ID 与环境变量名称保持英文。
- 规范与行为说明以英文原文为准。
## 更新记录
- 2026-03-01新增 StepFun provider 对齐信息(`stepfun` / `step` / `step-ai` / `step_ai`)。
## StepFun 快速说明
- Provider ID`stepfun`
- 别名:`step`、`step-ai`、`step_ai`
- Base API URL`https://api.stepfun.com/v1`
- 模型列表端点:`GET /v1/models`
- 对话端点:`POST /v1/chat/completions`
- 鉴权变量:`STEP_API_KEY`(回退:`STEPFUN_API_KEY`
- 默认模型:`step-3.5-flash`
快速验证:
```bash
export STEP_API_KEY="your-stepfun-api-key"
zeroclaw models refresh --provider stepfun
zeroclaw agent --provider stepfun --model step-3.5-flash -m "ping"
```

View File

@ -2,7 +2,7 @@
This document maps provider IDs, aliases, and credential environment variables.
Last verified: **February 28, 2026**.
Last verified: **March 1, 2026**.
## How to List Providers
@ -35,6 +35,7 @@ credential is not reused for fallback providers.
| `vercel` | `vercel-ai` | No | `VERCEL_API_KEY` |
| `cloudflare` | `cloudflare-ai` | No | `CLOUDFLARE_API_KEY` |
| `moonshot` | `kimi` | No | `MOONSHOT_API_KEY` |
| `stepfun` | `step`, `step-ai`, `step_ai` | No | `STEP_API_KEY`, `STEPFUN_API_KEY` |
| `kimi-code` | `kimi_coding`, `kimi_for_coding` | No | `KIMI_CODE_API_KEY`, `MOONSHOT_API_KEY` |
| `synthetic` | — | No | `SYNTHETIC_API_KEY` |
| `opencode` | `opencode-zen` | No | `OPENCODE_API_KEY` |
@ -137,6 +138,33 @@ zeroclaw models refresh --provider volcengine
zeroclaw agent --provider volcengine --model doubao-1-5-pro-32k-250115 -m "ping"
```
### StepFun Notes
- Provider ID: `stepfun` (aliases: `step`, `step-ai`, `step_ai`)
- Base API URL: `https://api.stepfun.com/v1`
- Chat endpoint: `/chat/completions`
- Model discovery endpoint: `/models`
- Authentication: `STEP_API_KEY` (fallback: `STEPFUN_API_KEY`)
- Default model preset: `step-3.5-flash`
- Official docs:
- Chat Completions: <https://platform.stepfun.com/docs/zh/api-reference/chat/chat-completion-create>
- Models List: <https://platform.stepfun.com/docs/api-reference/models/list>
- OpenAI migration guide: <https://platform.stepfun.com/docs/guide/openai>
Minimal setup example:
```bash
export STEP_API_KEY="your-stepfun-api-key"
zeroclaw onboard --provider stepfun --api-key "$STEP_API_KEY" --model step-3.5-flash --force
```
Quick validation:
```bash
zeroclaw models refresh --provider stepfun
zeroclaw agent --provider stepfun --model step-3.5-flash -m "ping"
```
### SiliconFlow Notes
- Provider ID: `siliconflow` (aliases: `silicon-cloud`, `siliconcloud`)

View File

@ -77,6 +77,7 @@ pub fn default_model_fallback_for_provider(provider_name: Option<&str>) -> &'sta
"together-ai" => "meta-llama/Llama-3.3-70B-Instruct-Turbo",
"cohere" => "command-a-03-2025",
"moonshot" => "kimi-k2.5",
"stepfun" => "step-3.5-flash",
"hunyuan" => "hunyuan-t1-latest",
"glm" | "zai" => "glm-5",
"minimax" => "MiniMax-M2.5",
@ -11817,6 +11818,9 @@ provider_api = "not-a-real-mode"
let openai = resolve_default_model_id(None, Some("openai"));
assert_eq!(openai, "gpt-5.2");
let stepfun = resolve_default_model_id(None, Some("stepfun"));
assert_eq!(stepfun, "step-3.5-flash");
let bedrock = resolve_default_model_id(None, Some("aws-bedrock"));
assert_eq!(bedrock, "anthropic.claude-sonnet-4-5-20250929-v1:0");
}
@ -11828,6 +11832,12 @@ provider_api = "not-a-real-mode"
let google_alias = resolve_default_model_id(None, Some("google-gemini"));
assert_eq!(google_alias, "gemini-2.5-pro");
let step_alias = resolve_default_model_id(None, Some("step"));
assert_eq!(step_alias, "step-3.5-flash");
let step_ai_alias = resolve_default_model_id(None, Some("step-ai"));
assert_eq!(step_ai_alias, "step-3.5-flash");
}
#[test]

View File

@ -1,7 +1,7 @@
use super::{IntegrationCategory, IntegrationEntry, IntegrationStatus};
use crate::providers::{
is_doubao_alias, is_glm_alias, is_minimax_alias, is_moonshot_alias, is_qianfan_alias,
is_qwen_alias, is_siliconflow_alias, is_zai_alias,
is_qwen_alias, is_siliconflow_alias, is_stepfun_alias, is_zai_alias,
};
/// Returns the full catalog of integrations
@ -352,6 +352,18 @@ pub fn all_integrations() -> Vec<IntegrationEntry> {
}
},
},
IntegrationEntry {
name: "StepFun",
description: "Step 3, Step 3.5 Flash, and vision models",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref().is_some_and(is_stepfun_alias) {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Synthetic",
description: "Synthetic-1 and synthetic family models",
@ -1020,6 +1032,13 @@ mod tests {
IntegrationStatus::Active
));
config.default_provider = Some("step-ai".to_string());
let stepfun = entries.iter().find(|e| e.name == "StepFun").unwrap();
assert!(matches!(
(stepfun.status_fn)(&config),
IntegrationStatus::Active
));
config.default_provider = Some("qwen-intl".to_string());
let qwen = entries.iter().find(|e| e.name == "Qwen").unwrap();
assert!(matches!(

View File

@ -25,7 +25,7 @@ use crate::migration::{
use crate::providers::{
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,
is_siliconflow_alias, is_stepfun_alias, is_zai_alias, is_zai_cn_alias,
};
use anyhow::{bail, Context, Result};
use console::style;
@ -966,6 +966,7 @@ fn default_model_for_provider(provider: &str) -> String {
"together-ai" => "meta-llama/Llama-3.3-70B-Instruct-Turbo".into(),
"cohere" => "command-a-03-2025".into(),
"moonshot" => "kimi-k2.5".into(),
"stepfun" => "step-3.5-flash".into(),
"hunyuan" => "hunyuan-t1-latest".into(),
"glm" | "zai" => "glm-5".into(),
"minimax" => "MiniMax-M2.5".into(),
@ -1246,6 +1247,24 @@ fn curated_models_for_provider(provider_name: &str) -> Vec<(String, String)> {
"Kimi K2 0905 Preview (strong coding)".to_string(),
),
],
"stepfun" => vec![
(
"step-3.5-flash".to_string(),
"Step 3.5 Flash (recommended default)".to_string(),
),
(
"step-3".to_string(),
"Step 3 (flagship reasoning)".to_string(),
),
(
"step-2-mini".to_string(),
"Step 2 Mini (balanced and fast)".to_string(),
),
(
"step-1o-turbo-vision".to_string(),
"Step 1o Turbo Vision (multimodal)".to_string(),
),
],
"glm" | "zai" => vec![
("glm-5".to_string(), "GLM-5 (high reasoning)".to_string()),
(
@ -1483,6 +1502,7 @@ fn supports_live_model_fetch(provider_name: &str) -> bool {
| "novita"
| "cohere"
| "moonshot"
| "stepfun"
| "glm"
| "zai"
| "qwen"
@ -1515,6 +1535,7 @@ fn models_endpoint_for_provider(provider_name: &str) -> Option<&'static str> {
"novita" => Some("https://api.novita.ai/openai/v1/models"),
"cohere" => Some("https://api.cohere.com/compatibility/v1/models"),
"moonshot" => Some("https://api.moonshot.ai/v1/models"),
"stepfun" => Some("https://api.stepfun.com/v1/models"),
"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"),
@ -2515,6 +2536,7 @@ async fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String,
"moonshot-intl",
"Moonshot — Kimi API (international endpoint)",
),
("stepfun", "StepFun — Step AI OpenAI-compatible endpoint"),
("glm", "GLM — ChatGLM / Zhipu (international endpoint)"),
("glm-cn", "GLM — ChatGLM / Zhipu (China endpoint)"),
(
@ -2934,6 +2956,8 @@ async fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String,
"https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey"
} else if is_siliconflow_alias(provider_name) {
"https://cloud.siliconflow.cn/account/ak"
} else if is_stepfun_alias(provider_name) {
"https://platform.stepfun.com/interface-key"
} else {
match provider_name {
"openrouter" => "https://openrouter.ai/keys",
@ -3239,6 +3263,7 @@ fn provider_env_var(name: &str) -> &'static str {
"cohere" => "COHERE_API_KEY",
"kimi-code" => "KIMI_CODE_API_KEY",
"moonshot" => "MOONSHOT_API_KEY",
"stepfun" => "STEP_API_KEY",
"glm" => "GLM_API_KEY",
"minimax" => "MINIMAX_API_KEY",
"qwen" => "DASHSCOPE_API_KEY",
@ -7817,6 +7842,7 @@ mod tests {
);
assert_eq!(default_model_for_provider("venice"), "zai-org-glm-5");
assert_eq!(default_model_for_provider("moonshot"), "kimi-k2.5");
assert_eq!(default_model_for_provider("stepfun"), "step-3.5-flash");
assert_eq!(default_model_for_provider("hunyuan"), "hunyuan-t1-latest");
assert_eq!(default_model_for_provider("tencent"), "hunyuan-t1-latest");
assert_eq!(
@ -7858,6 +7884,9 @@ mod tests {
assert_eq!(canonical_provider_name("openai_codex"), "openai-codex");
assert_eq!(canonical_provider_name("moonshot-intl"), "moonshot");
assert_eq!(canonical_provider_name("kimi-cn"), "moonshot");
assert_eq!(canonical_provider_name("step"), "stepfun");
assert_eq!(canonical_provider_name("step-ai"), "stepfun");
assert_eq!(canonical_provider_name("step_ai"), "stepfun");
assert_eq!(canonical_provider_name("kimi_coding"), "kimi-code");
assert_eq!(canonical_provider_name("kimi_for_coding"), "kimi-code");
assert_eq!(canonical_provider_name("glm-cn"), "glm");
@ -7959,6 +7988,19 @@ mod tests {
assert!(!ids.contains(&"kimi-thinking-preview".to_string()));
}
#[test]
fn curated_models_for_stepfun_include_expected_defaults() {
let ids: Vec<String> = curated_models_for_provider("stepfun")
.into_iter()
.map(|(id, _)| id)
.collect();
assert!(ids.contains(&"step-3.5-flash".to_string()));
assert!(ids.contains(&"step-3".to_string()));
assert!(ids.contains(&"step-2-mini".to_string()));
assert!(ids.contains(&"step-1o-turbo-vision".to_string()));
}
#[test]
fn allows_unauthenticated_model_fetch_for_public_catalogs() {
assert!(allows_unauthenticated_model_fetch("openrouter"));
@ -8046,6 +8088,9 @@ mod tests {
assert!(supports_live_model_fetch("vllm"));
assert!(supports_live_model_fetch("astrai"));
assert!(supports_live_model_fetch("venice"));
assert!(supports_live_model_fetch("stepfun"));
assert!(supports_live_model_fetch("step"));
assert!(supports_live_model_fetch("step-ai"));
assert!(supports_live_model_fetch("glm-cn"));
assert!(supports_live_model_fetch("qwen-intl"));
assert!(supports_live_model_fetch("qwen-coding-plan"));
@ -8120,6 +8165,14 @@ mod tests {
curated_models_for_provider("volcengine"),
curated_models_for_provider("ark")
);
assert_eq!(
curated_models_for_provider("stepfun"),
curated_models_for_provider("step")
);
assert_eq!(
curated_models_for_provider("stepfun"),
curated_models_for_provider("step-ai")
);
assert_eq!(
curated_models_for_provider("siliconflow"),
curated_models_for_provider("silicon-cloud")
@ -8192,6 +8245,18 @@ mod tests {
models_endpoint_for_provider("moonshot"),
Some("https://api.moonshot.ai/v1/models")
);
assert_eq!(
models_endpoint_for_provider("stepfun"),
Some("https://api.stepfun.com/v1/models")
);
assert_eq!(
models_endpoint_for_provider("step"),
Some("https://api.stepfun.com/v1/models")
);
assert_eq!(
models_endpoint_for_provider("step-ai"),
Some("https://api.stepfun.com/v1/models")
);
assert_eq!(
models_endpoint_for_provider("siliconflow"),
Some("https://api.siliconflow.cn/v1/models")
@ -8497,6 +8562,9 @@ mod tests {
assert_eq!(provider_env_var("minimax-oauth"), "MINIMAX_API_KEY");
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("stepfun"), "STEP_API_KEY");
assert_eq!(provider_env_var("step"), "STEP_API_KEY");
assert_eq!(provider_env_var("step-ai"), "STEP_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");

View File

@ -83,6 +83,7 @@ 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 STEPFUN_BASE_URL: &str = "https://api.stepfun.com/v1";
const VERCEL_AI_GATEWAY_BASE_URL: &str = "https://ai-gateway.vercel.sh/v1";
pub(crate) fn is_minimax_intl_alias(name: &str) -> bool {
@ -192,6 +193,10 @@ pub(crate) fn is_siliconflow_alias(name: &str) -> bool {
matches!(name, "siliconflow" | "silicon-cloud" | "siliconcloud")
}
pub(crate) fn is_stepfun_alias(name: &str) -> bool {
matches!(name, "stepfun" | "step" | "step-ai" | "step_ai")
}
#[derive(Clone, Copy, Debug)]
enum MinimaxOauthRegion {
Global,
@ -633,6 +638,8 @@ pub(crate) fn canonical_china_provider_name(name: &str) -> Option<&'static str>
Some("doubao")
} else if is_siliconflow_alias(name) {
Some("siliconflow")
} else if is_stepfun_alias(name) {
Some("stepfun")
} else if matches!(name, "hunyuan" | "tencent") {
Some("hunyuan")
} else {
@ -694,6 +701,14 @@ fn zai_base_url(name: &str) -> Option<&'static str> {
}
}
fn stepfun_base_url(name: &str) -> Option<&'static str> {
if is_stepfun_alias(name) {
Some(STEPFUN_BASE_URL)
} else {
None
}
}
#[derive(Debug, Clone)]
pub struct ProviderRuntimeOptions {
pub auth_profile_override: Option<String>,
@ -943,6 +958,7 @@ fn resolve_provider_credential(name: &str, credential_override: Option<&str>) ->
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"],
name if is_stepfun_alias(name) => vec!["STEP_API_KEY", "STEPFUN_API_KEY"],
"nvidia" | "nvidia-nim" | "build.nvidia.com" => vec!["NVIDIA_API_KEY"],
"synthetic" => vec!["SYNTHETIC_API_KEY"],
"opencode" | "opencode-zen" => vec!["OPENCODE_API_KEY"],
@ -1274,6 +1290,12 @@ fn create_provider_with_url_and_options(
true,
)))
}
name if stepfun_base_url(name).is_some() => Ok(Box::new(OpenAiCompatibleProvider::new(
"StepFun",
stepfun_base_url(name).expect("checked in guard"),
key,
AuthStyle::Bearer,
))),
name if qwen_base_url(name).is_some() => {
Ok(Box::new(OpenAiCompatibleProvider::new_with_vision(
"Qwen",
@ -1831,6 +1853,12 @@ pub fn list_providers() -> Vec<ProviderInfo> {
aliases: &["kimi"],
local: false,
},
ProviderInfo {
name: "stepfun",
display_name: "StepFun",
aliases: &["step", "step-ai", "step_ai"],
local: false,
},
ProviderInfo {
name: "kimi-code",
display_name: "Kimi Code",
@ -2273,6 +2301,10 @@ mod tests {
assert!(is_siliconflow_alias("siliconflow"));
assert!(is_siliconflow_alias("silicon-cloud"));
assert!(is_siliconflow_alias("siliconcloud"));
assert!(is_stepfun_alias("stepfun"));
assert!(is_stepfun_alias("step"));
assert!(is_stepfun_alias("step-ai"));
assert!(is_stepfun_alias("step_ai"));
assert!(!is_moonshot_alias("openrouter"));
assert!(!is_glm_alias("openai"));
@ -2281,6 +2313,7 @@ mod tests {
assert!(!is_qianfan_alias("cohere"));
assert!(!is_doubao_alias("deepseek"));
assert!(!is_siliconflow_alias("volcengine"));
assert!(!is_stepfun_alias("moonshot"));
}
#[test]
@ -2312,6 +2345,9 @@ mod tests {
canonical_china_provider_name("silicon-cloud"),
Some("siliconflow")
);
assert_eq!(canonical_china_provider_name("stepfun"), Some("stepfun"));
assert_eq!(canonical_china_provider_name("step"), Some("stepfun"));
assert_eq!(canonical_china_provider_name("step-ai"), Some("stepfun"));
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);
@ -2352,6 +2388,10 @@ mod tests {
assert_eq!(zai_base_url("z.ai-global"), Some(ZAI_GLOBAL_BASE_URL));
assert_eq!(zai_base_url("zai-cn"), Some(ZAI_CN_BASE_URL));
assert_eq!(zai_base_url("z.ai-cn"), Some(ZAI_CN_BASE_URL));
assert_eq!(stepfun_base_url("stepfun"), Some(STEPFUN_BASE_URL));
assert_eq!(stepfun_base_url("step"), Some(STEPFUN_BASE_URL));
assert_eq!(stepfun_base_url("step-ai"), Some(STEPFUN_BASE_URL));
}
// ── Primary providers ────────────────────────────────────
@ -2438,6 +2478,13 @@ mod tests {
assert!(create_provider("kimi-cn", Some("key")).is_ok());
}
#[test]
fn factory_stepfun() {
assert!(create_provider("stepfun", Some("key")).is_ok());
assert!(create_provider("step", Some("key")).is_ok());
assert!(create_provider("step-ai", Some("key")).is_ok());
}
#[test]
fn factory_kimi_code() {
assert!(create_provider("kimi-code", Some("key")).is_ok());
@ -2990,6 +3037,9 @@ mod tests {
"kimi-code",
"moonshot-cn",
"kimi-code",
"stepfun",
"step",
"step-ai",
"synthetic",
"opencode",
"zai",