feat(llm): 新增 StepFun 供应商预设;修复 Codex OAuth 默认模型不可用 (#836)

StepFun 预设:
- 供应商下拉新增 StepFun(阶跃星辰),预填 https://api.stepfun.com/v1,
  默认模型 step-1o-turbo-vision(无思考,流式首字 ~0.3s;step-3.x-flash
  系列为推理模型且思考无法关闭,正式内容需等隐藏思考结束,不适合听写润色)
- thinking 控制表新增 stepfun → reasoning_effort(开=medium / 关=low),
  base_url 兜底识别同步支持"自定义"preset 接入 StepFun 的场景
- 补默认 endpoint 映射与两个单元测试

Codex OAuth 默认模型:
- gpt-5.3-codex-spark 对 ChatGPT 账号会被后端 400 拒绝
  ("model is not supported when using Codex with a ChatGPT account"),
  导致每次润色静默失败回退原文;默认与占位改为实测可用的 gpt-5.5

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bigsongandClaude Fable 5 authored and GitHub committed 2026-07-18 15:43:39 +08:00
1 parent 17fe67c59b
commit ce3fd8d847
8 files changed
+73 -3

No files matched your search

@@ -92,6 +92,7 @@ fn llm_provider_default_endpoint(provider: &str) -> Option<&'static str> {
"openrouterFree" => Some("https://openrouter.ai/api/v1"),
"alibabaCoding" => Some("https://coding-intl.dashscope.aliyuncs.com/v1"),
"codingPlanX" => Some("https://api.codingplanx.ai/v1"),
"stepfun" => Some("https://api.stepfun.com/v1"),
_ => None,
}
}
+51 -2
View File
@@ -25,7 +25,10 @@ const DEFAULT_REQUEST_TIMEOUT_SECS: u64 = 30;
const BODY_PREVIEW_LIMIT: usize = 200;
pub const CODEX_OAUTH_PROVIDER_ID: &str = "codex_oauth";
pub const CODEX_DEFAULT_BASE_URL: &str = "https://chatgpt.com/backend-api";
pub const CODEX_DEFAULT_MODEL: &str = "gpt-5.3-codex-spark";
// 注意:gpt-5.3-codex-spark 不能做默认——ChatGPT 账号走 Codex OAuth 时后端会
// 400 拒绝("model is not supported when using Codex with a ChatGPT account"),
// 每次润色都失败并回退原文。gpt-5.5 是该通道实测可用的模型。
pub const CODEX_DEFAULT_MODEL: &str = "gpt-5.5";
const CODEX_MIN_TOKEN_TTL_SECS: u64 = 60;
#[derive(Clone, Debug)]
@@ -1607,7 +1610,9 @@ fn openai_compatible_thinking_control(provider_id: &str) -> Option<ThinkingContr
"minimax" => Some(ThinkingControl::MiniMaxThinking),
"openrouterFree" => Some(ThinkingControl::OpenRouterReasoning),
"alibabaCoding" => Some(ThinkingControl::EnableThinking),
"openai" | "codingPlanX" => Some(ThinkingControl::ReasoningEffort),
// StepFun step-3.x-flash 系列按官方文档接受 reasoning_effort(low/medium/high,
// 无法完全关闭思考);非推理模型(如 step-1o-turbo-vision)会忽略该字段。
"openai" | "codingPlanX" | "stepfun" => Some(ThinkingControl::ReasoningEffort),
// custom / 其他未声明 provider 走 base_url 兜底识别——用户用自定义
// endpoint 接入 MiniMax 时,根据 base_url 命中即下发官方 thinking 参数。
_ => None,
@@ -1643,6 +1648,9 @@ fn openai_compatible_thinking_control_for_base_url(base_url: &str) -> Option<Thi
if host.contains("dashscope") || host.contains("aliyuncs") {
return Some(ThinkingControl::EnableThinking);
}
if host.contains("stepfun") {
return Some(ThinkingControl::ReasoningEffort);
}
None
}
@@ -2650,6 +2658,47 @@ mod tests {
}
}
#[test]
fn openai_chat_body_adds_reasoning_effort_for_stepfun_channel() {
// StepFun 按渠道声明下发 reasoning_effort:开启思考发 medium,关闭发 low。
for (thinking_enabled, expected) in [(true, "medium"), (false, "low")] {
let provider = OpenAICompatibleLLMProvider::new(
OpenAICompatibleConfig::new(
"stepfun",
"StepFun",
"https://api.stepfun.com/v1",
"k",
"step-3.7-flash",
)
.with_thinking_enabled(thinking_enabled),
);
let body = provider.chat_body(false, vec![json!({ "role": "user", "content": "hi" })]);
assert_eq!(body["reasoning_effort"], expected);
}
}
#[test]
fn openai_chat_body_falls_back_to_base_url_for_custom_stepfun_endpoint() {
// 用 "custom" preset + StepFun base_url 接入时,base_url 兜底识别需要
// 命中 "stepfun" 关键字,下发 reasoning_effort。
let provider = OpenAICompatibleLLMProvider::new(
OpenAICompatibleConfig::new(
"custom",
"Custom",
"https://api.stepfun.com/v1",
"k",
"step-3.7-flash",
)
.with_thinking_enabled(false),
);
let body = provider.chat_body(false, vec![json!({ "role": "user", "content": "hi" })]);
assert_eq!(body["reasoning_effort"], "low");
}
#[test]
fn openai_chat_body_omits_thinking_control_for_unknown_provider() {
let provider = OpenAICompatibleLLMProvider::new(
+1
View File
@@ -782,6 +782,7 @@ export const en: typeof zhCN = {
alibabaCoding: 'Alibaba Cloud Coding Plan',
codingPlanX: 'CodingPlanX',
minimax: 'MiniMax (M3)',
stepfun: 'StepFun',
custom: 'Custom',
asrVolcengine: 'Volcengine bigasr',
asrBailian: 'Alibaba Bailian realtime ASR',
+1
View File
@@ -784,6 +784,7 @@ export const ja: typeof zhCN = {
alibabaCoding: 'Alibaba Cloud Coding Plan',
codingPlanX: 'CodingPlanX',
minimax: 'MiniMax(M3)',
stepfun: 'StepFun(階躍星辰)',
custom: 'カスタム',
asrVolcengine: 'Volcengine bigasr',
asrBailian: 'Alibaba Bailian リアルタイム ASR',
+1
View File
@@ -784,6 +784,7 @@ export const ko: typeof zhCN = {
alibabaCoding: 'Alibaba Cloud Coding Plan',
codingPlanX: 'CodingPlanX',
minimax: 'MiniMax (M3)',
stepfun: 'StepFun',
custom: '사용자 정의',
asrVolcengine: 'Volcengine bigasr',
asrBailian: 'Alibaba Bailian 실시간 ASR',
+1
View File
@@ -780,6 +780,7 @@ export const zhCN = {
alibabaCoding: '阿里云 Coding Plan',
codingPlanX: 'CodingPlanX',
minimax: 'MiniMax(M3)',
stepfun: 'StepFun(阶跃星辰)',
custom: '自定义',
asrVolcengine: '火山引擎 bigasr',
asrBailian: '阿里云百炼实时 ASR',
+1
View File
@@ -782,6 +782,7 @@ export const zhTW: typeof zhCN = {
alibabaCoding: '阿里雲 Coding Plan',
codingPlanX: 'CodingPlanX',
minimax: 'MiniMax(M3)',
stepfun: 'StepFun(階躍星辰)',
custom: '自定義',
asrVolcengine: '火山引擎 bigasr',
asrBailian: '阿里雲百煉即時 ASR',
@@ -88,7 +88,9 @@ const LLM_PRESETS = [
id: 'codex_oauth',
nameKey: 'codexOAuth',
baseUrl: '',
modelPlaceholder: 'gpt-5.3-codex-spark',
// gpt-5.3-codex-spark 对 ChatGPT 账号的 Codex 通道会被 400 拒绝,
// 默认与占位一律用实测可用的 gpt-5.5(见 polish.rs::CODEX_DEFAULT_MODEL)。
modelPlaceholder: 'gpt-5.5',
},
{
id: 'mimo',
@@ -132,6 +134,19 @@ const LLM_PRESETS = [
baseUrl: 'https://api.minimaxi.com/v1',
modelPlaceholder: 'MiniMax-M3',
},
{
// StepFun(阶跃星辰)OpenAI 兼容 /v1/chat/completions。
// 默认模型选 step-1o-turbo-vision:step-3.x-flash 系列是推理模型且思考无法关闭
// (reasoning_effort 只能调档,正式内容要等隐藏思考结束,润色场景 TTFT 2s+),
// 而 step-1o-turbo-vision 无思考、TTFT ~0.3s,润色忠实度实测更适合听写链路。
// provider_id 在后端 polish.rs::openai_compatible_thinking_control 命中
// "stepfun" → ReasoningEffort 分支;走"自定义"preset 接入时由 base_url
// 含 "stepfun" 兜底识别,见 polish.rs。
id: 'stepfun',
nameKey: 'stepfun',
baseUrl: 'https://api.stepfun.com/v1',
modelPlaceholder: 'step-1o-turbo-vision',
},
{
id: 'custom',
nameKey: 'custom',