Skip to content

usage.tool_calls undercounts when running tools in parallel #3120

@certainly-param

Description

@certainly-param

Initial Checks

Description

Hey, I think there's a race condition in the usage tracking. When multiple tools run concurrently, usage.tool_calls doesn't get incremented correctly.

The increment happens in _tool_manager.py (line ~350):
python
usage.tool_calls += 1

But in _agent_graph.py, tools are launched in parallel with asyncio.create_task, and they all share the same usage object. So the increment isn't atomic and the count gets messed up.

I tested with 3 tools and sometimes get tool_calls=1, sometimes 2, rarely 3.

This affects usage limits and monitoring. Should I add a lock around the increment or is there a better approach?

Example Code

import asyncio
from pydantic_ai import Agent

agent = Agent('openai:gpt-4o')

@agent.tool
async def fetch_user(user_id: int) -> str:
    await asyncio.sleep(0.1)
    return f"User {user_id}"

@agent.tool
async def fetch_orders(user_id: int) -> str:
    await asyncio.sleep(0.1)
    return f"Orders for {user_id}"

@agent.tool
async def fetch_preferences(user_id: int) -> str:
    await asyncio.sleep(0.1)
    return f"Preferences for {user_id}"

async def main():
    result = await agent.run("Get user data, orders, and preferences for user 123")
    print(f"Expected tool_calls: 3")
    print(f"Actual tool_calls: {result.usage.tool_calls}")
    
    # Run multiple times to see the race
    for i in range(5):
        r = await agent.run("Use all three tools")
        print(f"Run {i+1}: {r.usage.tool_calls}")

asyncio.run(main())

Python, Pydantic AI & LLM client version

Python: 3.12.7
PydanticAI: 1.0.16 (or main branch commit d2a39f61)
OS: macOS (or whatever you're using)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions