From 76008b4720823747560705a8b0f2baa2a10bd1df Mon Sep 17 00:00:00 2001 From: sususu Date: Thu, 30 Jul 2026 22:44:01 +0800 Subject: [PATCH] fix(thinking): decouple Interactions effort summaries --- .../interactions_antigravity_request.go | 5 ++-- .../interactions_antigravity_test.go | 29 +++++++++++++++++++ test/summary_intent_translation_test.go | 3 ++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/internal/translator/antigravity/interactions/interactions_antigravity_request.go b/internal/translator/antigravity/interactions/interactions_antigravity_request.go index 2d00a4f38..53d9df0e6 100644 --- a/internal/translator/antigravity/interactions/interactions_antigravity_request.go +++ b/internal/translator/antigravity/interactions/interactions_antigravity_request.go @@ -195,12 +195,13 @@ func copyInteractionsReasoningToAntigravity(out []byte, root gjson.Result) []byt effort = strings.ToLower(strings.TrimSpace(reasoning.Get("thinking_level").String())) } if effort != "" { + // Thinking amount and summary visibility are independent. This OpenAI-style + // compatibility alias controls only the amount; includeThoughts is written + // below only for an explicit Interactions summary selector. if effort == "auto" { out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingBudget", -1) - out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", true) } else { out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingLevel", effort) - out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", effort != "none") } } if summary := reasoning.Get("summary"); summary.Exists() { diff --git a/internal/translator/antigravity/interactions/interactions_antigravity_test.go b/internal/translator/antigravity/interactions/interactions_antigravity_test.go index d6baa800e..d0052a7ba 100644 --- a/internal/translator/antigravity/interactions/interactions_antigravity_test.go +++ b/internal/translator/antigravity/interactions/interactions_antigravity_test.go @@ -68,6 +68,35 @@ func TestConvertInteractionsRequestToAntigravityPreservesGenerationConfig(t *tes } } +func TestConvertInteractionsReasoningToAntigravityKeepsSummaryIndependent(t *testing.T) { + tests := []struct { + name string + reasoning string + want bool + wantExists bool + }{ + {name: "effort only leaves summaries unspecified", reasoning: `{"effort":"high"}`}, + {name: "explicit auto enables summaries", reasoning: `{"effort":"high","summary":"auto"}`, want: true, wantExists: true}, + {name: "explicit none disables summaries", reasoning: `{"effort":"high","summary":"none"}`, wantExists: true}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + body := []byte(`{"model":"antigravity-test","input":"hi","reasoning":` + test.reasoning + `}`) + out := ConvertInteractionsRequestToAntigravity("antigravity-test", body, false) + if got := gjson.GetBytes(out, "request.generationConfig.thinkingConfig.thinkingLevel").String(); got != "high" { + t.Fatalf("thinkingLevel = %q, want high. Output: %s", got, out) + } + includeThoughts := gjson.GetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts") + if includeThoughts.Exists() != test.wantExists { + t.Fatalf("includeThoughts exists = %v, want %v. Output: %s", includeThoughts.Exists(), test.wantExists, out) + } + if test.wantExists && includeThoughts.Bool() != test.want { + t.Fatalf("includeThoughts = %v, want %v. Output: %s", includeThoughts.Bool(), test.want, out) + } + }) + } +} + func TestConvertAntigravityResponseToInteractionsNonStream(t *testing.T) { raw := []byte(`{"response":{"responseId":"resp_1","candidates":[{"content":{"role":"model","parts":[{"text":"ok"},{"functionCall":{"name":"lookup","id":"call_1","args":{"q":"x"}}}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":3,"candidatesTokenCount":2,"totalTokenCount":5}}}`) out := ConvertAntigravityResponseToInteractionsNonStream(context.Background(), "antigravity-test", nil, nil, raw, nil) diff --git a/test/summary_intent_translation_test.go b/test/summary_intent_translation_test.go index 06edfdad1..5cb013622 100644 --- a/test/summary_intent_translation_test.go +++ b/test/summary_intent_translation_test.go @@ -140,6 +140,9 @@ func TestSummaryIntentFinalPipeline(t *testing.T) { {name: "Summary-only control is stripped for non-thinking Gemini model", from: sdktranslator.FormatOpenAIResponse, to: sdktranslator.FormatGemini, model: "no-thinking-model", body: `{"model":"no-thinking-model","reasoning":{"summary":"auto"},"input":"hi"}`, path: "generationConfig.thinkingConfig"}, {name: "Interactions level alone keeps summaries omitted", from: sdktranslator.FormatInteractions, to: sdktranslator.FormatInteractions, model: "level-model", body: `{"model":"level-model","generation_config":{"thinking_level":"high"},"input":"hi"}`, path: "generation_config.thinking_summaries"}, {name: "Interactions auto survives its applier", from: sdktranslator.FormatInteractions, to: sdktranslator.FormatInteractions, model: "level-model", body: `{"model":"level-model","generation_config":{"thinking_level":"high","thinking_summaries":"auto"},"input":"hi"}`, path: "generation_config.thinking_summaries", want: "auto", wantExists: true}, + {name: "Interactions reasoning effort leaves Antigravity summaries unspecified", from: sdktranslator.FormatInteractions, to: sdktranslator.FormatAntigravity, model: "antigravity-budget-model", body: `{"model":"antigravity-budget-model","reasoning":{"effort":"high"},"input":"hi"}`, path: "request.generationConfig.thinkingConfig.includeThoughts"}, + {name: "Interactions reasoning summary auto reaches Antigravity", from: sdktranslator.FormatInteractions, to: sdktranslator.FormatAntigravity, model: "antigravity-budget-model", body: `{"model":"antigravity-budget-model","reasoning":{"effort":"high","summary":"auto"},"input":"hi"}`, path: "request.generationConfig.thinkingConfig.includeThoughts", want: "true", wantExists: true}, + {name: "Interactions reasoning summary none reaches Antigravity", from: sdktranslator.FormatInteractions, to: sdktranslator.FormatAntigravity, model: "antigravity-budget-model", body: `{"model":"antigravity-budget-model","reasoning":{"effort":"high","summary":"none"},"input":"hi"}`, path: "request.generationConfig.thinkingConfig.includeThoughts", want: "false", wantExists: true}, {name: "Deprecated Responses detail reaches Codex", from: sdktranslator.FormatOpenAIResponse, to: sdktranslator.FormatCodex, model: "level-model", body: `{"model":"level-model","reasoning":{"effort":"high","generate_summary":"detailed"},"input":"hi"}`, path: "reasoning.summary", want: "detailed", wantExists: true}, {name: "Gemini missing includeThoughts stays omitted on Claude", from: sdktranslator.FormatGemini, to: sdktranslator.FormatClaude, model: "claude-sonnet-4-6-model", body: `{"model":"claude-sonnet-4-6-model","generationConfig":{"thinkingConfig":{"thinkingLevel":"high"}},"contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, path: "thinking.display"}, {name: "Gemini true includeThoughts reaches Claude", from: sdktranslator.FormatGemini, to: sdktranslator.FormatClaude, model: "claude-sonnet-4-6-model", body: `{"model":"claude-sonnet-4-6-model","generationConfig":{"thinkingConfig":{"thinkingLevel":"high","includeThoughts":true}},"contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, path: "thinking.display", want: "summarized", wantExists: true},