Skip to content

fix(antigravity): default includeThoughts for modern Gemini models - #4180

Merged
diegosouzapw merged 3 commits into
cheaper-inference:release/v3.8.29from
dhaern:fix/antigravity-include-thoughts-default
Jun 18, 2026
Merged

diegosouzapw merged 3 commits into
cheaper-inference:release/v3.8.29from
dhaern:fix/antigravity-include-thoughts-default

Conversation

@dhaern

@dhaern dhaern commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Problem

Gemini 2.5+ models always have thinking capability, but the translator only set includeThoughts: true when the client explicitly requested it via reasoning_effort or thinking.type.

Without this flag, the upstream does not mark thought parts with thought: true in its response. The response translator (gemini-to-openai.ts) checks part.thought === true to route text to reasoning_content — so without the flag, the model's internal reasoning leaks into visible content instead of being separated into reasoning_content.

This is especially problematic for smaller models (e.g. gemini-3.5-flash-low) that tend to reason extensively about tool names, instructions, and internal logic when their reasoning has no separate channel. Users see the model's internal deliberation (including echoed system prompt fragments) as regular assistant output.

Root Cause

In openai-to-gemini.ts, thinkingConfig is only set in two cases:

  1. Client sends reasoning_effort (OpenAI format) → maps to a budget + includeThoughts: true
  2. Client sends thinking.type: "enabled" (Claude format) → sets budget + includeThoughts: true

