-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
- Have you read the custom model provider docs, including the 'Common issues' section? Yes, Model provider docs
- Have you searched for related issues? Yes, no existing issue found.
The Usage.add() method in agents/usage.py fails with TypeError: unsupported operand type(s) for +: 'int' and 'NoneType' when aggregating usage from LLM providers that return None for cached_tokens or reasoning_tokens.
The issue is that lines 31-34 guard against None values:
self.requests += other.requests if other.requests else 0
self.input_tokens += other.input_tokens if other.input_tokens else 0
self.output_tokens += other.output_tokens if other.output_tokens else 0
self.total_tokens += other.total_tokens if other.total_tokens else 0But lines 35-43 do not have the same guards:
self.input_tokens_details = InputTokensDetails(
cached_tokens=self.input_tokens_details.cached_tokens
+ other.input_tokens_details.cached_tokens # ← No null check!
)
self.output_tokens_details = OutputTokensDetails(
reasoning_tokens=self.output_tokens_details.reasoning_tokens
+ other.output_tokens_details.reasoning_tokens # ← No null check!
)Additionally, when output_tokens_details or input_tokens_details is explicitly passed as None during Usage instantiation (from LLM response parsing), Pydantic validation fails because the field types don't accept None.
Debug information
- Agents SDK version:
v0.6.2(also affects latest) - Python version: 3.13
- LLM Provider: Google Gemini via LiteLLM Proxy server
Repro steps
from agents import Agent, Runner
from agents import OpenAIResponsesModel, AsyncOpenAI
client = AsyncOpenAI(
base_url="http://localhost:4000",
api_key=os.environ.get("GEMINI_API_KEY"),
)
model = OpenAIResponsesModel(
model="gemini/gemini-2.5-flash",
openai_client=client,
)
# Use any non-OpenAI model provider that may return None for token details
agent = Agent(
name="test",
model=model,
instructions="You are a helpful assistant."
)
# Run a conversation that triggers usage aggregation
result = await Runner.run(agent, "Hello!")When the LLM response has input_tokens_details.cached_tokens = None or output_tokens_details.reasoning_tokens = None, the Usage.add() method crashes.
Expected behavior
The SDK should handle None values gracefully by defaulting to 0, consistent with how requests, input_tokens, output_tokens, and total_tokens are handled.
Proposed fix
def add(self, other: "Usage") -> None:
self.requests += other.requests if other.requests else 0
self.input_tokens += other.input_tokens if other.input_tokens else 0
self.output_tokens += other.output_tokens if other.output_tokens else 0
self.total_tokens += other.total_tokens if other.total_tokens else 0
# Add null guards for nested token details
other_cached = (other.input_tokens_details.cached_tokens
if other.input_tokens_details and other.input_tokens_details.cached_tokens
else 0)
other_reasoning = (other.output_tokens_details.reasoning_tokens
if other.output_tokens_details and other.output_tokens_details.reasoning_tokens
else 0)
self.input_tokens_details = InputTokensDetails(
cached_tokens=(self.input_tokens_details.cached_tokens
if self.input_tokens_details and self.input_tokens_details.cached_tokens
else 0) + other_cached
)
self.output_tokens_details = OutputTokensDetails(
reasoning_tokens=(self.output_tokens_details.reasoning_tokens
if self.output_tokens_details and self.output_tokens_details.reasoning_tokens
else 0) + other_reasoning
)I'm happy to submit a PR for this fix.