From e7353772184b31d13758504574758f0e8dead6f6 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Sun, 15 Jun 2025 21:12:56 +0800 Subject: [PATCH 1/3] feat: implement thinking budget control in model name --- relay/channel/gemini/adaptor.go | 7 ++- relay/channel/gemini/relay-gemini.go | 43 ++++++++++++++++++- setting/operation_setting/model-ratio.go | 18 ++++++-- web/src/i18n/locales/en.json | 3 ++ .../pages/Setting/Model/SettingGeminiModel.js | 7 +-- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/relay/channel/gemini/adaptor.go b/relay/channel/gemini/adaptor.go index e6f66d5f..a81eb3a9 100644 --- a/relay/channel/gemini/adaptor.go +++ b/relay/channel/gemini/adaptor.go @@ -72,8 +72,11 @@ func (a *Adaptor) Init(info *relaycommon.RelayInfo) { func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) { if model_setting.GetGeminiSettings().ThinkingAdapterEnabled { - // suffix -thinking and -nothinking - if strings.HasSuffix(info.OriginModelName, "-thinking") { + // 新增逻辑:处理 -thinking- 格式 + if strings.Contains(info.OriginModelName, "-thinking-") { + parts := strings.Split(info.UpstreamModelName, "-thinking-") + info.UpstreamModelName = parts[0] + } else if strings.HasSuffix(info.OriginModelName, "-thinking") { // 旧的适配 info.UpstreamModelName = strings.TrimSuffix(info.UpstreamModelName, "-thinking") } else if strings.HasSuffix(info.OriginModelName, "-nothinking") { info.UpstreamModelName = strings.TrimSuffix(info.UpstreamModelName, "-nothinking") diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index e2288faf..b65d5af7 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -12,6 +12,7 @@ import ( "one-api/relay/helper" "one-api/service" "one-api/setting/model_setting" + "strconv" "strings" "unicode/utf8" @@ -36,6 +37,13 @@ var geminiSupportedMimeTypes = map[string]bool{ "video/flv": true, } +// Gemini 允许的思考预算范围 +const ( + pro25MinBudget = 128 + pro25MaxBudget = 32768 + flash25MaxBudget = 24576 +) + // Setting safety to the lowest possible values since Gemini is already powerless enough func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*GeminiChatRequest, error) { @@ -57,7 +65,40 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon } if model_setting.GetGeminiSettings().ThinkingAdapterEnabled { - if strings.HasSuffix(info.OriginModelName, "-thinking") { + // 新增逻辑:处理 -thinking- 格式 + if strings.Contains(info.OriginModelName, "-thinking-") { + parts := strings.SplitN(info.OriginModelName, "-thinking-", 2) + if len(parts) == 2 && parts[1] != "" { + if budgetTokens, err := strconv.Atoi(parts[1]); err == nil { + // 从模型名称成功解析预算 + isNew25Pro := strings.HasPrefix(info.OriginModelName, "gemini-2.5-pro") && + !strings.HasPrefix(info.OriginModelName, "gemini-2.5-pro-preview-05-06") && + !strings.HasPrefix(info.OriginModelName, "gemini-2.5-pro-preview-03-25") + + if isNew25Pro { + // 新的2.5pro模型:ThinkingBudget范围为128-32768 + if budgetTokens < pro25MinBudget { + budgetTokens = pro25MinBudget + } else if budgetTokens > pro25MaxBudget { + budgetTokens = pro25MaxBudget + } + } else { + // 其他模型:ThinkingBudget范围为0-24576 + if budgetTokens < 0 { + budgetTokens = 0 + } else if budgetTokens > flash25MaxBudget { + budgetTokens = flash25MaxBudget + } + } + + geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ + ThinkingBudget: common.GetPointer(budgetTokens), + IncludeThoughts: true, + } + } + // 如果解析失败,则不设置ThinkingConfig,静默处理 + } + } else if strings.HasSuffix(info.OriginModelName, "-thinking") { // 保留旧逻辑以兼容 // 硬编码不支持 ThinkingBudget 的旧模型 unsupportedModels := []string{ "gemini-2.5-pro-preview-05-06", diff --git a/setting/operation_setting/model-ratio.go b/setting/operation_setting/model-ratio.go index 700a7c4e..fa6f9560 100644 --- a/setting/operation_setting/model-ratio.go +++ b/setting/operation_setting/model-ratio.go @@ -142,6 +142,11 @@ var defaultModelRatio = map[string]float64{ "gemini-2.5-flash-preview-04-17": 0.075, "gemini-2.5-flash-preview-04-17-thinking": 0.075, "gemini-2.5-flash-preview-04-17-nothinking": 0.075, + "gemini-2.5-flash-preview-05-20": 0.075, + "gemini-2.5-flash-preview-05-20-thinking": 0.075, + "gemini-2.5-flash-preview-05-20-nothinking": 0.075, + "gemini-2.5-flash-thinking-*": 0.075, // 用于为后续所有2.5 flash thinking budget 模型设置默认倍率 + "gemini-2.5-pro-thinking-*": 0.625, // 用于为后续所有2.5 pro thinking budget 模型设置默认倍率 "text-embedding-004": 0.001, "chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens "chatglm_pro": 0.7143, // ¥0.01 / 1k tokens @@ -345,7 +350,14 @@ func UpdateModelRatioByJSONString(jsonStr string) error { func GetModelRatio(name string) (float64, bool) { modelRatioMapMutex.RLock() defer modelRatioMapMutex.RUnlock() - + // 处理带有思考预算的模型名称,方便统一定价 + handleThinkingBudgetModel := func(prefix, wildcard string) { + if strings.HasPrefix(name, prefix) && strings.Contains(name, "-thinking-") { + name = wildcard + } + } + handleThinkingBudgetModel("gemini-2.5-flash", "gemini-2.5-flash-thinking-*") + handleThinkingBudgetModel("gemini-2.5-pro", "gemini-2.5-pro-thinking-*") if strings.HasPrefix(name, "gpt-4-gizmo") { name = "gpt-4-gizmo-*" } @@ -470,9 +482,9 @@ func getHardcodedCompletionModelRatio(name string) (float64, bool) { return 4, true } else if strings.HasPrefix(name, "gemini-2.0") { return 4, true - } else if strings.HasPrefix(name, "gemini-2.5-pro-preview") { + } else if strings.HasPrefix(name, "gemini-2.5-pro") { // 移除preview来增加兼容性,这里假设正式版的倍率和preview一致 return 8, true - } else if strings.HasPrefix(name, "gemini-2.5-flash-preview") { + } else if strings.HasPrefix(name, "gemini-2.5-flash") { // 同上 if strings.HasSuffix(name, "-nothinking") { return 4, false } else { diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index ba23ca5c..d563aaad 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1373,6 +1373,9 @@ "示例": "Example", "缺省 MaxTokens": "Default MaxTokens", "启用Claude思考适配(-thinking后缀)": "Enable Claude thinking adaptation (-thinking suffix)", + "启用Gemini思考后缀适配": "Enable Gemini thinking suffix adaptation", + "适配-thinking、-thinking-预算数字和-nothinking后缀": "Adapt -thinking, -thinking-budgetNumber, and -nothinking suffixes", + "思考预算占比": "Thinking budget ratio", "Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Claude thinking adaptation BudgetTokens = MaxTokens * BudgetTokens percentage", "思考适配 BudgetTokens 百分比": "Thinking adaptation BudgetTokens percentage", "0.1-1之间的小数": "Decimal between 0.1 and 1", diff --git a/web/src/pages/Setting/Model/SettingGeminiModel.js b/web/src/pages/Setting/Model/SettingGeminiModel.js index b802af1a..1d28ae92 100644 --- a/web/src/pages/Setting/Model/SettingGeminiModel.js +++ b/web/src/pages/Setting/Model/SettingGeminiModel.js @@ -173,7 +173,8 @@ export default function SettingGeminiModel(props) { {t( "和Claude不同,默认情况下Gemini的思考模型会自动决定要不要思考,就算不开启适配模型也可以正常使用," + - "如果您需要计费,推荐设置无后缀模型价格按思考价格设置" + "如果您需要计费,推荐设置无后缀模型价格按思考价格设置。" + + "支持使用 gemini-2.5-pro-preview-06-05-thinking-128 格式来精确传递思考预算。" )} @@ -183,7 +184,7 @@ export default function SettingGeminiModel(props) { setInputs({ ...inputs, @@ -205,7 +206,7 @@ export default function SettingGeminiModel(props) { Date: Sun, 15 Jun 2025 23:40:58 +0800 Subject: [PATCH 2/3] update i18n --- web/src/i18n/locales/en.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index d563aaad..8316f8a2 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1373,6 +1373,9 @@ "示例": "Example", "缺省 MaxTokens": "Default MaxTokens", "启用Claude思考适配(-thinking后缀)": "Enable Claude thinking adaptation (-thinking suffix)", + "和Claude不同,默认情况下Gemini的思考模型会自动决定要不要思考,就算不开启适配模型也可以正常使用,": "Unlike Claude, Gemini's thinking model automatically decides whether to think by default, and can be used normally even without enabling the adaptation model.", + "如果您需要计费,推荐设置无后缀模型价格按思考价格设置。": "If you need billing, it is recommended to set the no-suffix model price according to the thinking price.", + "支持使用 gemini-2.5-pro-preview-06-05-thinking-128 格式来精确传递思考预算。": "Supports using gemini-2.5-pro-preview-06-05-thinking-128 format to precisely pass thinking budget.", "启用Gemini思考后缀适配": "Enable Gemini thinking suffix adaptation", "适配-thinking、-thinking-预算数字和-nothinking后缀": "Adapt -thinking, -thinking-budgetNumber, and -nothinking suffixes", "思考预算占比": "Thinking budget ratio", From 1294d286ee0decfd73e14bc907cdeb736bc4b4de Mon Sep 17 00:00:00 2001 From: RedwindA Date: Mon, 16 Jun 2025 19:41:42 +0800 Subject: [PATCH 3/3] refactor: replace inline closure with a helper function --- setting/operation_setting/model-ratio.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/setting/operation_setting/model-ratio.go b/setting/operation_setting/model-ratio.go index fa6f9560..5155b2fc 100644 --- a/setting/operation_setting/model-ratio.go +++ b/setting/operation_setting/model-ratio.go @@ -347,17 +347,20 @@ func UpdateModelRatioByJSONString(jsonStr string) error { return json.Unmarshal([]byte(jsonStr), &modelRatioMap) } +// 处理带有思考预算的模型名称,方便统一定价 +func handleThinkingBudgetModel(name, prefix, wildcard string) string { + if strings.HasPrefix(name, prefix) && strings.Contains(name, "-thinking-") { + return wildcard + } + return name +} + func GetModelRatio(name string) (float64, bool) { modelRatioMapMutex.RLock() defer modelRatioMapMutex.RUnlock() - // 处理带有思考预算的模型名称,方便统一定价 - handleThinkingBudgetModel := func(prefix, wildcard string) { - if strings.HasPrefix(name, prefix) && strings.Contains(name, "-thinking-") { - name = wildcard - } - } - handleThinkingBudgetModel("gemini-2.5-flash", "gemini-2.5-flash-thinking-*") - handleThinkingBudgetModel("gemini-2.5-pro", "gemini-2.5-pro-thinking-*") + + name = handleThinkingBudgetModel(name, "gemini-2.5-flash", "gemini-2.5-flash-thinking-*") + name = handleThinkingBudgetModel(name, "gemini-2.5-pro", "gemini-2.5-pro-thinking-*") if strings.HasPrefix(name, "gpt-4-gizmo") { name = "gpt-4-gizmo-*" }