diff --git a/src-tauri/src/proxy/model_mapper.rs b/src-tauri/src/proxy/model_mapper.rs index 4f0f94d81..c7f543e1e 100644 --- a/src-tauri/src/proxy/model_mapper.rs +++ b/src-tauri/src/proxy/model_mapper.rs @@ -11,7 +11,6 @@ pub struct ModelMapping { pub sonnet_model: Option, pub opus_model: Option, pub default_model: Option, - pub reasoning_model: Option, } impl ModelMapping { @@ -40,11 +39,6 @@ impl ModelMapping { .and_then(|v| v.as_str()) .filter(|s| !s.is_empty()) .map(String::from), - reasoning_model: env - .and_then(|e| e.get("ANTHROPIC_REASONING_MODEL")) - .and_then(|v| v.as_str()) - .filter(|s| !s.is_empty()) - .map(String::from), } } @@ -54,21 +48,13 @@ impl ModelMapping { || self.sonnet_model.is_some() || self.opus_model.is_some() || self.default_model.is_some() - || self.reasoning_model.is_some() } /// 根据原始模型名称获取映射后的模型 - pub fn map_model(&self, original_model: &str, has_thinking: bool) -> String { + pub fn map_model(&self, original_model: &str) -> String { let model_lower = original_model.to_lowercase(); - // 1. thinking 模式优先使用推理模型 - if has_thinking { - if let Some(ref m) = self.reasoning_model { - return m.clone(); - } - } - - // 2. 按模型类型匹配 + // 1. 按模型类型匹配 if model_lower.contains("haiku") { if let Some(ref m) = self.haiku_model { return m.clone(); @@ -85,35 +71,16 @@ impl ModelMapping { } } - // 3. 默认模型 + // 2. 默认模型 if let Some(ref m) = self.default_model { return m.clone(); } - // 4. 无映射,保持原样 + // 3. 无映射,保持原样 original_model.to_string() } } -/// 检测请求是否启用了 thinking 模式 -pub fn has_thinking_enabled(body: &Value) -> bool { - match body - .get("thinking") - .and_then(|v| v.as_object()) - .and_then(|o| o.get("type")) - .and_then(|t| t.as_str()) - { - Some("enabled") | Some("adaptive") => true, - Some("disabled") | None => false, - Some(other) => { - log::warn!( - "[ModelMapper] 未知 thinking.type='{other}',按 disabled 处理以避免误路由 reasoning 模型" - ); - false - } - } -} - /// 对请求体应用模型映射 /// /// 返回 (映射后的请求体, 原始模型名, 映射后模型名) @@ -133,8 +100,7 @@ pub fn apply_model_mapping( let original_model = body.get("model").and_then(|m| m.as_str()).map(String::from); if let Some(ref original) = original_model { - let has_thinking = has_thinking_enabled(&body); - let mapped = mapping.map_model(original, has_thinking); + let mapped = mapping.map_model(original); if mapped != *original { log::debug!("[ModelMapper] 模型映射: {original} → {mapped}"); @@ -160,8 +126,7 @@ mod tests { "ANTHROPIC_MODEL": "default-model", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "haiku-mapped", "ANTHROPIC_DEFAULT_SONNET_MODEL": "sonnet-mapped", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "opus-mapped", - "ANTHROPIC_REASONING_MODEL": "reasoning-model" + "ANTHROPIC_DEFAULT_OPUS_MODEL": "opus-mapped" } }), website_url: None, @@ -193,27 +158,6 @@ mod tests { } } - fn create_provider_with_reasoning_only() -> Provider { - Provider { - id: "test".to_string(), - name: "Test".to_string(), - settings_config: json!({ - "env": { - "ANTHROPIC_REASONING_MODEL": "reasoning-only-model" - } - }), - website_url: None, - category: None, - created_at: None, - sort_index: None, - notes: None, - meta: None, - icon: None, - icon_color: None, - in_failover_queue: false, - } - } - #[test] fn test_sonnet_mapping() { let provider = create_provider_with_mapping(); @@ -243,40 +187,29 @@ mod tests { } #[test] - fn test_thinking_mode() { + fn test_thinking_does_not_affect_model_mapping() { + // Issue #2081: thinking 参数不应影响模型映射 let provider = create_provider_with_mapping(); let body = json!({ "model": "claude-sonnet-4-5", "thinking": {"type": "enabled"} }); let (result, _, mapped) = apply_model_mapping(body, &provider); - assert_eq!(result["model"], "reasoning-model"); - assert_eq!(mapped, Some("reasoning-model".to_string())); + assert_eq!(result["model"], "sonnet-mapped"); + assert_eq!(mapped, Some("sonnet-mapped".to_string())); } #[test] - fn test_reasoning_only_mapping_in_thinking_mode() { - let provider = create_provider_with_reasoning_only(); + fn test_thinking_adaptive_does_not_affect_model_mapping() { + // Issue #2081: adaptive thinking 也不应影响模型映射 + let provider = create_provider_with_mapping(); let body = json!({ "model": "claude-sonnet-4-5", - "thinking": {"type": "enabled"} + "thinking": {"type": "adaptive"} }); let (result, _, mapped) = apply_model_mapping(body, &provider); - assert_eq!(result["model"], "reasoning-only-model"); - assert_eq!(mapped, Some("reasoning-only-model".to_string())); - } - - #[test] - fn test_reasoning_only_mapping_does_not_affect_non_thinking() { - let provider = create_provider_with_reasoning_only(); - let body = json!({ - "model": "claude-sonnet-4-5", - "thinking": {"type": "disabled"} - }); - let (result, original, mapped) = apply_model_mapping(body, &provider); - assert_eq!(result["model"], "claude-sonnet-4-5"); - assert_eq!(original, Some("claude-sonnet-4-5".to_string())); - assert!(mapped.is_none()); + assert_eq!(result["model"], "sonnet-mapped"); + assert_eq!(mapped, Some("sonnet-mapped".to_string())); } #[test] @@ -310,30 +243,6 @@ mod tests { assert!(mapped.is_none()); } - #[test] - fn test_thinking_adaptive() { - let provider = create_provider_with_mapping(); - let body = json!({ - "model": "claude-sonnet-4-5", - "thinking": {"type": "adaptive"} - }); - let (result, _, mapped) = apply_model_mapping(body, &provider); - assert_eq!(result["model"], "reasoning-model"); - assert_eq!(mapped, Some("reasoning-model".to_string())); - } - - #[test] - fn test_thinking_unknown_type() { - let provider = create_provider_with_mapping(); - let body = json!({ - "model": "claude-sonnet-4-5", - "thinking": {"type": "some_future_type"} - }); - let (result, _, mapped) = apply_model_mapping(body, &provider); - assert_eq!(result["model"], "sonnet-mapped"); - assert_eq!(mapped, Some("sonnet-mapped".to_string())); - } - #[test] fn test_case_insensitive() { let provider = create_provider_with_mapping(); diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index d16f18089..a21ff1554 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -1734,9 +1734,9 @@ impl ProviderService { // Auth "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", - // Models (5 fields) + // Models (4 fields + 1 legacy) "ANTHROPIC_MODEL", - "ANTHROPIC_REASONING_MODEL", + "ANTHROPIC_REASONING_MODEL", // legacy: 已废弃,但旧配置可能残留 "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index 267d71ca7..766d727fa 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -27,7 +27,7 @@ const PROXY_TOKEN_PLACEHOLDER: &str = "PROXY_MANAGED"; /// Claude Code 会继续以旧模型名发起请求,导致新供应商不支持时失败。 const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 6] = [ "ANTHROPIC_MODEL", - "ANTHROPIC_REASONING_MODEL", + "ANTHROPIC_REASONING_MODEL", // legacy: 已废弃,但旧配置可能残留 "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index 8362015f4..3df63410a 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -102,14 +102,12 @@ interface ClaudeFormFieldsProps { // Model Selector shouldShowModelSelector: boolean; claudeModel: string; - reasoningModel: string; defaultHaikuModel: string; defaultSonnetModel: string; defaultOpusModel: string; onModelChange: ( field: | "ANTHROPIC_MODEL" - | "ANTHROPIC_REASONING_MODEL" | "ANTHROPIC_DEFAULT_HAIKU_MODEL" | "ANTHROPIC_DEFAULT_SONNET_MODEL" | "ANTHROPIC_DEFAULT_OPUS_MODEL", @@ -164,7 +162,6 @@ export function ClaudeFormFields({ onAutoSelectChange, shouldShowModelSelector, claudeModel, - reasoningModel, defaultHaikuModel, defaultSonnetModel, defaultOpusModel, @@ -180,7 +177,6 @@ export function ClaudeFormFields({ const { t } = useTranslation(); const hasAnyAdvancedValue = !!( claudeModel || - reasoningModel || defaultHaikuModel || defaultSonnetModel || defaultOpusModel || @@ -656,18 +652,6 @@ export function ClaudeFormFields({ )} - {/* 推理模型 */} -
- - {t("providerForm.anthropicReasoningModel")} - - {renderModelInput( - "reasoningModel", - reasoningModel, - "ANTHROPIC_REASONING_MODEL", - )} -
- {/* 默认 Haiku */}
diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index 7f8cf560e..6d414c769 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -314,7 +314,6 @@ export function ProviderForm({ const { claudeModel, - reasoningModel, defaultHaikuModel, defaultSonnetModel, defaultOpusModel, @@ -1567,7 +1566,6 @@ export function ProviderForm({ onAutoSelectChange={setEndpointAutoSelect} shouldShowModelSelector={category !== "official"} claudeModel={claudeModel} - reasoningModel={reasoningModel} defaultHaikuModel={defaultHaikuModel} defaultSonnetModel={defaultSonnetModel} defaultOpusModel={defaultOpusModel} diff --git a/src/components/providers/forms/hooks/useModelState.ts b/src/components/providers/forms/hooks/useModelState.ts index 01278c4b5..d7c333e9d 100644 --- a/src/components/providers/forms/hooks/useModelState.ts +++ b/src/components/providers/forms/hooks/useModelState.ts @@ -14,11 +14,6 @@ function parseModelsFromConfig(settingsConfig: string) { const env = cfg?.env || {}; const model = typeof env.ANTHROPIC_MODEL === "string" ? env.ANTHROPIC_MODEL : ""; - const explicitReasoning = - typeof env.ANTHROPIC_REASONING_MODEL === "string" - ? env.ANTHROPIC_REASONING_MODEL - : ""; - const reasoning = explicitReasoning || model; const small = typeof env.ANTHROPIC_SMALL_FAST_MODEL === "string" ? env.ANTHROPIC_SMALL_FAST_MODEL @@ -36,15 +31,15 @@ function parseModelsFromConfig(settingsConfig: string) { ? env.ANTHROPIC_DEFAULT_OPUS_MODEL : model || small; - return { model, reasoning, haiku, sonnet, opus }; + return { model, haiku, sonnet, opus }; } catch { - return { model: "", reasoning: "", haiku: "", sonnet: "", opus: "" }; + return { model: "", haiku: "", sonnet: "", opus: "" }; } } /** * 管理模型选择状态 - * 支持 ANTHROPIC_MODEL, ANTHROPIC_REASONING_MODEL 和各类型默认模型 + * 支持 ANTHROPIC_MODEL 和各类型默认模型 */ export function useModelState({ settingsConfig, @@ -54,9 +49,6 @@ export function useModelState({ const [claudeModel, setClaudeModel] = useState( () => parseModelsFromConfig(settingsConfig).model, ); - const [reasoningModel, setReasoningModel] = useState( - () => parseModelsFromConfig(settingsConfig).reasoning, - ); const [defaultHaikuModel, setDefaultHaikuModel] = useState( () => parseModelsFromConfig(settingsConfig).haiku, ); @@ -93,11 +85,6 @@ export function useModelState({ const env = cfg?.env || {}; const model = typeof env.ANTHROPIC_MODEL === "string" ? env.ANTHROPIC_MODEL : ""; - const explicitReasoning = - typeof env.ANTHROPIC_REASONING_MODEL === "string" - ? env.ANTHROPIC_REASONING_MODEL - : ""; - const reasoning = explicitReasoning || model; const small = typeof env.ANTHROPIC_SMALL_FAST_MODEL === "string" ? env.ANTHROPIC_SMALL_FAST_MODEL @@ -116,7 +103,6 @@ export function useModelState({ : model || small; setClaudeModel(model || ""); - setReasoningModel(reasoning || ""); setDefaultHaikuModel(haiku || ""); setDefaultSonnetModel(sonnet || ""); setDefaultOpusModel(opus || ""); @@ -129,7 +115,6 @@ export function useModelState({ ( field: | "ANTHROPIC_MODEL" - | "ANTHROPIC_REASONING_MODEL" | "ANTHROPIC_DEFAULT_HAIKU_MODEL" | "ANTHROPIC_DEFAULT_SONNET_MODEL" | "ANTHROPIC_DEFAULT_OPUS_MODEL", @@ -138,7 +123,6 @@ export function useModelState({ isUserEditingRef.current = true; if (field === "ANTHROPIC_MODEL") setClaudeModel(value); - if (field === "ANTHROPIC_REASONING_MODEL") setReasoningModel(value); if (field === "ANTHROPIC_DEFAULT_HAIKU_MODEL") setDefaultHaikuModel(value); if (field === "ANTHROPIC_DEFAULT_SONNET_MODEL") @@ -173,8 +157,6 @@ export function useModelState({ return { claudeModel, setClaudeModel, - reasoningModel, - setReasoningModel, defaultHaikuModel, setDefaultHaikuModel, defaultSonnetModel, diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index cefc88203..117dd0b31 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -820,7 +820,6 @@ "visitWebsite": "Visit {{url}}", "anthropicModel": "Main Model", "anthropicSmallFastModel": "Fast Model", - "anthropicReasoningModel": "Reasoning Model (Thinking)", "apiFormat": "API Format", "apiFormatHint": "Select the input format for the provider's API", "fullUrlLabel": "Full URL", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 56c3968ae..b7b238884 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -820,7 +820,6 @@ "visitWebsite": "{{url}} を開く", "anthropicModel": "メインモデル", "anthropicSmallFastModel": "高速モデル", - "anthropicReasoningModel": "推論モデル(Thinking)", "apiFormat": "API フォーマット", "apiFormatHint": "プロバイダー API の入力フォーマットを選択", "fullUrlLabel": "フル URL", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 9059ef323..2ce2aeb4a 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -821,7 +821,6 @@ "visitWebsite": "访问 {{url}}", "anthropicModel": "主模型", "anthropicSmallFastModel": "快速模型", - "anthropicReasoningModel": "推理模型 (Thinking)", "apiFormat": "API 格式", "apiFormatHint": "选择供应商 API 的输入格式", "fullUrlLabel": "完整 URL",