Files
MyGoNavi/internal/ai/provider/thinking_intensity.go
Syngnat 4fcd7ff61f feat(ai): 支持 MCP 少量查数与分服务商思考强度,修复 DeepSeek Anthropic
- MCP HTTP 默认可注册 execute_sql,行数默认 50/上限 200,设置页可切换仅结构模式
- 思考强度按 OpenAI/Anthropic/DeepSeek/Gemini 体系映射档位与请求参数
- 自定义端点 DeepSeek Anthropic 自动纠正 base 路径并回传 thinking 块
- 补充前后端回归测试与多语言文案
2026-07-09 11:34:38 +08:00

228 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package provider
import (
"strings"
"GoNavi-Wails/internal/ai"
)
// ThinkingProfile 描述不同服务商/协议下的思考档位体系。
type ThinkingProfile string
const (
ThinkingProfileOpenAI ThinkingProfile = "openai" // none/minimal/low/medium/high/xhigh
ThinkingProfileAnthropic ThinkingProfile = "anthropic" // off/low/medium/high/xhigh/max
ThinkingProfileDeepSeek ThinkingProfile = "deepseek" // off/low/medium/high
ThinkingProfileGemini ThinkingProfile = "gemini" // off/minimal/low/medium/high
ThinkingProfileGeneric ThinkingProfile = "generic" // off/low/medium/high
)
// NormalizeThinkingIntensity 保留服务商原生档位字面量,不做跨档压缩。
// 空值表示“未设置 / 走供应商默认”。
func NormalizeThinkingIntensity(raw string) ai.ThinkingIntensity {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "":
return ""
case "auto", "default":
return ""
case "off", "disabled", "false", "0":
return ai.ThinkingIntensityOff
case "none":
// OpenAI 用 none 表示不推理;与 off 同义但保留字面量供映射。
return ai.ThinkingIntensity("none")
case "minimal", "min":
return ai.ThinkingIntensity("minimal")
case "low", "light":
return ai.ThinkingIntensityLow
case "medium", "mid", "normal", "standard":
return ai.ThinkingIntensityMedium
case "high":
return ai.ThinkingIntensityHigh
case "xhigh", "extra_high", "extra-high", "xh":
return ai.ThinkingIntensity("xhigh")
case "max", "maximum", "heavy":
return ai.ThinkingIntensity("max")
default:
return ""
}
}
// ResolveThinkingProfile 根据供应商类型、API 格式、端点与模型选择档位体系。
func ResolveThinkingProfile(providerType, apiFormat, baseURL, model string) ThinkingProfile {
ptype := strings.ToLower(strings.TrimSpace(providerType))
format := strings.ToLower(strings.TrimSpace(apiFormat))
base := strings.ToLower(strings.TrimSpace(baseURL))
modelName := strings.ToLower(strings.TrimSpace(model))
if isDeepSeekHost(baseURL) || strings.Contains(base, "deepseek") || strings.Contains(modelName, "deepseek") {
return ThinkingProfileDeepSeek
}
if ptype == "gemini" || format == "gemini" || strings.Contains(base, "generativelanguage.googleapis.com") || strings.Contains(base, "googleapis.com/v1beta") {
return ThinkingProfileGemini
}
if ptype == "anthropic" || format == "anthropic" {
return ThinkingProfileAnthropic
}
if ptype == "openai" || format == "" || format == "openai" {
return ThinkingProfileOpenAI
}
return ThinkingProfileGeneric
}
// ThinkingIntensityOptions 返回某 profile 支持的档位UI / 校验用)。
func ThinkingIntensityOptions(profile ThinkingProfile) []string {
switch profile {
case ThinkingProfileOpenAI:
return []string{"none", "minimal", "low", "medium", "high", "xhigh"}
case ThinkingProfileAnthropic:
return []string{"off", "low", "medium", "high", "xhigh", "max"}
case ThinkingProfileDeepSeek:
return []string{"off", "low", "medium", "high"}
case ThinkingProfileGemini:
return []string{"off", "minimal", "low", "medium", "high"}
default:
return []string{"off", "low", "medium", "high"}
}
}
func clampThinkingIntensityToProfile(raw string, profile ThinkingProfile) ai.ThinkingIntensity {
intensity := NormalizeThinkingIntensity(raw)
if intensity == "" {
return ""
}
// 统一 off/none
normalized := string(intensity)
if intensity == ai.ThinkingIntensityOff {
if profile == ThinkingProfileOpenAI {
normalized = "none"
} else {
normalized = "off"
}
}
if intensity == ai.ThinkingIntensity("none") && profile != ThinkingProfileOpenAI {
normalized = "off"
}
allowed := ThinkingIntensityOptions(profile)
for _, opt := range allowed {
if opt == normalized {
return ai.ThinkingIntensity(normalized)
}
}
// 跨体系兜底:尽量落到最接近的可用档位
switch normalized {
case "xhigh", "max":
if profile == ThinkingProfileOpenAI {
return ai.ThinkingIntensity("xhigh")
}
if profile == ThinkingProfileAnthropic && normalized == "max" {
// anthropic 支持 max此处仅当 max 不在 allowed 时才会进入(理论上不会)
return ai.ThinkingIntensity("max")
}
return ai.ThinkingIntensityHigh
case "minimal":
if profile == ThinkingProfileGemini || profile == ThinkingProfileOpenAI {
return ai.ThinkingIntensity("minimal")
}
return ai.ThinkingIntensityLow
case "none", "off":
if profile == ThinkingProfileOpenAI {
return ai.ThinkingIntensity("none")
}
return ai.ThinkingIntensityOff
default:
return ai.ThinkingIntensityMedium
}
}
func anthropicThinkingBudgetTokens(intensity ai.ThinkingIntensity) int {
switch intensity {
case ai.ThinkingIntensityLow, ai.ThinkingIntensity("minimal"):
return 1024
case ai.ThinkingIntensityHigh:
return 16000
case ai.ThinkingIntensity("xhigh"):
return 32000
case ai.ThinkingIntensity("max"):
return 64000
default:
return 8192
}
}
// anthropicOutputEffort 映射到 Anthropic/DeepSeek Anthropic 的 effort 字面量。
func anthropicOutputEffort(intensity ai.ThinkingIntensity) string {
switch intensity {
case ai.ThinkingIntensityOff, ai.ThinkingIntensity("none"), "":
return ""
case ai.ThinkingIntensityLow, ai.ThinkingIntensity("minimal"):
return "low"
case ai.ThinkingIntensityMedium:
return "medium"
case ai.ThinkingIntensityHigh:
return "high"
case ai.ThinkingIntensity("xhigh"):
return "xhigh"
case ai.ThinkingIntensity("max"):
return "max"
default:
return "medium"
}
}
// openAIReasoningEffort 映射到 OpenAI reasoning_effort。
func openAIReasoningEffort(intensity ai.ThinkingIntensity) string {
switch intensity {
case "", ai.ThinkingIntensityOff:
return ""
case ai.ThinkingIntensity("none"):
return "none"
case ai.ThinkingIntensity("minimal"):
return "minimal"
case ai.ThinkingIntensityLow:
return "low"
case ai.ThinkingIntensityMedium:
return "medium"
case ai.ThinkingIntensityHigh:
return "high"
case ai.ThinkingIntensity("xhigh"), ai.ThinkingIntensity("max"):
return "xhigh"
default:
return ""
}
}
// geminiThinkingLevel 映射到 Gemini thinking_level。
func geminiThinkingLevel(intensity ai.ThinkingIntensity) string {
switch intensity {
case ai.ThinkingIntensityOff, ai.ThinkingIntensity("none"):
return ""
case ai.ThinkingIntensity("minimal"):
return "minimal"
case ai.ThinkingIntensityLow:
return "low"
case ai.ThinkingIntensityMedium:
return "medium"
case ai.ThinkingIntensityHigh, ai.ThinkingIntensity("xhigh"), ai.ThinkingIntensity("max"):
return "high"
default:
return ""
}
}
// geminiThinkingBudget 给 Gemini 2.5 系列 thinking_budget 兜底。
func geminiThinkingBudget(intensity ai.ThinkingIntensity) int {
switch intensity {
case ai.ThinkingIntensityOff, ai.ThinkingIntensity("none"):
return 0
case ai.ThinkingIntensity("minimal"), ai.ThinkingIntensityLow:
return 1024
case ai.ThinkingIntensityMedium:
return 8192
case ai.ThinkingIntensityHigh, ai.ThinkingIntensity("xhigh"), ai.ThinkingIntensity("max"):
return 24576
default:
return -1 // dynamic
}
}