fix(go-adk): use genai.Schema for sub-agent tool declaration#1713
fix(go-adk): use genai.Schema for sub-agent tool declaration#1713mesutoezdil wants to merge 1 commit intokagent-dev:mainfrom
Conversation
Fixes kagent-dev#1645. functiontool.New populates Declaration().ParametersJsonSchema (JSON Schema wire format). The Anthropic and AWS Bedrock model adapters inside the Google ADK only read Declaration().Parameters (genai.Schema format) when building the tool list for the API call; they silently ignore ParametersJsonSchema. The LLM therefore receives no parameter schema for remote-agent tools and has to guess a field name. Claude Bedrock guessed "$task" on the first call (rejected as an unexpected property) and then fell back to {} (rejected as missing the required "request" property), so every sub-agent invocation failed. Replace functiontool.New with a hand-written remoteA2ATool struct that: - sets Declaration().Parameters with an explicit genai.Schema (same approach used by agenttool in the ADK itself) - implements Run() and ProcessRequest() to satisfy the ADK-internal FunctionTool and RequestProcessor interfaces without importing the internal toolutils package OpenAI and Gemini were not affected because their adapters handle both schema representations; the regression was specific to Anthropic-family models (direct API, Bedrock, Vertex AI). Signed-off-by: mesutoezdil <mesudozdil@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses sub-agent tool invocation failures for certain backends by ensuring the remote A2A tool declaration carries a parameter schema in the format expected by ADK tooling.
Changes:
- Replaces
functiontool.Newusage with a customremoteA2AToolimplementation. - Declares tool parameters via an explicit
genai.Schema(FunctionDeclaration.Parameters) and ports request-packing logic intoProcessRequest.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (t *remoteA2ATool) Declaration() *genai.FunctionDeclaration { | ||
| return &genai.FunctionDeclaration{ | ||
| Name: t.state.name, | ||
| Description: t.state.description, | ||
| Parameters: &genai.Schema{ | ||
| Type: genai.TypeObject, | ||
| Properties: map[string]*genai.Schema{ | ||
| "request": { | ||
| Type: genai.TypeString, |
There was a problem hiding this comment.
remoteA2ATool.Declaration() sets only FunctionDeclaration.Parameters (genai.Schema). In this repo, the Anthropic model adapter currently builds tool schemas only from FunctionDeclaration.ParametersJsonSchema (see go/adk/pkg/models/anthropic_adk.go:238-253) and ignores Parameters. That means sub-agent calls will still be missing the required "request" field when using the Anthropic backend. To keep compatibility across adapters, either also populate ParametersJsonSchema here (e.g., as a small map[string]any JSON schema) or update the Anthropic adapter to handle Parameters as a fallback (like Bedrock/OpenAI do).
| // genai.Schema value rather than ParametersJsonSchema. Anthropic and AWS | ||
| // Bedrock model adapters inside the ADK read the Parameters field; they ignore | ||
| // ParametersJsonSchema, so using functiontool.New causes those adapters to omit | ||
| // the "request" parameter from the tool schema sent to the LLM. When the LLM | ||
| // receives no schema it guesses field names (e.g. "$task") and the call fails | ||
| // with "missing properties: [request]". |
There was a problem hiding this comment.
The comment claims Anthropic and Bedrock adapters "read the Parameters field" and "ignore ParametersJsonSchema", but in this repo Bedrock conversion checks ParametersJsonSchema first and Anthropic conversion currently only reads ParametersJsonSchema (go/adk/pkg/models/bedrock.go:512-522, go/adk/pkg/models/anthropic_adk.go:238-253). Please adjust the comment to match the actual compatibility matrix (or explicitly clarify it refers to upstream google.golang.org/adk adapters, not kagent's models).
| // genai.Schema value rather than ParametersJsonSchema. Anthropic and AWS | |
| // Bedrock model adapters inside the ADK read the Parameters field; they ignore | |
| // ParametersJsonSchema, so using functiontool.New causes those adapters to omit | |
| // the "request" parameter from the tool schema sent to the LLM. When the LLM | |
| // receives no schema it guesses field names (e.g. "$task") and the call fails | |
| // with "missing properties: [request]". | |
| // genai.Schema value rather than relying only on ParametersJsonSchema. ADK | |
| // adapters and conversion paths do not all consume tool schemas the same way, | |
| // and ensuring Parameters is populated avoids cases where the "request" | |
| // parameter is omitted from the tool schema sent to the LLM. When the LLM | |
| // receives no usable schema it may guess field names (e.g. "$task"), and the | |
| // call then fails with "missing properties: [request]". |
supreme-gg-gg
left a comment
There was a problem hiding this comment.
@mesutoezdil this should be fixed already in #1650 the proper way is to use functiontool.New according to ADK docs and to avoid hand-rolling tool packing functions (and a lot of other boilerplate)
|
Thanks for pointing that out, @supreme-gg-gg! Closing this in favour of #1650. Will pull the latest and look at how you approached it properly. |
Fixes #1645.
Ran into this while testing a multi-agent setup with Bedrock. The orchestrator kept failing every time it tried to delegate to a sub-agent. Logs showed it first sent
$taskas the field name, got rejected, then tried empty args, got rejected again. Never got through.Dug into it and found that
functiontool.Newputs the schema inParametersJsonSchemaon the function declaration. Turns out the Anthropic and Bedrock adapters in the ADK only read theParametersfield and just skipParametersJsonSchema. So from the LLM side the tool shows up with no schema at all, and it has to guess what to send.OpenAI and Gemini both handle both fields so they work fine, which is why it did not reproduce there.
The fix replaces
functiontool.Newwith a smallremoteA2AToolstruct that setsParametersdirectly usinggenai.Schema, the same wayagenttooldoes it inside the ADK. It also wires upRunandProcessRequestso the ADK runtime is happy without needing to reach into internal packages.Builds clean, all existing tests pass.