fix(FlowiseChatGoogleGenerativeAI): handle 'thinking' content type round-trip#6449
Conversation
…und-trip
Gemini models with thinking enabled (gemini-2.5-* with thinkingConfig,
gemini-3-flash-preview with thinkingLevel, …) emit thought summary
parts in their responses. This file's response parsers convert them
into LangChain content blocks of shape:
{ type: 'thinking', thinking: '...', signature: '...' }
(see the two existing producer sites in this same file).
But when those blocks are echoed back to Gemini as conversation history
on the next iteration of an agent loop, they hit
`_convertLangChainContentToPart`, which has cases for text,
executableCode, codeExecutionResult, image_url, media, tool_use,
tool_call, mimetype patterns and functionCall — but NOT for thinking.
The function falls through to the throwing else and raises:
Error: Unknown content type thinking
surfacing in the Agent_AgentFlow node as
"Error in Agent node: Unknown content type thinking" on the SECOND
iteration of any agent loop running on a thinking model.
The asymmetry is the bug: the file produces `type: 'thinking'`
blocks but doesn't consume them.
Fix: add a branch that translates the LangChain `thinking` block back
into Google's native shape — a text Part with `thought: true` plus an
optional `thoughtSignature` (preserving the Gemini-3 thought-signature
that PR FlowiseAI#5715 added for functionCall continuity). This is the exact
shape Google's API expects per
https://ai.google.dev/gemini-api/docs/thinking and
https://ai.google.dev/gemini-api/docs/thought-signatures.
Tests (5/5 pass with the fix, 4/5 fail without — at the same line 307
throw users see in production):
✓ round-trips a thinking content block back into a Gemini text Part
with thought=true
✓ preserves the thoughtSignature for Gemini-3 multi-turn tool-call
continuity
✓ also accepts thoughtSignature on the LangChain block (alternate
key name)
✓ coerces non-string thinking payload to a string instead of throwing
✓ still throws "Unknown content type" for truly unrecognized types
There was a problem hiding this comment.
Code Review
This pull request adds support for Gemini thinking content blocks when echoing assistant messages back to the API as conversation history. It maps the thinking content type to Google's native shape with thought: true and preserves the thoughtSignature for Gemini-3 tool-call continuity, accompanied by comprehensive unit tests. The reviewer suggests validating the types of the text and signature fields and throwing an error for invalid types to promote fail-fast behavior instead of silently coercing them.
| const text = (content as any).thinking ?? (content as any).text ?? '' | ||
| const signature = (content as any).signature ?? (content as any).thoughtSignature | ||
| return { | ||
| text: typeof text === 'string' ? text : String(text ?? ''), | ||
| thought: true, | ||
| ...(signature ? { thoughtSignature: signature } : {}) | ||
| } as Part |
There was a problem hiding this comment.
When handling potentially invalid data from external API responses, we should prefer throwing an error for invalid input types rather than silently returning a default/empty value or silently coercing them. This promotes fail-fast behavior. Please validate that the text and signature fields are of the expected types and throw an error if they are invalid.
const text = (content as any).thinking ?? (content as any).text
if (text !== undefined && typeof text !== 'string') {
throw new Error('Invalid text type received from Google Generative AI API')
}
const signature = (content as any).signature ?? (content as any).thoughtSignature
if (signature !== undefined && typeof signature !== 'string') {
throw new Error('Invalid signature type received from Google Generative AI API')
}
return {
text: text ?? '',
thought: true,
...(signature ? { thoughtSignature: signature } : {})
} as PartReferences
- When handling potentially invalid data from external sources (like an API response), prefer throwing an error for invalid input types rather than silently returning a default or empty value. This promotes fail-fast behavior.
Per @gemini-code-assist review feedback on PR FlowiseAI#6449. Replaces the silent coercion of non-string `thinking` / `signature` fields with explicit validation that throws an informative error. Matches the existing fail-fast pattern used by the rest of `_convertLangChainContentToPart`. undefined/null thinking still degrades gracefully to empty text (producer sites always emit a string, but the converter shouldn't crash on a missing field). Tests updated to cover both directions: ✓ treats undefined/null thinking as empty text rather than throwing ✓ fails fast on non-string thinking payload (per code-review feedback) ✓ fails fast on non-string thinking signature 7/7 tests pass.
Per @gemini-code-assist review feedback on PR FlowiseAI#6449. Replaces the silent coercion of non-string `thinking` / `signature` fields with explicit validation that throws an informative error. Matches the existing fail-fast pattern used by the rest of `_convertLangChainContentToPart`. undefined/null thinking still degrades gracefully to empty text (producer sites always emit a string, but the converter shouldn't crash on a missing field). Tests updated to cover both directions: ✓ treats undefined/null thinking as empty text rather than throwing ✓ fails fast on non-string thinking payload (per code-review feedback) ✓ fails fast on non-string thinking signature 7/7 tests pass.
6a38754 to
21e7725
Compare
Gemini models with thinking enabled emit thought summary parts in their responses. This file's response parsers convert them into LangChain content blocks of shape:
{ type: 'thinking', thinking: '...', signature: '...' }
(see the two existing producer sites in this same file).
But when those blocks are echoed back to Gemini as conversation history on the next iteration of an agent loop, they hit
_convertLangChainContentToPart, which has cases for text, executableCode, codeExecutionResult, image_url, media, tool_use, tool_call, mimetype patterns and functionCall — but NOT for thinking. The function falls through to the throwing else and raises:Error: Unknown content type thinking
surfacing in the Agent_AgentFlow node as
"Error in Agent node: Unknown content type thinking" on the SECOND iteration of any agent loop running on a thinking model.
The asymmetry is the bug: the file produces
type: 'thinking'blocks but doesn't consume them.Fix: add a branch that translates the LangChain
thinkingblock back into Google's native shape — a text Part withthought: trueplus an optionalthoughtSignature(preserving the Gemini-3 thought-signature that PR #5715 added for functionCall continuity). This is the exact shape Google's API expects perhttps://ai.google.dev/gemini-api/docs/thinking and https://ai.google.dev/gemini-api/docs/thought-signatures.
Tests (5/5 pass with the fix, 4/5 fail without — at the same line 307 throw users see in production):
✓ round-trips a thinking content block back into a Gemini text Part
with thought=true
✓ preserves the thoughtSignature for Gemini-3 multi-turn tool-call
continuity
✓ also accepts thoughtSignature on the LangChain block (alternate
key name)
✓ coerces non-string thinking payload to a string instead of throwing
✓ still throws "Unknown content type" for truly unrecognized types