diff --git a/relay/channel/gemini/constant.go b/relay/channel/gemini/constant.go
index 2c972e37dca9..9063fe73813d 100644
--- a/relay/channel/gemini/constant.go
+++ b/relay/channel/gemini/constant.go
@@ -26,6 +26,19 @@ var ModelList = []string{
"embedding-001",
}
+// Abilities and limitations
+// See: https://ai.google.dev/gemini-api/docs/thinking#supported-models
+var ModelsWithThoughtsSummarySupport = []string{
+ "gemini-2.5-pro",
+ "gemini-2.5-flash",
+ "gemini-2.5-flash-lite",
+}
+
+// Models with minimum thinking budget limits (thinking cannot be completely disabled due to the model's design)
+var ModelsWithMinimumThinkingBudgetLimits = []string{
+ "gemini-2.5-pro",
+}
+
var SafetySettingList = []string{
"HARM_CATEGORY_HARASSMENT",
"HARM_CATEGORY_HATE_SPEECH",
diff --git a/relay/channel/gemini/helper.go b/relay/channel/gemini/helper.go
new file mode 100644
index 000000000000..b24a7736c49c
--- /dev/null
+++ b/relay/channel/gemini/helper.go
@@ -0,0 +1,25 @@
+package gemini
+
+import (
+ "strings"
+)
+
+func IsModelSupportThoughtsSummary(modelName string) bool {
+ for _, supportedModel := range ModelsWithThoughtsSummarySupport {
+ if strings.HasPrefix(modelName, supportedModel) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func IsModelWithMinimumThinkingBudgetLimits(modelName string) bool {
+ for _, specialModel := range ModelsWithMinimumThinkingBudgetLimits {
+ if strings.HasPrefix(modelName, specialModel) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go
index a65216ad0c27..f2e7ebd2e73a 100644
--- a/relay/channel/gemini/relay-gemini.go
+++ b/relay/channel/gemini/relay-gemini.go
@@ -79,56 +79,74 @@ func clampThinkingBudget(modelName string, budget int) int {
}
func ThinkingAdaptor(geminiRequest *GeminiChatRequest, info *relaycommon.RelayInfo) {
- if model_setting.GetGeminiSettings().ThinkingAdapterEnabled {
- modelName := info.UpstreamModelName
- isNew25Pro := strings.HasPrefix(modelName, "gemini-2.5-pro") &&
- !strings.HasPrefix(modelName, "gemini-2.5-pro-preview-05-06") &&
- !strings.HasPrefix(modelName, "gemini-2.5-pro-preview-03-25")
-
- if strings.Contains(modelName, "-thinking-") {
- parts := strings.SplitN(modelName, "-thinking-", 2)
- if len(parts) == 2 && parts[1] != "" {
- if budgetTokens, err := strconv.Atoi(parts[1]); err == nil {
- clampedBudget := clampThinkingBudget(modelName, budgetTokens)
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
- ThinkingBudget: common.GetPointer(clampedBudget),
- IncludeThoughts: true,
- }
+ if !model_setting.GetGeminiSettings().ThinkingAdapterEnabled {
+ return
+ }
+
+ modelName := info.UpstreamModelName
+
+ // 1. Process thinking budget settings
+ if strings.Contains(modelName, "-thinking-") {
+ parts := strings.SplitN(modelName, "-thinking-", 2)
+ if len(parts) == 2 && parts[1] != "" {
+ if budgetTokens, err := strconv.Atoi(parts[1]); err == nil {
+ clampedBudget := clampThinkingBudget(modelName, budgetTokens)
+ geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
+ ThinkingBudget: common.GetPointer(clampedBudget),
}
}
- } else if strings.HasSuffix(modelName, "-thinking") {
- unsupportedModels := []string{
- "gemini-2.5-pro-preview-05-06",
- "gemini-2.5-pro-preview-03-25",
- }
- isUnsupported := false
- for _, unsupportedModel := range unsupportedModels {
- if strings.HasPrefix(modelName, unsupportedModel) {
- isUnsupported = true
- break
- }
+ }
+ } else if strings.HasSuffix(modelName, "-thinking") {
+ unsupportedModels := []string{
+ "gemini-2.5-pro-preview-05-06",
+ "gemini-2.5-pro-preview-03-25",
+ }
+ isSupported := true
+ for _, unsupportedModel := range unsupportedModels {
+ if strings.HasPrefix(modelName, unsupportedModel) {
+ isSupported = false
+ break
}
+ }
- if isUnsupported {
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
- IncludeThoughts: true,
- }
- } else {
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
- IncludeThoughts: true,
- }
- if geminiRequest.GenerationConfig.MaxOutputTokens > 0 {
- budgetTokens := model_setting.GetGeminiSettings().ThinkingAdapterBudgetTokensPercentage * float64(geminiRequest.GenerationConfig.MaxOutputTokens)
- clampedBudget := clampThinkingBudget(modelName, int(budgetTokens))
- geminiRequest.GenerationConfig.ThinkingConfig.ThinkingBudget = common.GetPointer(clampedBudget)
- }
+ if isSupported && geminiRequest.GenerationConfig.MaxOutputTokens > 0 {
+ budgetTokens := model_setting.GetGeminiSettings().ThinkingAdapterBudgetTokensPercentage * float64(geminiRequest.GenerationConfig.MaxOutputTokens)
+ clampedBudget := clampThinkingBudget(modelName, int(budgetTokens))
+ geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
+ ThinkingBudget: common.GetPointer(clampedBudget),
}
- } else if strings.HasSuffix(modelName, "-nothinking") {
- if !isNew25Pro {
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
- ThinkingBudget: common.GetPointer(0),
- }
+ }
+ } else if strings.HasSuffix(modelName, "-nothinking") {
+ thinkingBudget := 0
+
+ // Reset to minimum allowed value if the model has minimum thinking budget limits
+ if IsModelWithMinimumThinkingBudgetLimits(modelName) {
+ thinkingBudget = clampThinkingBudget(modelName, 0)
+ }
+
+ geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
+ ThinkingBudget: common.GetPointer(thinkingBudget),
+ }
+ }
+
+ // 2. Process thoughts summary parameter settings
+ if model_setting.GetGeminiSettings().IncludeThoughtsSummaryEnabled && IsModelSupportThoughtsSummary(modelName) {
+ applyIncludeThoughts := true
+ allowZeroThinkingBudget := !IsModelWithMinimumThinkingBudgetLimits(modelName)
+
+ // Skip only if the model has -nothinking suffix and no minimum thinking budget limits.
+ // Keep applying if the model has minimum thinking budget limits since there still a little pieces
+ // of thoughts content in the response
+ if strings.HasSuffix(modelName, "-nothinking") && allowZeroThinkingBudget {
+ applyIncludeThoughts = false
+ }
+
+ if applyIncludeThoughts {
+ if geminiRequest.GenerationConfig.ThinkingConfig == nil {
+ geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{}
}
+
+ geminiRequest.GenerationConfig.ThinkingConfig.IncludeThoughts = applyIncludeThoughts
}
}
}
diff --git a/setting/model_setting/gemini.go b/setting/model_setting/gemini.go
index f132fec88162..dcd0fc79378c 100644
--- a/setting/model_setting/gemini.go
+++ b/setting/model_setting/gemini.go
@@ -11,6 +11,7 @@ type GeminiSettings struct {
SupportedImagineModels []string `json:"supported_imagine_models"`
ThinkingAdapterEnabled bool `json:"thinking_adapter_enabled"`
ThinkingAdapterBudgetTokensPercentage float64 `json:"thinking_adapter_budget_tokens_percentage"`
+ IncludeThoughtsSummaryEnabled bool `json:"include_thoughts_summary_enabled"`
}
// 默认配置
@@ -29,6 +30,7 @@ var defaultGeminiSettings = GeminiSettings{
},
ThinkingAdapterEnabled: false,
ThinkingAdapterBudgetTokensPercentage: 0.6,
+ IncludeThoughtsSummaryEnabled: true,
}
// 全局实例
diff --git a/web/src/components/settings/ModelSetting.js b/web/src/components/settings/ModelSetting.js
index 1d58363443e0..11b70f49bcd1 100644
--- a/web/src/components/settings/ModelSetting.js
+++ b/web/src/components/settings/ModelSetting.js
@@ -22,6 +22,7 @@ const ModelSetting = () => {
'general_setting.ping_interval_seconds': 60,
'gemini.thinking_adapter_enabled': false,
'gemini.thinking_adapter_budget_tokens_percentage': 0.6,
+ 'gemini.include_thoughts_summary_enabled': true,
});
let [loading, setLoading] = useState(false);
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 6b982b51e101..a3d1f677c2e6 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -1691,6 +1691,8 @@
"请先选择同步渠道": "Please select the synchronization channel first",
"与本地相同": "Same as local",
"未找到匹配的模型": "No matching model found",
+ "包含思考摘要": "Include thinking summaries",
+ "开启思考过程摘要并将收到的摘要包含在回复中。仅在模型支持和和思考预算不为0时生效。": "Enable thinking summaries and include the received summaries in the reply. Only effective when the model supports it and thinking budget is not 0.",
"暴露倍率接口": "Expose ratio API",
"支付设置": "Payment Settings",
"(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)",
diff --git a/web/src/pages/Setting/Model/SettingGeminiModel.js b/web/src/pages/Setting/Model/SettingGeminiModel.js
index a5daace63bbe..dcae865ae72b 100644
--- a/web/src/pages/Setting/Model/SettingGeminiModel.js
+++ b/web/src/pages/Setting/Model/SettingGeminiModel.js
@@ -30,6 +30,7 @@ export default function SettingGeminiModel(props) {
'gemini.supported_imagine_models': '',
'gemini.thinking_adapter_enabled': false,
'gemini.thinking_adapter_budget_tokens_percentage': 0.6,
+ 'gemini.include_thoughts_summary_enabled': true,
});
const refForm = useRef();
const [inputsRow, setInputsRow] = useState(inputs);
@@ -221,6 +222,21 @@ export default function SettingGeminiModel(props) {
/>
+
+
+
+ setInputs({
+ ...inputs,
+ 'gemini.include_thoughts_summary_enabled': value,
+ })
+ }
+ />
+
+