-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Closed
Labels
bugSomething isn't workingSomething isn't working
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
Description
I'm encountering an issue when using pydantic-ai>=0.4.7 with OpenAI's gpt-4o-mini. When the agent attempts to invoke multiple tools in a single user prompt (e.g., requesting prices of multiple fruits), the run fails with a 400 error from the OpenAI API:
Error code: 400 - {'error': {'message': "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: ...", 'type': 'invalid_request_error', 'param': 'messages.[4].role', 'code': None}}
This seems to indicate that the agent is not sending tool response messages for all the tool_call_ids generated by the assistant message.
Example Code
import asyncio
import logging
from dataclasses import dataclass
from string import Template
from typing import Union
from dotenv import load_dotenv
from openai import AsyncOpenAI
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
from pydantic_ai.messages import ToolReturn
from pydantic_ai.models.openai import OpenAIModel
from pydantic_ai.providers.openai import OpenAIProvider
load_dotenv()
logger = logging.getLogger(__name__)
class Failure(BaseModel):
"""Represents structure for action failure"""
reason: str = Field(description="The Reason why the task failed")
@dataclass(frozen=True, slots=True)
class AgentContext:
shop_name: str
model_ = OpenAIModel(
model_name="gpt-4o-mini",
provider=OpenAIProvider(openai_client=AsyncOpenAI()),
)
agent = Agent[AgentContext, str | Failure](
name="Fruit Shop Agent",
model=model_,
deps_type=AgentContext,
output_type=Union[str, Failure], # type: ignore # noqa: PGH003
retries=3,
)
SYSTEM_MESSAGE = (
"You are a helpful Food Seller agent.\n"
"You help users with information about food items, their colors, and other details.\n"
"You are working at the shop: ${SHOP_NAME}"
)
@agent.system_prompt
async def get_system_prompt(ctx: RunContext[AgentContext]) -> str:
# The system prompt uses the shop_name from AgentContext
return Template(SYSTEM_MESSAGE).substitute(SHOP_NAME=ctx.deps.shop_name)
@agent.tool
async def get_fruit_price(ctx: RunContext[AgentContext], food_name: str) -> ToolReturn:
"""Get the price of a fruit per kilogram.
Args:
food_name: The name of the fruit.
Returns:
returns price of the fruit or an error message if not found.
"""
food_prices = {
"apple": 1.2,
"banana": 0.5,
"grape": 2.0,
"pineapple": 3.0,
"orange": 1.0,
"kiwi": 1.5,
"mango": 2.5,
"strawberry": 2.0,
}
price = food_prices.get(food_name.lower(), None)
if price is None:
return ToolReturn(
return_value=None,
content=f"Price of {food_name} is not known.",
metadata={"success": False, "error": "UNKNOWN_FRUIT"},
)
return ToolReturn(
return_value=price,
content=f"The price of {food_name} is {price} per kilogram.",
metadata={"success": True},
)
task_str = "what is the price of an apple, banana, grape, pineapple?"
async def run_agent(shop_name: str, task: str) -> None:
context = AgentContext(shop_name=shop_name)
results = await agent.run(user_prompt=task, deps=context)
print(results)
shop_name = "Rahul's Fresh Foods"
asyncio.run(run_agent(shop_name, task_str))Here is the output
D:\python\open-coder\agentic_coder>uv run test_openai.py
Traceback (most recent call last):
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\models\openai.py", line 332, in _completions_create
return await self.client.chat.completions.create(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<25 lines>...
)
^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\openai\resources\chat\completions\completions.py", line 2454, in create
return await self._post(
^^^^^^^^^^^^^^^^^
...<45 lines>...
)
^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\openai\_base_client.py", line 1791, in post
return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\openai\_base_client.py", line 1591, in request
raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: call_Eiv5OsuW5aFEDoh9U9GwCbxs, call_czqKque4kEH4li9triArNTBv, call_q3z8CqKk3Yrj3V6ydPW7MWdc", 'type': 'invalid_request_error', 'param': 'messages.[4].role', 'code': None}}
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:\python\open-coder\agentic_coder\test_openai.py", line 102, in <module>
asyncio.run(run_agent(shop_name, task_str))
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rahul.das\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run
return runner.run(main)
~~~~~~~~~~^^^^^^
File "C:\Users\rahul.das\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "C:\Users\rahul.das\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 719, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "D:\python\open-coder\agentic_coder\test_openai.py", line 97, in run_agent
results = await agent.run(user_prompt=task, deps=context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\agent.py", line 562, in run
async for _ in agent_run:
pass
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\agent.py", line 2175, in __anext__
next_node = await self._graph_run.__anext__()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_graph\graph.py", line 809, in __anext__
return await self.next(self._next_node)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_graph\graph.py", line 782, in next
self._next_node = await node.run(ctx)
^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\_agent_graph.py", line 299, in run
return await self._make_request(ctx)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\_agent_graph.py", line 359, in _make_request
model_response = await ctx.deps.model.request(message_history, model_settings, model_request_parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\models\openai.py", line 244, in request
response = await self._completions_create(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
messages, False, cast(OpenAIModelSettings, model_settings or {}), model_request_parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "D:\python\open-coder\agentic_coder\.venv\Lib\site-packages\pydantic_ai\models\openai.py", line 361, in _completions_create
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
pydantic_ai.exceptions.ModelHTTPError: status_code: 400, model_name: gpt-4o-mini, body: {'message': "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: call_Eiv5OsuW5aFEDoh9U9GwCbxs, call_czqKque4kEH4li9triArNTBv, call_q3z8CqKk3Yrj3V6ydPW7MWdc", 'type': 'invalid_request_error', 'param': 'messages.[4].role', 'code': None}
Python, Pydantic AI & LLM client version
requires-python = ">=3.13"
dependencies = [
"pydantic>=2.11.7",
"pydantic-ai>=0.4.7",
"pydantic-settings>=2.10.1",
]
Wh1isper
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working