-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Initial Checks
- I confirm that I'm using the latest version of Pydantic AI
- I confirm that I searched for my issue in https://github.com/pydantic/pydantic-ai/issues before opening this issue
Description
I'm running into an issue when running a Bedrock agent in AG-UI mode with tool calls. It appears that the messages are getting reordered on subsequent requests.
Scenerio:
-
I make a request which invokes multiple tool calls and it succeeds.
-
The messages sent to bedrock might look something like this:
[
{
"role": "user",
"content": [
{
"text": "whats the latest transaction on each of my account"
}
]
},
{
"role": "assistant",
"content": [
{
"text": "I'll help you get the latest transaction for each of your accounts. Let me first retrieve your account information and then get the transaction details for each account."
}
]
},
{
"role": "assistant",
"content": [
{
"toolUse": {
"toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
"name": "get-client-wealth-overview",
"input": {}
}
}
]
},
{
"role": "user",
"content": [
{
"toolResult": {
"toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
"content": [
{
"text": "{\"success\":true,\"tool\":\"get-client-wealth-overview\",\"data\":{omitted},\"parameters\":{}}"
}
],
"status": "success"
}
}
]
}
]-
I made another request (don't matter what it is)
-
The
messagessent to bedrock are in a different order and now look like this:
[
{
"role": "user",
"content": [
{
"text": "whats the latest transaction on each of my account"
}
]
},
{
"role": "assistant",
"content": [
{
"toolUse": {
"toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
"name": "get-client-wealth-overview",
"input": {}
}
}
]
},
{
"role": "assistant",
"content": [
{
"text": "I'll help you get the latest transaction for each of your accounts. Let me first retrieve your account information and then get the transaction details for each account."
}
]
},
{
"role": "user",
"content": [
{
"toolResult": {
"toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
"content": [
{
"text": "{\"success\":true,\"tool\":\"get-client-wealth-overview\",\"data\":{omitted},\"parameters\":{}}"
}
],
"status": "success"
}
}
]
}
]The order of the messages is changed so the assistant response is between the tool_call and tool_result, which is apparently not allowed with anthropic models...
This results in an error when models/bedrock.py sends the request to AWS:
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The number of toolResult blocks at messages.3.content exceeds the number of toolUse blocks of previous turn.
I'm not totally clear if this is a problem with the AG-UI client application mixing up the messages or if it something in the pydantic agent runtime. Maybe it's something that could be handled by the agent though since it feels like an implementation detail of the specific model that other providers probably don't have.
Stack Trace:
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 403, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/applications.py", line 113, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/errors.py", line 186, in __call__
raise exc
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/base.py", line 189, in __call__
raise app_exc
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/base.py", line 144, in coro
await self.app(scope, receive_or_disconnect, send_no_error)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
raise exc
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 716, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 736, in app
await route.handle(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 290, in handle
await self.app(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 78, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
raise exc
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 76, in app
await response(scope, receive, send)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/responses.py", line 270, in __call__
with collapse_excgroups():
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 155, in __exit__
self.gen.throw(typ, value, traceback)
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_utils.py", line 83, in collapse_excgroups
raise exc
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/responses.py", line 274, in wrap
await func()
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/responses.py", line 254, in stream_response
async for chunk in self.body_iterator:
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/ag_ui.py", line 325, in run
raise e
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/ag_ui.py", line 315, in run
async for event in self._agent_stream(run):
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/ag_ui.py", line 349, in _agent_stream
async with node.stream(run.ctx) as request_stream:
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/_agent_graph.py", line 306, in stream
async with self._stream(ctx) as streamed_response:
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/_agent_graph.py", line 333, in _stream
async with ctx.deps.model.request_stream(
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/instrumented.py", line 229, in request_stream
async with super().request_stream(
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/wrapper.py", line 39, in request_stream
async with self.wrapped.request_stream(messages, model_settings, model_request_parameters) as response_stream:
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/bedrock.py", line 269, in request_stream
response = await self._messages_create(messages, True, settings, model_request_parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/bedrock.py", line 360, in _messages_create
model_response = await anyio.to_thread.run_sync(functools.partial(self.client.converse_stream, **params))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/anyio/to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2470, in run_sync_in_worker_thread
return await future
^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 967, in run
result = context.run(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/botocore/client.py", line 595, in _api_call
return self._make_api_call(operation_name, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/botocore/context.py", line 123, in wrapper
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/botocore/client.py", line 1058, in _make_api_call
raise error_class(parsed_response, operation_name)
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The number of toolResult blocks at messages.3.content exceeds the number of toolUse blocks of previous turn.
Example Code
model = BedrockConverseModel(model_name="us.anthropic.claude-sonnet-4-20250514-v1:0")
# Configure MCP server with custom tool call processing and improved connection settings
eg_mcp = MCPServerStreamableHTTP("http://localhost:8080/mcp")
agent = Agent(
model=model,
instructions="You are a financial advisor. You are given a user's financial situation and you need to help them make decisions about their money.",
toolsets=[eg_mcp],
)
# Create the ag_ui app with middleware
app = agent.to_ag_ui()Python, Pydantic AI & LLM client version
Python: 3.11
Pydantic: 0.4.7
Model: bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0