If neither is present (the common case for clients that don't explicitly request thinking), no thinkingConfig is sent at all, and the upstream treats all output as regular content.

Fix

Add a default thinkingConfig with includeThoughts: true for all modern Gemini models (2.5+) when the client hasn't explicitly requested thinking.

if (!result.generationConfig.thinkingConfig) {
  const modelLower = model.toLowerCase();
  if (
    modelLower.includes("gemini") &&
    !modelLower.includes("gemini-1") &&
    !modelLower.includes("gemini-2.0")
  ) {
    result.generationConfig.thinkingConfig = {
      thinkingBudget: getDefaultThinkingBudget(model) || 24576,
      includeThoughts: true,
    };
  }
}

Design decisions

  • Excludes legacy models (gemini-1.x, gemini-2.0) that lack thinking capability. All other Gemini models (2.5+, 3.x, future 4.x) are included automatically.
  • Uses getDefaultThinkingBudget(model) when available, falling back to 24576 (the highest budget from the 2.5 era). This respects per-model caps while ensuring a sensible default.
  • Does not override explicit client requests. The guard if (!result.generationConfig.thinkingConfig) ensures that reasoning_effort and thinking.type still take precedence.

Files changed

  • open-sse/translator/request/openai-to-gemini.ts (+20 lines)

Validation

  • Built and tested locally against gemini-3.5-flash-low and gemini-3.5-flash-high via Antigravity.
  • Confirmed tokens.reasoning is now non-zero in call logs (e.g. 483 reasoning tokens for flash-low), indicating the upstream is generating and returning thought parts.
  • Tool calls (glob, grep, read) execute correctly without _ide suffix leaks.
  • No <internal_reminder> or internal reasoning echoed in visible content.

Compatibility

No breaking changes. Models that previously received no thinkingConfig now receive a default one, which only affects how the upstream marks thought parts — it does not change the model's behavior or capabilities. Clients that explicitly request thinking are unaffected.

Gemini 2.5+ models always have thinking capability, but the translator
only set includeThoughts when the client explicitly requested it via
reasoning_effort or thinking.type. Without this flag, the upstream does
not mark thought parts with thought:true, so the response translator
cannot route reasoning to reasoning_content — the model's internal
reasoning leaks into visible content instead.

This is especially problematic for smaller models (e.g. gemini-3.5-flash)
that tend to reason extensively about tool names and instructions when
their reasoning has no separate channel.

Fix: set thinkingConfig with includeThoughts:true by default for all
Gemini models except legacy 1.x and 2.0 (which lack thinking). Uses
getDefaultThinkingBudget(model) with a 24576 fallback.
@dhaern
dhaern requested a review from diegosouzapw as a code owner June 18, 2026 15:38

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

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 introduces default thinking capability configuration for modern Gemini models (2.5+) in open-sse/translator/request/openai-to-gemini.ts to prevent reasoning leakage. The review feedback highlights a style guide violation for missing tests, an issue with the exclusion logic that incorrectly filters out gemini-2.0-flash-thinking-exp, and a recommendation to wrap the fallback thinking budget in capThinkingBudget to prevent potential API errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +310 to +315
// 3. Default: all modern Gemini models (2.5+) have thinking capability.
// If the client didn't explicitly request thinking (via reasoning_effort or
// thinking.type), still set includeThoughts so the upstream marks thought
// parts with thought:true. Without this, the model's reasoning leaks into
// visible content instead of being routed to reasoning_content by the
// response translator. (#4170)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

According to the Repository Style Guide (Rule 2.9), tests must always be included when changing production code under open-sse/. Since this PR modifies open-sse/translator/request/openai-to-gemini.ts but does not include any test changes, it violates this rule.

Please add a test case to tests/fixtures/translation/openai-to-gemini.json to verify that the default thinkingConfig is correctly applied to modern Gemini models. For example:

{
  "name": "openai-to-gemini/modern-gemini-default-thinking",
  "sourceFormat": "openai",
  "targetFormat": "gemini",
  "input": {
    "model": "gemini-2.5-flash",
    "messages": [{ "role": "user", "content": "Hello Gemini!" }]
  }
}
References
  1. Always include tests when changing production code (src/, open-sse/, electron/, bin/). (link)

Comment on lines +318 to +322
if (
modelLower.includes("gemini") &&
!modelLower.includes("gemini-1") &&
!modelLower.includes("gemini-2.0")
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current exclusion logic !modelLower.includes("gemini-2.0") will incorrectly exclude gemini-2.0-flash-thinking-exp, which is a valid thinking model and should have includeThoughts: true enabled by default to prevent reasoning leakage.

We should update the condition to allow models that explicitly contain thinking even if they are in the gemini-2.0 family.

Suggested change
if (
modelLower.includes("gemini") &&
!modelLower.includes("gemini-1") &&
!modelLower.includes("gemini-2.0")
) {
if (
modelLower.includes("gemini") &&
!modelLower.includes("gemini-1") &&
(!modelLower.includes("gemini-2.0") || modelLower.includes("thinking"))
) {

Comment on lines +323 to +326
result.generationConfig.thinkingConfig = {
thinkingBudget: getDefaultThinkingBudget(model) || 24576,
includeThoughts: true,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent potential 400 Bad Request errors from the Gemini API if a model has a lower thinking budget cap than 24576, we should wrap the fallback budget in capThinkingBudget(model, 24576). This ensures the fallback budget is always safe and within the model's capabilities.

Suggested change
result.generationConfig.thinkingConfig = {
thinkingBudget: getDefaultThinkingBudget(model) || 24576,
includeThoughts: true,
};
result.generationConfig.thinkingConfig = {
thinkingBudget: getDefaultThinkingBudget(model) || capThinkingBudget(model, 24576),
includeThoughts: true,
};

…ion, test

- Wrap fallback budget in capThinkingBudget(model, 24576) to prevent
  400 errors for models with lower caps
- Allow gemini-2.0-flash-thinking-exp (and similar) through the exclusion
  by checking for 'thinking' in the model name
- Add test fixture for default thinkingConfig on modern Gemini models
@dhaern

dhaern commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Hi @gemini-code-assist, thanks for the review. All three points addressed in 072b05f:

  1. Missing tests (high) — Added test fixture openai-to-gemini/modern-gemini-default-thinking to tests/fixtures/translation/openai-to-gemini.json covering the default thinkingConfig path for modern Gemini models.

  2. gemini-2.0-flash-thinking-exp exclusion (medium) — Updated the condition to allow thinking-capable 2.0 models through: (!modelLower.includes("gemini-2.0") || modelLower.includes("thinking")).

  3. Wrap fallback in capThinkingBudget (medium) — Changed 24576 to capThinkingBudget(model, 24576) so the fallback is always within the model's capabilities and never triggers a 400.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Thanks for the updates, @dhaern. These changes look excellent and correctly address the points raised. The inclusion of the test fixture and the refinement of the thinking budget logic ensure both correctness and robustness for modern Gemini models. The PR is in great shape.

…ludeThoughts

The default-includeThoughts change for modern Gemini (2.5+) alters the
openai->gemini output, so the existing golden snapshots basic-chat
(gemini-2.5-flash) and with-system (gemini-2.5-pro) now carry the injected
thinkingConfig, and the new modern-gemini-default-thinking snapshot (its
fixture case was added but its snapshot was not committed) is included.
61/61 translator + golden tests pass.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
@diegosouzapw
diegosouzapw merged commit 89cd954 into cheaper-inference:release/v3.8.29 Jun 18, 2026
3 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Merged into release/v3.8.29 🎉 Thanks @dhaern — nice correctness fix; aligning OmniRoute with Gemini 2.5+'s always-on thinking so reasoning lands in reasoning_content is the right call. On review we just regenerated the two existing gemini golden snapshots (they're also 2.5 models, so they now carry the thinkingConfig) and committed the modern-gemini-default-thinking snapshot whose fixture case you added. All 61 translator + golden tests green. 🙏

diegosouzapw added a commit that referenced this pull request Jun 18, 2026
…iscovery (#3931 bug 3, #3737) (#4185)

Integrated into release/v3.8.29 — add moonshotai/kimi-k2.7-code to the kmca (kimi-coding-apikey) catalog (KIMI_CODING_SHARED.models), requested in discussion #3737. On resync: the PR's qwen-web PROVIDER_MODELS_CONFIG addition (issue #3931 bug #3) was already shipped by #4172 — dropped the duplicate, kept release's entry (which has the Array.isArray guard); kept #4183's KIMI_K27_MODELS spread + added the moonshotai-prefixed entry (union). Also reconciled the file-size baseline for openai-to-gemini.ts (#4180 merged without its +20 bump). Validated: 13/13 tests (kmca catalog + qwen-web parse + kimi registration) + file-size green.
@diegosouzapw diegosouzapw mentioned this pull request Jun 19, 2026
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…heaper-inference#4180)

Integrated into release/v3.8.29 — default includeThoughts for modern Gemini (2.5+) in the openai->gemini translator, so the model's reasoning is marked thought:true and routed to reasoning_content instead of leaking into visible content (cheaper-inference#4170). Thanks @dhaern! On review we regenerated the gemini golden snapshots (basic-chat/with-system) and committed the new modern-gemini-default-thinking snapshot. 61/61 translator + golden tests pass; eslint clean. Respects explicit client thinking config (only applies when none is set) and excludes gemini-1.x / non-thinking 2.0.
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…iscovery (cheaper-inference#3931 bug 3, cheaper-inference#3737) (cheaper-inference#4185)

Integrated into release/v3.8.29 — add moonshotai/kimi-k2.7-code to the kmca (kimi-coding-apikey) catalog (KIMI_CODING_SHARED.models), requested in discussion cheaper-inference#3737. On resync: the PR's qwen-web PROVIDER_MODELS_CONFIG addition (issue cheaper-inference#3931 bug cheaper-inference#3) was already shipped by cheaper-inference#4172 — dropped the duplicate, kept release's entry (which has the Array.isArray guard); kept cheaper-inference#4183's KIMI_K27_MODELS spread + added the moonshotai-prefixed entry (union). Also reconciled the file-size baseline for openai-to-gemini.ts (cheaper-inference#4180 merged without its +20 bump). Validated: 13/13 tests (kmca catalog + qwen-web parse + kimi registration) + file-size green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants