Skip to content

fix(bedrock): JSON-parse accumulated_tool_input at contentBlockStop in streaming handlers#5739

Open
Ghraven wants to merge 2 commits intocrewAIInc:mainfrom
Ghraven:fix/bedrock-streaming-tool-input-json-parse
Open

fix(bedrock): JSON-parse accumulated_tool_input at contentBlockStop in streaming handlers#5739
Ghraven wants to merge 2 commits intocrewAIInc:mainfrom
Ghraven:fix/bedrock-streaming-tool-input-json-parse

Conversation

@Ghraven
Copy link
Copy Markdown

@Ghraven Ghraven commented May 7, 2026

Fixes #4972

Problem

When using the Bedrock Converse API in streaming mode, tool input arrives as incremental JSON string deltas that get concatenated into accumulated_tool_input. At contentBlockStop, the code reads current_tool_use.get("input", {}) — but current_tool_use["input"] was never updated during streaming; it still holds the initial empty value from contentBlockStart.

The accumulated JSON string lives only in accumulated_tool_input, never in current_tool_use["input"]. So every streaming tool call receives {} as its arguments, causing:

Field required [type=missing, input_value={}, input_type=dict]

Non-streaming Bedrock calls are unaffected because function_args is read directly from the completed response block.

Fix

At contentBlockStop, in both the sync (_handle_converse_stream) and async (_ahandle_converse_stream) handlers: if accumulated_tool_input is non-empty, JSON-parse it and write the result back to current_tool_use["input"] before reading function_args. Falls back to an empty dict on parse failure.

Ghraven added 2 commits May 7, 2026 10:26
…andler

Fixes crewAIInc#4972

When using Bedrock Converse API in streaming mode, tool input arrives as
JSON string deltas that get concatenated into `accumulated_tool_input`.
At content block stop, this accumulated string is stored directly in
`current_tool_use["input"]` and then passed to `_execute_single_native_tool_call`
as `function_args` — but it's still a raw JSON string, not a dict.

Tools that use Pydantic validation then receive a string instead of the
expected dict and fail immediately with `Field required [type=missing,
input_value={}, input_type=dict]` because the string can't be validated
as a model schema.

Fix: at content-block-stop in both sync and async streaming handlers,
check if `current_tool_use.get('input')` is a string and JSON-parse it
before passing to the executor. Falls back to empty dict on parse failure.
…n streaming

Fixes crewAIInc#4972

During streaming, Bedrock sends tool input as incremental string deltas
that get concatenated into `accumulated_tool_input`. When `contentBlockStop`
fires, the code reads `current_tool_use.get('input', {})` — but `input` was
never updated during streaming; it still holds the initial empty string from
`contentBlockStart`. The accumulated JSON string is only in
`accumulated_tool_input`, not in `current_tool_use['input']`.

This causes every streaming tool call to receive `{}` as arguments,
resulting in Pydantic validation failures on tools with required fields.

Fix: at `contentBlockStop`, if `accumulated_tool_input` is non-empty,
parse it as JSON and write it back to `current_tool_use['input']` before
reading `function_args`. Both sync and async streaming handlers are fixed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] _parse_native_tool_call drops Bedrock Converse API tool arguments — always passes empty dict

1 participant