Align Claude Desktop model mapping with Claude Code three-role tiers

Claude Desktop's 3P validation only accepts claude-{sonnet,opus,haiku}-*
role IDs, so providers must map every tier. Bring the Desktop mapping flow
in line with Claude Code and fix the fallout that broke sub-agent Haiku calls.

- Proxy form: replace the dynamic route list with fixed Sonnet/Opus/Haiku
  tiers; blank tiers backfill from the first filled tier (Sonnet first) on
  submit and inherit its supports1m flag
- Backend: add a role-keyword fallback in map_proxy_request_model so dated
  official names (e.g. claude-haiku-4-5-20251001) resolve to the right tier,
  guarded by is_claude_safe_model_id so [1m]-suffixed IDs stay rejected
- Tighten is_claude_safe_model_id / isClaudeSafeRoute to reject degenerate
  role IDs like "claude-sonnet-"
- Fix the seed-effect race where normalizing empty routes to three blank
  tiers blocked the default-route backfill
- Sync switch hints, placeholders, and the zh/en/ja/zh-TW locales to the
  three-role-ID rule
- Update zh/en/ja user manual (2.1, 2.6), calling out legacy Claude IDs
  (claude-3-5-sonnet-...) as a rejected example

Tests: 282 frontend + 34 backend claude_desktop; typecheck, clippy, fmt clean.
This commit is contained in:
Jason
2026-05-29 22:27:47 +08:00
parent 058c9fb8e5
commit 94cc3d103a
13 changed files with 574 additions and 363 deletions
+6 -6
View File
@@ -187,10 +187,10 @@
"modeDirect": "Direct",
"modeProxy": "Requires routing",
"modelMappingToggle": "Needs model mapping",
"modelMappingOffHint": "If your provider does not offer native Claude series models, please enable this switch.",
"modelMappingOnHint": "Claude Desktop currently restricts model IDs. If your provider offers non-Claude models, enable this switch and keep local routing running while in use.",
"modelMappingOffHint": "Direct mode works only when the provider accepts Claude Desktop's three role IDs (claude-sonnet-* / claude-opus-* / claude-haiku-*); for any other model name (including legacy IDs like claude-3-5-sonnet-…), enable this switch to use mapping.",
"modelMappingOnHint": "Claude Desktop only accepts the three role IDs claude-sonnet-* / claude-opus-* / claude-haiku-*. When enabled, CC Switch maps these three roles to your provider's actual models and keeps local routing running while in use.",
"routeMapTitle": "Model mapping",
"routeMapHint": "Choose a model role and CC Switch will generate a Claude Desktop-compatible route automatically. Use the menu display name for branded models such as DeepSeek or Kimi; requested model IDs are sent upstream exactly as entered.",
"routeMapHint": "Fill in the actual request model for each of the Sonnet, Opus, and Haiku tiers. Use the menu display name for branded models such as DeepSeek or Kimi. Any tier left blank inherits the Sonnet tier's model (or the first tier you filled in), so the Haiku that sub-agents call is always available.",
"routeModelLabel": "Model role",
"routeRoleSonnet": "Sonnet",
"routeRoleOpus": "Opus",
@@ -201,15 +201,15 @@
"supports1mShort": "1M",
"directModelListTitle": "Manually specify Claude Desktop models (advanced, optional)",
"directModelListCollapsedHint": "Native Claude model providers usually do not need this. Claude Desktop will fetch /v1/models automatically.",
"directModelListHint": "Only fill this when the provider's /v1/models is unavailable or does not return Claude Desktop-safe claude-* model IDs. Checking 1M declares 1M context support to Claude Desktop.",
"directModelInvalid": "Direct models must use claude-* / anthropic/claude-* model IDs",
"directModelListHint": "Only fill this when the provider's /v1/models is unavailable or does not return Claude Desktop-recognized Sonnet / Opus / Haiku model IDs. Checking 1M declares 1M context support to Claude Desktop.",
"directModelInvalid": "Direct models must use Claude Desktop-recognized Sonnet / Opus / Haiku model IDs",
"addModel": "Add model",
"addRoute": "Add model",
"routeInvalid": "Choose a model role and enter the requested model",
"routesRequired": "At least one model mapping is required",
"statusTitle": "Claude Desktop configuration needs attention",
"statusUnsupported": "This platform does not support writing Claude Desktop 3P configuration yet.",
"statusStaleRawModels": "The Claude Desktop profile contains non-claude-* model IDs. Newer Claude Desktop versions may reject it; switch to the current provider again to repair it.",
"statusStaleRawModels": "The Claude Desktop profile contains model IDs outside the Sonnet / Opus / Haiku role families. Newer Claude Desktop versions may reject it; switch to the current provider again to repair it.",
"statusMissingRouteMappings": "The current provider has model mapping enabled but no valid routes. Edit the provider and add at least one mapping.",
"statusGatewayTokenMissing": "The local routing token has not been generated yet. Switching to this provider again will write a new local token.",
"statusBaseUrlMismatch": "The Claude Desktop profile points to a different URL than the current provider. Current: {{actual}}; expected: {{expected}}. Switch to the current provider again to repair it.",
+6 -6
View File
@@ -187,10 +187,10 @@
"modeDirect": "直結",
"modeProxy": "ルーティング必要",
"modelMappingToggle": "モデルマッピングが必要",
"modelMappingOffHint": "プロバイダーがネイティブの Claude シリーズモデルを提供していない場合は、このスイッチを有効にしてください。",
"modelMappingOnHint": "Claude Desktop は現在モデル ID を制限しています。お使いのプロバイダーが Claude 系列以外のモデルを提供する場合、このスイッチを有効にし、使用中はローカルルーティングを起動したままにしてください。",
"modelMappingOffHint": "プロバイダーが Claude Desktop の三つの役割 IDclaude-sonnet-* / claude-opus-* / claude-haiku-*)を受け付ける場合のみ直結できます。それ以外のモデル名(claude-3-5-sonnet-… などの旧式 ID を含む)はこのスイッチを有効にしてマッピングを使ってください。",
"modelMappingOnHint": "Claude Desktop は claude-sonnet-* / claude-opus-* / claude-haiku-* の三つの役割 ID のみ受け付けます。有効にすると CC Switch がこの三役割をプロバイダーの実モデルにマッピングし、使用中はローカルルーティングを起動したままにします。",
"routeMapTitle": "モデルマッピング",
"routeMapHint": "モデル役割を選ぶと、CC Switch が Claude Desktop 互換ルートを自動生成します。DeepSeek や Kimi などのブランド名はメニュー表示名に入れ、実際のリクエストモデルは右側の入力どおりに送信されます。",
"routeMapHint": "Sonnet・Opus・Haiku の各区分に実際のリクエストモデルを入力してください。DeepSeek や Kimi などのブランド名はメニュー表示名に入れられます。空欄の区分は Sonnet(または最初に入力した区分)のモデルを引き継ぐため、サブ agent が呼び出す Haiku が常に利用可能になります。",
"routeModelLabel": "モデル役割",
"routeRoleSonnet": "Sonnet",
"routeRoleOpus": "Opus",
@@ -201,15 +201,15 @@
"supports1mShort": "1M",
"directModelListTitle": "Claude Desktop モデルを手動指定(高度・任意)",
"directModelListCollapsedHint": "ネイティブ Claude モデルのプロバイダーでは通常不要です。Claude Desktop が /v1/models を自動取得します。",
"directModelListHint": "プロバイダーの /v1/models が使えない、または Claude Desktop が認識できる claude-* モデル ID を返さない場合だけ入力してください。1M をオンにすると Claude Desktop に 1M コンテキスト対応を宣言します。",
"directModelInvalid": "直結モデルは claude-* / anthropic/claude-* のモデル ID を使う必要があります",
"directModelListHint": "プロバイダーの /v1/models が使えない、または Claude Desktop が認識できる Sonnet / Opus / Haiku モデル ID を返さない場合だけ入力してください。1M をオンにすると Claude Desktop に 1M コンテキスト対応を宣言します。",
"directModelInvalid": "直結モデルは Claude Desktop が認識できる Sonnet / Opus / Haiku モデル ID を使う必要があります",
"addModel": "モデルを追加",
"addRoute": "モデルを追加",
"routeInvalid": "モデル役割を選択し、リクエストモデルを入力してください",
"routesRequired": "少なくとも 1 つのモデルマッピングが必要です",
"statusTitle": "Claude Desktop 設定の確認が必要です",
"statusUnsupported": "このプラットフォームでは Claude Desktop 3P 設定の書き込みはまだサポートされていません。",
"statusStaleRawModels": "Claude Desktop profile に claude-* ではないモデル ID が含まれています。新しい Claude Desktop では拒否される可能性があります。現在のプロバイダーへ再度切り替えると修復できます。",
"statusStaleRawModels": "Claude Desktop profile に Sonnet / Opus / Haiku の役割ファミリー以外のモデル ID が含まれています。新しい Claude Desktop では拒否される可能性があります。現在のプロバイダーへ再度切り替えると修復できます。",
"statusMissingRouteMappings": "現在のプロバイダーはモデルマッピングを有効にしていますが、有効なルートがありません。プロバイダーを編集し、少なくとも 1 つのマッピングを追加してください。",
"statusGatewayTokenMissing": "ローカルルーティング token がまだ生成されていません。このプロバイダーへ再度切り替えると新しい token が書き込まれます。",
"statusBaseUrlMismatch": "Claude Desktop profile の URL が現在のプロバイダーと一致しません。現在: {{actual}}、期待値: {{expected}}。現在のプロバイダーへ再度切り替えると修復できます。",
+6 -6
View File
@@ -187,10 +187,10 @@
"modeDirect": "直連",
"modeProxy": "需要路由",
"modelMappingToggle": "需要模型對應",
"modelMappingOffHint": "如果您的供應商提供的不是原生 Claude 系列模型,請開啟此開關。",
"modelMappingOnHint": "Claude Desktop 目前對模型 ID 進行了限制,如果您的供應商提供的模型不是 Claude 系列模型,則需要開啟本開關,並在使用過程中保持本地路由開啟。",
"modelMappingOffHint": "僅當供應商直接接受 Claude Desktop 可識別的三檔角色 IDclaude-sonnet-* / claude-opus-* / claude-haiku-*)時才適用直連;其他模型名(含 claude-3-5-sonnet-… 等舊式 ID請開啟此開關走對應。",
"modelMappingOnHint": "Claude Desktop 只接受 claude-sonnet-* / claude-opus-* / claude-haiku-* 三檔角色 ID。開啟後 CC Switch 會把這三檔對應到供應商的實際模型,並在使用期間保持本地路由開啟。",
"routeMapTitle": "模型對應",
"routeMapHint": "選擇模型角色後,CC Switch 會自動產生 Claude Desktop 相容路由;選單顯示名稱可以填寫 DeepSeek、Kimi 等品牌模型,實際請求模型依右側填寫內容發送。",
"routeMapHint": "為 Sonnet、Opus、Haiku 三檔分別填寫實際請求模型;選單顯示名稱可寫 DeepSeek、Kimi 等品牌名。留空的檔會自動沿用 Sonnet(或第一個已填檔)的模型,確保子 agent 呼叫的 Haiku 始終可用。",
"routeModelLabel": "模型角色",
"routeRoleSonnet": "Sonnet",
"routeRoleOpus": "Opus",
@@ -201,15 +201,15 @@
"supports1mShort": "1M",
"directModelListTitle": "手動指定 Claude Desktop 模型清單(進階,選填)",
"directModelListCollapsedHint": "原生 Claude 模型供應商通常不需填寫,Claude Desktop 會自動讀取 /v1/models。",
"directModelListHint": "僅當供應商的 /v1/models 不可用或沒有回傳 Claude Desktop 可識別的 claude-* 模型名稱時填寫;勾選 1M 會向 Claude Desktop 聲明支援 1M 上下文。",
"directModelInvalid": "直連模型必須使用 claude-* / anthropic/claude-* 模型名稱",
"directModelListHint": "僅當供應商的 /v1/models 不可用或沒有回傳 Claude Desktop 可識別的 Sonnet / Opus / Haiku 模型名稱時填寫;勾選 1M 會向 Claude Desktop 聲明支援 1M 上下文。",
"directModelInvalid": "直連模型必須使用 Claude Desktop 可識別的 Sonnet / Opus / Haiku 模型名稱",
"addModel": "新增模型",
"addRoute": "新增模型",
"routeInvalid": "請選擇模型角色並填寫實際請求模型",
"routesRequired": "至少填寫一個模型對應",
"statusTitle": "Claude Desktop 設定需要檢查",
"statusUnsupported": "目前平台暫不支援 Claude Desktop 3P 設定寫入。",
"statusStaleRawModels": "Claude Desktop profile 中存在非 claude-* 模型名稱,新版 Claude Desktop 可能拒絕載入;重新切換目前供應商可修復。",
"statusStaleRawModels": "Claude Desktop profile 中存在非 Sonnet / Opus / Haiku 角色模型名稱,新版 Claude Desktop 可能拒絕載入;重新切換目前供應商可修復。",
"statusMissingRouteMappings": "目前供應商啟用了模型對應,但沒有有效路由;請編輯供應商並補全至少一個模型對應。",
"statusGatewayTokenMissing": "目前本地路由 token 尚未產生;重新切換該供應商會寫入新的本地 token。",
"statusBaseUrlMismatch": "Claude Desktop profile 指向的位址與目前供應商不一致;目前為 {{actual}},應為 {{expected}}。重新切換目前供應商可修復。",
+6 -6
View File
@@ -187,10 +187,10 @@
"modeDirect": "直连",
"modeProxy": "需要路由",
"modelMappingToggle": "需要模型映射",
"modelMappingOffHint": "如果您的供应商提供的不是原生 Claude 系列模型,请打开此开关。",
"modelMappingOnHint": "Claude Desktop 目前对模型 ID 进行了限制,如果您的供应商提供的模型不是 Claude 系列模型,则需要打开本开关,并在使用过程中保持本地路由开启。",
"modelMappingOffHint": "仅当供应商直接接受 Claude Desktop 可识别的三档角色 IDclaude-sonnet-* / claude-opus-* / claude-haiku-*)时才适用直连;其他模型名(含 claude-3-5-sonnet-… 等旧式 ID请打开此开关走映射。",
"modelMappingOnHint": "Claude Desktop 只接受 claude-sonnet-* / claude-opus-* / claude-haiku-* 三档角色 ID。开启后 CC Switch 会把这三档映射到供应商的实际模型,并在使用期间保持本地路由开启。",
"routeMapTitle": "模型映射",
"routeMapHint": "选择模型角色后,CC Switch 会自动生成 Claude Desktop 兼容路由;菜单显示名可写 DeepSeek、Kimi 等品牌模型,实际请求模型按右侧填写内容发送。",
"routeMapHint": "为 Sonnet、Opus、Haiku 三档分别填写实际请求模型;菜单显示名可写 DeepSeek、Kimi 等品牌名。留空的档会自动沿用 Sonnet(或第一个已填档)的模型,确保子 agent 调用的 Haiku 始终可用。",
"routeModelLabel": "模型角色",
"routeRoleSonnet": "Sonnet",
"routeRoleOpus": "Opus",
@@ -201,15 +201,15 @@
"supports1mShort": "1M",
"directModelListTitle": "手动指定 Claude Desktop 模型列表(高级,可选)",
"directModelListCollapsedHint": "原生 Claude 模型供应商通常不用填写,Claude Desktop 会自动读取 /v1/models。",
"directModelListHint": "仅当供应商的 /v1/models 不可用或没有返回 Claude Desktop 可识别的 claude-* 模型名时填写;勾选 1M 会向 Claude Desktop 声明支持 1M 上下文。",
"directModelInvalid": "直连模型必须使用 claude-* / anthropic/claude-* 模型名",
"directModelListHint": "仅当供应商的 /v1/models 不可用或没有返回 Claude Desktop 可识别的 Sonnet / Opus / Haiku 模型名时填写;勾选 1M 会向 Claude Desktop 声明支持 1M 上下文。",
"directModelInvalid": "直连模型必须使用 Claude Desktop 可识别的 Sonnet / Opus / Haiku 模型名",
"addModel": "添加模型",
"addRoute": "添加模型",
"routeInvalid": "请选择模型角色并填写实际请求模型",
"routesRequired": "至少填写一个模型映射",
"statusTitle": "Claude Desktop 配置需要检查",
"statusUnsupported": "当前平台暂不支持 Claude Desktop 3P 配置写入。",
"statusStaleRawModels": "Claude Desktop profile 中存在非 claude-* 模型名,新版 Claude Desktop 可能拒绝加载;重新切换当前供应商可修复。",
"statusStaleRawModels": "Claude Desktop profile 中存在非 Sonnet / Opus / Haiku 角色模型名,新版 Claude Desktop 可能拒绝加载;重新切换当前供应商可修复。",
"statusMissingRouteMappings": "当前供应商启用了模型映射,但没有有效路由;请编辑供应商并补全至少一个模型映射。",
"statusGatewayTokenMissing": "当前本地路由 token 尚未生成;重新切换该供应商会写入新的本地 token。",
"statusBaseUrlMismatch": "Claude Desktop profile 指向的地址与当前供应商不一致;当前为 {{actual}},应为 {{expected}}。重新切换当前供应商可修复。",