fix(adk/bedrock): coerce nil tool-call args to empty object#2222
fix(adk/bedrock): coerce nil tool-call args to empty object#2222go-faustino wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes Bedrock Converse tool-call serialization by ensuring no-argument tool calls produce toolUse.input as an empty JSON object ({}) instead of null, which Bedrock rejects on follow-up turns.
Changes:
- Coerces
nilFunctionCall.Argsto an emptymap[string]any{}before creating the BedrockToolUseBlock. - Adds a unit test to cover both
niland empty-map tool arguments, assertingtoolUse.inputis a non-null empty object.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| go/adk/pkg/models/bedrock.go | Coerces nil tool-call args to an empty object before building toolUse.input. |
| go/adk/pkg/models/bedrock_test.go | Adds coverage for no-arg tool calls to ensure toolUse.input is {} not null. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // rejects a null input with "ValidationException: Malformed input request" | ||
| // ("The value at messages.N.content.M.toolUse.input is empty"). | ||
| func TestConvertGenaiContentsNoArgToolInput(t *testing.T) { | ||
| for _, args := range []map[string]any{nil, {}} { |
There was a problem hiding this comment.
Thanks for the review. This one is a false positive — the code compiles and the tests pass as-is.
In a Go composite literal, the element type may be elided, so {} inside []map[string]any{nil, {}} is shorthand for map[string]any{} (Go spec, Composite literals: "Within a composite literal of array, slice, or map type T, elements or map keys that are themselves composite literals may elide the respective literal type if it is identical to the element or key type of T"). nil is also a valid map[string]any element.
Verified on the branch:
$ go vet ./pkg/models/
$ go test ./pkg/models/ -run TestConvertGenaiContentsNoArgToolInput -count=1
ok github.com/kagent-dev/kagent/go/adk/pkg/models 0.855s
Keeping the elided form as it's idiomatic, but happy to spell out map[string]any{} if maintainers prefer explicitness.
A tool call with no arguments reaches the Bedrock converter with a nil
FunctionCall.Args map: genai's FunctionCall.Args is `json:"args,omitempty"`,
so an empty map is dropped when the event is persisted to the session store
and reloaded as nil. document.NewLazyDocument(nil) then serializes
toolUse.input to `null`, and Bedrock Converse rejects the follow-up
(tool-result) turn with:
ValidationException: Malformed input request
(The value at messages.N.content.M.toolUse.input is empty)
Coerce nil args to an empty map[string]any{} so no-argument tool calls
round-trip as `{}`. Adds a unit test covering both nil and empty Args.
Signed-off-by: Gonçalo Faustino <goncalo.santos@wellhub.com>
d4594c0 to
b6ec5e5
Compare
Summary
The Bedrock converter serializes a no-argument tool call as
toolUse.input: null, which Bedrock Converse rejects on the follow-up (tool-result) turn with:In
convertGenaiContentsToBedrockMessages(go/adk/pkg/models/bedrock.go), a tool call's input is built asdocument.NewLazyDocument(part.FunctionCall.Args). A no-argument call reaches this point with a nilArgsmap, because genai'sFunctionCall.Argsis taggedjson:"args,omitempty": an emptymap[string]any{}is dropped when the event is persisted to the session store and reloaded asnil.document.NewLazyDocument(nil)then serializes to JSONnull, and Bedrock requirestoolUse.inputto be a JSON object ({}).This is reproducible against
us.anthropic.claude-opus-4-8in any multi-turn conversation where the model calls a tool that takes no arguments (e.g.datetime_get_current_time) and the history is reloaded before the next turn.Fix
Coerce a nil
Argsmap to an emptymap[string]any{}before building theToolUseBlock, so no-argument tool calls round-trip as{}instead ofnull.Test
Adds
TestConvertGenaiContentsNoArgToolInputcovering bothniland empty ({})Args, asserting the producedtoolUse.inputis a non-null, empty JSON object. Without the fix, thenilcase serializes tonulland the test fails.