Repository navigation
A tool call's extra_content rides back to the endpoint within the turn - #1183
Merged
WaylandYang merged 2 commits intoOct 10, 2026
Merged
Conversation
Gemini 3 attaches extra_content.google.thought_signature to every function call and refuses the turn that returns the tool result without it: HTTP 400 "Function call is missing a thought_signature". Utopia kept id, name and arguments of a call and dropped the rest, so every question that used a tool failed on its second turn against Gemini while the connection test, a single tool-free message, passed. ToolCall keeps the endpoint's extra_content as an opaque object, in both transports; rig carries it in the slot it already has for provider metadata, additional_params; the wire writes it back as extra_content. A call without one adds no key. Calls from earlier, completed turns need no signature, so persisted history is left alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
WaylandYang
approved these changes
Oct 10, 2026
WaylandYang
left a comment
Contributor
There was a problem hiding this comment.
Thank you for this, and for the curl reproduction: it made the cause plain. The change reads correctly. extra_content is kept opaque, read in both the unary and the streaming parser, and written back only when the endpoint sent an object, so other endpoints see the same requests as before. Checked here: the utopia-llm tests and the adapter tests pass, and the branch merges cleanly with dev. Merging now. Thanks again.
This was referenced Oct 10, 2026
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.
What happened
Against Gemini's OpenAI-compatible endpoint (
gemini-3.8-flash), every chat question that used a tool ended withanswer_failedon its second turn, while the model connection test passed. Reproduced with curl: Gemini 3 attaches a signature to each function call,and the turn that returns the tool result without it is refused:
The same turn with the signature echoed answers normally. Function calls from earlier, completed turns are accepted without one (also verified), so this is about the in-turn replay only.
What changed
utopia_llm::ToolCallgainsextra_content: Option<Value>, the endpoint's object kept opaque, read in both the unary and the streaming parser, written back byto_message. Not an object (absent,null, a string) reads as none and adds no key.rig_model: a streamed call carries it inRawStreamingToolCall::additional_params, a unary one inToolCall::additional_params(rig preserves both into history), andpush_messagewrites it back asextra_content.Tests: both transports keep the Gemini shape and echo it; absent or malformed
extra_contentadds no key; the turn → rig → wire round trip carries it; a call without one stays as before.cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo test -p utopia-llm -p utopia-serverpass (49 + 603).Related: #1135 (embedding index), the other half of running rc8 against Gemini.