Surface Gemini server-side tool-call and tool-response parts - #1107
PratikDhanave (PratikDhanave) wants to merge 2 commits into
Conversation
buildResponsePart never inspected part.ToolCall or part.ToolResponse, so a server-side tool invocation echoed back by the model (its call and result) was silently dropped and the caller never saw it. Map part.ToolCall to an informational-only FunctionCallContent (so the tool loop observes but does not re-execute the server-side call) and part.ToolResponse to a FunctionResultContent, mirroring the Python client. Tool names come from ToolType; missing call ids are synthesized like the existing FunctionCall path.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Address call-ID correlation and ThoughtSignature preservation; strengthen the response-payload assertion.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Surfaces Gemini server-side tool calls and responses as framework function content.
Changes:
- Maps
ToolCallto informational-only function calls. - Maps
ToolResponseto function results. - Adds regression coverage for both mappings.
| File | Summary |
|---|---|
provider/geminiprovider/agent.go |
Converts server-side tool parts into framework content. |
provider/geminiprovider/agent_test.go |
Tests surfaced tool calls and responses. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| callID := part.ToolResponse.ID | ||
| if callID == "" { | ||
| callID = "tool-call-" + uuid.NewString() | ||
| } |
This comment has been minimized.
This comment has been minimized.
When a server-side tool response omits its own id, fall back to the preceding tool call's id (threaded through responsePartState) before synthesizing one, so the FunctionResultContent shares a CallID with its FunctionCallContent.
|
Scope: user-visible behavior, internal-only The Go change mirrors the Python One minor, non-blocking observation: when a No exported Go API surface changed (both touched symbols are unexported), so
|

Gemini can echo back server-side tool invocations as
toolCall/toolResponseresponse parts (e.g. when a function tool is combined with a native tool).buildResponsePartonly handledThought,Text,FunctionCall,InlineData,FileData,ExecutableCode, andCodeExecutionResult— it never readpart.ToolCallorpart.ToolResponse, so those parts were silently dropped and the caller never observed the model's server-side tool interactions.The Python client (
_parse_parts) maps both:tool_call→ a function call withinformational_only=True,tool_response→ a function result.Change
part.ToolCall→FunctionCallContent{InformationalOnly: true}— surfaced so callers can observe it, but marked informational so the tool-call loop does not try to execute a call the server already ran (toolautocallskipsInformationalOnlycalls).part.ToolResponse→FunctionResultContent.ToolType(fallback"tool_call"); missing call ids are synthesized like the existingFunctionCallpath.Test
TestServerSideToolCallAndResponsePartsSurfaced: a candidate carrying atoolCalland atoolResponsenow surfaces an informational-onlyFunctionCallContentand aFunctionResultContent. Fails before the change (both dropped), passes after.