From 209d74062a0aa1b4d7d5f0be091c6374dce07890 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:24:42 +0800 Subject: [PATCH] fix(thinking): ensure includeThoughts is false for ModeNone in budget processing --- .../thinking/provider/antigravity/apply.go | 29 ++++++++++++------- internal/thinking/provider/gemini/apply.go | 15 ++++++---- internal/thinking/provider/geminicli/apply.go | 11 +++++-- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/internal/thinking/provider/antigravity/apply.go b/internal/thinking/provider/antigravity/apply.go index 7d5a5075..d202035f 100644 --- a/internal/thinking/provider/antigravity/apply.go +++ b/internal/thinking/provider/antigravity/apply.go @@ -139,6 +139,24 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig, budget := config.Budget + // Apply Claude-specific constraints first to get the final budget value + if isClaude && modelInfo != nil { + budget, result = a.normalizeClaudeBudget(budget, result, modelInfo) + // Check if budget was removed entirely + if budget == -2 { + return result, nil + } + } + + // For ModeNone, always set includeThoughts to false regardless of user setting. + // This ensures that when user requests budget=0 (disable thinking output), + // the includeThoughts is correctly set to false even if budget is clamped to min. + if config.Mode == thinking.ModeNone { + result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.thinkingBudget", budget) + result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.includeThoughts", false) + return result, nil + } + // Determine includeThoughts: respect user's explicit setting from original body if provided // Support both camelCase and snake_case variants var includeThoughts bool @@ -154,8 +172,6 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig, if !userSetIncludeThoughts { // No explicit setting, use default logic based on mode switch config.Mode { - case thinking.ModeNone: - includeThoughts = false case thinking.ModeAuto: includeThoughts = true default: @@ -163,15 +179,6 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig, } } - // Apply Claude-specific constraints - if isClaude && modelInfo != nil { - budget, result = a.normalizeClaudeBudget(budget, result, modelInfo) - // Check if budget was removed entirely - if budget == -2 { - return result, nil - } - } - result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.thinkingBudget", budget) result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.includeThoughts", includeThoughts) return result, nil diff --git a/internal/thinking/provider/gemini/apply.go b/internal/thinking/provider/gemini/apply.go index 39399c09..39bb4231 100644 --- a/internal/thinking/provider/gemini/apply.go +++ b/internal/thinking/provider/gemini/apply.go @@ -163,6 +163,15 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig) budget := config.Budget + // For ModeNone, always set includeThoughts to false regardless of user setting. + // This ensures that when user requests budget=0 (disable thinking output), + // the includeThoughts is correctly set to false even if budget is clamped to min. + if config.Mode == thinking.ModeNone { + result, _ = sjson.SetBytes(result, "generationConfig.thinkingConfig.thinkingBudget", budget) + result, _ = sjson.SetBytes(result, "generationConfig.thinkingConfig.includeThoughts", false) + return result, nil + } + // Determine includeThoughts: respect user's explicit setting from original body if provided // Support both camelCase and snake_case variants var includeThoughts bool @@ -177,13 +186,7 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig) if !userSetIncludeThoughts { // No explicit setting, use default logic based on mode - // ModeNone semantics: - // - ModeNone + Budget=0: completely disable thinking - // - ModeNone + Budget>0: forced to think but hide output (includeThoughts=false) - // When ZeroAllowed=false, ValidateConfig clamps Budget to Min while preserving ModeNone. switch config.Mode { - case thinking.ModeNone: - includeThoughts = false case thinking.ModeAuto: includeThoughts = true default: diff --git a/internal/thinking/provider/geminicli/apply.go b/internal/thinking/provider/geminicli/apply.go index 476e5b6d..5908b6bc 100644 --- a/internal/thinking/provider/geminicli/apply.go +++ b/internal/thinking/provider/geminicli/apply.go @@ -124,6 +124,15 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig) budget := config.Budget + // For ModeNone, always set includeThoughts to false regardless of user setting. + // This ensures that when user requests budget=0 (disable thinking output), + // the includeThoughts is correctly set to false even if budget is clamped to min. + if config.Mode == thinking.ModeNone { + result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.thinkingBudget", budget) + result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.includeThoughts", false) + return result, nil + } + // Determine includeThoughts: respect user's explicit setting from original body if provided // Support both camelCase and snake_case variants var includeThoughts bool @@ -139,8 +148,6 @@ func (a *Applier) applyBudgetFormat(body []byte, config thinking.ThinkingConfig) if !userSetIncludeThoughts { // No explicit setting, use default logic based on mode switch config.Mode { - case thinking.ModeNone: - includeThoughts = false case thinking.ModeAuto: includeThoughts = true default: