-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: add adaptive thinking type and output_config.effort support #1516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add adaptive thinking type and output_config.effort support #1516
Conversation
Add support for Claude's `thinking.type = "adaptive"` and `output_config.effort` parameter across all translators (Gemini, Gemini CLI, Antigravity, OpenAI, Codex). - adaptive without effort defaults to level "high" - enabled without budget preserves backward-compatible behavior - output_config.effort mapped via MapClaudeEffortToLevel - effort "max" capped to "high" for OpenAI/Codex (only support low/medium/high) - Gemini translators use thinkingLevel instead of hardcoded budget, letting ApplyThinking resolve actual budget from model config - Strip output_config when model doesn't support thinking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @trongtrandp, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the system's capability to handle advanced thinking configurations from Claude models by integrating support for the new Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request adds support for Claude's adaptive thinking and output_config.effort parameter across various translators. The implementation is mostly correct, but I've identified a couple of areas for improvement. There's a recurring bug in both the codex and openai translators where the xhigh effort level isn't properly capped when derived from thinking.budget_tokens, which could lead to invalid requests. Additionally, there's a fair amount of duplicated code across the Gemini-family translators that could be refactored to improve maintainability. My specific comments provide detailed suggestions to address these issues.
| if thinkingConfig := rootResult.Get("thinking"); thinkingConfig.Exists() && thinkingConfig.IsObject() { | ||
| switch thinkingConfig.Get("type").String() { | ||
| case "enabled": | ||
| case "enabled", "adaptive": | ||
| if budgetTokens := thinkingConfig.Get("budget_tokens"); budgetTokens.Exists() { | ||
| budget := int(budgetTokens.Int()) | ||
| if effort, ok := thinking.ConvertBudgetToLevel(budget); ok && effort != "" { | ||
| reasoningEffort = effort | ||
| } | ||
| } else if thinkingConfig.Get("type").String() == "adaptive" { | ||
| // "adaptive" without budget_tokens: default to "high" | ||
| reasoningEffort = "high" | ||
| } | ||
| // "enabled" without budget_tokens: keep default "medium" | ||
| case "disabled": | ||
| if effort, ok := thinking.ConvertBudgetToLevel(0); ok && effort != "" { | ||
| reasoningEffort = effort | ||
| } | ||
| } | ||
| } | ||
| // output_config.effort takes priority (Claude Opus 4.6+ adaptive thinking) | ||
| if effort := rootResult.Get("output_config.effort"); effort.Exists() && effort.String() != "" { | ||
| if mapped := thinking.MapClaudeEffortToLevel(effort.String()); mapped != "" { | ||
| // Cap xhigh to high — Codex only supports low/medium/high | ||
| if mapped == string(thinking.LevelXHigh) { | ||
| mapped = string(thinking.LevelHigh) | ||
| } | ||
| reasoningEffort = mapped | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a bug in how the reasoningEffort is determined and capped. The current logic only caps the xhigh effort level to high when it's derived from output_config.effort. However, thinking.ConvertBudgetToLevel can also return xhigh for a large budget_tokens value, which is not supported by the Codex provider. This would result in an invalid xhigh value being sent.
I suggest refactoring this block to determine the reasoningEffort from both thinking and output_config.effort first, and then apply the capping logic once at the end before setting the value. This will fix the bug and also make the code cleaner.
if thinkingConfig := rootResult.Get("thinking"); thinkingConfig.Exists() && thinkingConfig.IsObject() {
thinkingType := thinkingConfig.Get("type").String()
switch thinkingType {
case "enabled", "adaptive":
if budgetTokens := thinkingConfig.Get("budget_tokens"); budgetTokens.Exists() {
budget := int(budgetTokens.Int())
if effort, ok := thinking.ConvertBudgetToLevel(budget); ok && effort != "" {
reasoningEffort = effort
}
} else if thinkingType == "adaptive" {
reasoningEffort = "high"
}
// "enabled" without budget_tokens keeps default "medium"
case "disabled":
if effort, ok := thinking.ConvertBudgetToLevel(0); ok && effort != "" {
reasoningEffort = effort
}
}
}
// output_config.effort takes priority (Claude Opus 4.6+ adaptive thinking)
if effort := rootResult.Get("output_config.effort"); effort.Exists() && effort.String() != "" {
if mapped := thinking.MapClaudeEffortToLevel(effort.String()); mapped != "" {
reasoningEffort = mapped
}
}
// Cap xhigh to high — Codex only supports low/medium/high
if reasoningEffort == string(thinking.LevelXHigh) {
reasoningEffort = string(thinking.LevelHigh)
}| // Map Anthropic thinking -> Gemini thinkingBudget/include_thoughts when type==enabled or adaptive | ||
| if t := gjson.GetBytes(rawJSON, "thinking"); enableThoughtTranslate && t.Exists() && t.IsObject() { | ||
| if t.Get("type").String() == "enabled" { | ||
| tType := t.Get("type").String() | ||
| if tType == "enabled" || tType == "adaptive" { | ||
| if b := t.Get("budget_tokens"); b.Exists() && b.Type == gjson.Number { | ||
| budget := int(b.Int()) | ||
| out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingBudget", budget) | ||
| out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.includeThoughts", true) | ||
| } else { | ||
| // No budget_tokens: signal auto so ApplyThinking resolves from model config | ||
| out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingBudget", -1) | ||
| out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.includeThoughts", true) | ||
| // adaptive without budget_tokens defaults to level "high" | ||
| if tType == "adaptive" { | ||
| out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingLevel", "high") | ||
| } | ||
| } | ||
| // output_config.effort overrides the default level — ApplyThinking converts to budget via model config | ||
| if effort := gjson.GetBytes(rawJSON, "output_config.effort"); effort.Exists() && effort.String() != "" { | ||
| if level := thinking.MapClaudeEffortToLevel(effort.String()); level != "" { | ||
| out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingLevel", level) | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's significant code duplication for handling Claude's thinking parameters across the Gemini-family translators (antigravity, gemini-cli, and gemini). This logic is nearly identical in all three files, with the only major difference being the JSON path prefix for setting the configuration.
To improve maintainability and reduce redundancy, consider extracting this logic into a shared helper function within the internal/translator/gemini/common package. This function could take the raw JSON, the output buffer, the path prefix, and the enableThoughtTranslate flag as arguments.
This would centralize the logic, making future updates easier and less error-prone.
Summary
thinking.type = "adaptive"andoutput_config.effortparameter across all translators (Gemini, Gemini CLI, Antigravity, OpenAI, Codex)adaptivewithouteffortdefaults to level"high";enabledwithoutbudget_tokenspreserves backward-compatible behavior per translatorthinkingLevelinstead of hardcoded budget (24576), lettingApplyThinkingresolve actual budget from model config / user payload configeffort = "max"capped to"high"for OpenAI/Codex (only supportlow/medium/high)output_configwhen model doesn't support thinkingChanges
thinking/apply.goextractClaudeConfighandlesadaptive+output_config.effort; addedMapClaudeEffortToLevelhelperthinking/strip.gooutput_configfor Claude providerantigravity_claude_request.goadaptivesupport +thinkingLevelinstead of hardcoded budgetgemini_claude_request.gorequest.prefix)gemini-cli_claude_request.goopenai_claude_request.goadaptive→"high",enabled→ auto;effortmapped + xhigh cappedcodex_claude_request.goadaptive→"high",enabled→"medium";effortmapped + xhigh cappedTest plan
thinking.type = "adaptive"withouteffortdefaults tohighacross all providersthinking.type = "enabled"withoutbudget_tokenspreserves existing behavioroutput_config.effort = "max"maps toxhighfor Gemini, capped tohighfor OpenAI/Codexoutput_config.effortoverrides adaptive default when both presentthinkingandoutput_config🤖 Generated with Claude Code