Repository navigation
fix(ai): recover Gemini malformed tool call and incomplete Interactions streams - #60764
Conversation
There was a problem hiding this comment.
The change looks sound. I copied the new tests onto the base: 7 of the 65 fail there, matching the described cases, and all 65 pass with this change. typecheck:tsc in packages/ai passes. Reading the code, I found no regressions:
- Gemini: finish reasons other than
MALFORMED_RESPONSEwere already invalid provider output; only the retry classification ofMALFORMED_FUNCTION_CALLandUNEXPECTED_TOOL_CALLchanges. - Interactions route: the route name now follows the same pattern as Gemini.
- Tool calls: checking for
function_callsteps works because steps stay in parser state.
Public API is unchanged; this only affects how packages/ai classifies errors and finish reasons. No blocking concerns.
Related: #52378
0317d83 to
0d12081
Compare
There was a problem hiding this comment.
My earlier review left no open points. The new version is sound:
- Google's error codes now go through the shared
classifyProviderFailure. - A Google Interactions stream that ends early is now caught by the generic missing-terminal-event check and marked retryable.
The Gemini, Interactions and provider-error tests pass (107/107), as do the full packages/ai suite and typecheck:tsc. Run against the base, the same Gemini and Interactions tests fail in 7 of 65 cases. The new codes only match Google-specific error codes and come after the context-overflow and payload-too-large checks, so other providers shouldn't be affected. Public API is unchanged. No blocking concerns.
0d12081 to
b6797fb
Compare
There was a problem hiding this comment.
The new commit is sound; I found nothing that needs fixing. Moving the Google-specific codes out of the shared classifyProviderFailure and into the Interactions parser leaves error handling for every other provider as it is on dev. It also reuses GeminiGenerateContent.contentFiltered instead of keeping a second list of block codes, and any other code still goes to the shared classifier.
The Gemini, Interactions and provider-error tests pass (107/107), as do the full packages/ai suite and typecheck:tsc. The tests didn't change in this commit, and the same Gemini and Interactions tests fail in 7 of 65 cases on the base. Public API is unchanged.
Gemini sporadically ends valid tool-calling turns with
MALFORMED_FUNCTION_CALLorUNEXPECTED_TOOL_CALL, and Google Interactions streams can drop beforeinteraction.completedor fail with matching malformed tool-call codes. UnlikeMALFORMED_RESPONSE, those paths were not markedincomplete-stream, causing immediate 0-retry failures on recoverable model glitches and 10x retries on blocked Interactions responses.I moved both protocols onto
incomplete-streamdirectly inside their own stream parsers:packages/ai/src/protocols/gemini.ts: addMALFORMED_FUNCTION_CALLandUNEXPECTED_TOOL_CALLalongsideMALFORMED_RESPONSEasInvalidProviderOutputError({ classification: "incomplete-stream" }), and ignore non-terminalFINISH_REASON_UNSPECIFIED.packages/ai/src/protocols/google-interactions.ts: drop the customonHaltoverride sorequireTerminalEventmarks premature EOF asincomplete-stream, normalizecompletedwithfunction_callsteps to"tool-calls", and classify Interactions stop/block error codes locally viaGeminiGenerateContent.contentFilteredbefore falling back toclassifyProviderFailure.Verification
bun run typecheck:tscandbun testinpackages/ai(1995 passed)bun run check(35/35 workspace packages passed)Visual
Added by the opencode bot (AI-generated).
Related: #52378