Fix Gemini thinking-only STOP handling - #83
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Problem
Gemini requests were treating omitted or disabled reasoning effort as a request for visible thought summaries. That made ordinary calls more likely to end with thinking output but no visible assistant text or tool call, especially on long ReAct turns.
The failure was also hard for callers to classify. A thinking-only STOP looked like a plain message string, and the agent layer dropped provider metadata as the event moved through strategy and session surfaces.
A typical failure looked like this: a host asks the agent to make a change, Gemini successfully calls a write tool, the tool result is dispatched, and the next model pass ends after internal thinking with finishReason=STOP but no visible output. Before this change, that final provider failure could make the whole turn look failed even though useful tool progress had already completed.
What Changed
Gemini now only requests visible thought summaries when the caller explicitly asks for reasoning with low, medium, or high effort. Omitted reasoning effort and reasoningEffort: off leave includeThoughts out of the request body.
When reasoning is requested, Gemini request construction uses deterministic model-family controls: Gemini 3.5 and Gemini 3 Flash receive thinkingLevel, Gemini 2.5 receives the configured thinkingBudget, and unknown Gemini model families only receive includeThoughts.
Gemini no-output streams now emit structured model errors with stable codes, retryability, and stream diagnostics. Thinking-only STOP, truncated no-output, malformed function call, and deterministic no-output failures are distinguishable without parsing prose. Retry-exhausted errors also carry final attempt metadata.
The Agent Behavior
The shared model error event now keeps message while allowing code, retryable, and details. Agent strategy, retrieval, relay, and session surfaces preserve that metadata instead of flattening it away.
ReAct stays strict by default. The new toolProgressErrorPolicy option is opt-in, and complete-with-warning only applies after successful tool progress in the current run and only for the known retryable gemini.thinking_only_stop error. In that narrow case, the strategy emits react_loop_model_warning and returns normally without synthesizing another turn completion.
Why This Is Safer
The default library behavior remains fatal on provider errors. Hosts that need conservative side-effect completion can opt into the progress-aware policy, while docs-site and ordinary ReAct callers keep the existing strict semantics.
The fix also avoids changing Gemini tool-call accumulation, schema sanitization, max output token behavior, and thought-signature replay, so the existing Gemini tool path keeps its current shape while the no-output failure path becomes explicit and actionable.