Skip to content

Commit 11e97c4

Browse files
committed
Add model_name, agent_name, and response_id to RequestUsage for better tracking
1 parent a7c539f commit 11e97c4

File tree

5 files changed

+63
-16
lines changed

5 files changed

+63
-16
lines changed

docs/usage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ The SDK automatically tracks usage for each API request in `request_usage_entrie
5555
result = await Runner.run(agent, "What's the weather in Tokyo?")
5656

5757
for request in enumerate(result.context_wrapper.usage.request_usage_entries):
58-
print(f"Request {i + 1}: {request.input_tokens} in, {request.output_tokens} out")
58+
print(f"Request {i + 1}: Model={request.model_name}, Agent={request.agent_name}, Input={request.input_tokens} tokens, Output={request.output_tokens} tokens")
5959
```
6060

6161
## Accessing usage with sessions

src/agents/models/interface.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ def include_data(self) -> bool:
3636
class Model(abc.ABC):
3737
"""The base interface for calling an LLM."""
3838

39+
# The model name. Subclasses can set this in __init__.
40+
model: str = ""
41+
3942
@abc.abstractmethod
4043
async def get_response(
4144
self,

src/agents/run.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,12 @@ async def _run_single_turn_streamed(
14021402
usage=usage,
14031403
response_id=event.response.id,
14041404
)
1405-
context_wrapper.usage.add(usage)
1405+
context_wrapper.usage.add(
1406+
usage,
1407+
model_name=model.model,
1408+
agent_name=agent.name,
1409+
response_id=event.response.id,
1410+
)
14061411

14071412
if isinstance(event, ResponseOutputItemDoneEvent):
14081413
output_item = event.item
@@ -1819,7 +1824,12 @@ async def _get_new_response(
18191824
prompt=prompt_config,
18201825
)
18211826

1822-
context_wrapper.usage.add(new_response.usage)
1827+
context_wrapper.usage.add(
1828+
new_response.usage,
1829+
model_name=model.model,
1830+
agent_name=agent.name,
1831+
response_id=new_response.response_id,
1832+
)
18231833

18241834
# If we have run hooks, or if the agent has hooks, we need to call them after the LLM call
18251835
await asyncio.gather(

src/agents/usage.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ class RequestUsage:
2323
output_tokens_details: OutputTokensDetails
2424
"""Details about the output tokens for this individual request."""
2525

26+
model_name: str
27+
"""The model name used for this request."""
28+
29+
agent_name: str
30+
"""The agent name that made this request."""
31+
32+
response_id: str | None = None
33+
"""The response ID for this request (i.e. ModelResponse.response_id)."""
34+
2635

2736
@dataclass
2837
class Usage:
@@ -70,13 +79,22 @@ def __post_init__(self) -> None:
7079
if self.output_tokens_details.reasoning_tokens is None:
7180
self.output_tokens_details = OutputTokensDetails(reasoning_tokens=0)
7281

73-
def add(self, other: "Usage") -> None:
82+
def add(
83+
self,
84+
other: "Usage",
85+
model_name: str,
86+
agent_name: str,
87+
response_id: str | None = None,
88+
) -> None:
7489
"""Add another Usage object to this one, aggregating all fields.
7590
7691
This method automatically preserves request_usage_entries.
7792
7893
Args:
7994
other: The Usage object to add to this one.
95+
model_name: The model name used for this request.
96+
agent_name: The agent name that made this request.
97+
response_id: The response ID for this request.
8098
"""
8199
self.requests += other.requests if other.requests else 0
82100
self.input_tokens += other.input_tokens if other.input_tokens else 0
@@ -101,6 +119,9 @@ def add(self, other: "Usage") -> None:
101119
total_tokens=other.total_tokens,
102120
input_tokens_details=other.input_tokens_details,
103121
output_tokens_details=other.output_tokens_details,
122+
model_name=model_name,
123+
agent_name=agent_name,
124+
response_id=response_id,
104125
)
105126
self.request_usage_entries.append(request_usage)
106127
elif other.request_usage_entries:

tests/test_usage.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_usage_add_aggregates_all_fields():
2121
total_tokens=15,
2222
)
2323

24-
u1.add(u2)
24+
u1.add(u2, model_name="gpt-5", agent_name="test-agent", response_id="resp-1")
2525

2626
assert u1.requests == 3
2727
assert u1.input_tokens == 17
@@ -42,7 +42,7 @@ def test_usage_add_aggregates_with_none_values():
4242
total_tokens=15,
4343
)
4444

45-
u1.add(u2)
45+
u1.add(u2, model_name="gpt-5", agent_name="test-agent", response_id="resp-1")
4646

4747
assert u1.requests == 2
4848
assert u1.input_tokens == 7
@@ -60,13 +60,19 @@ def test_request_usage_creation():
6060
total_tokens=300,
6161
input_tokens_details=InputTokensDetails(cached_tokens=10),
6262
output_tokens_details=OutputTokensDetails(reasoning_tokens=20),
63+
model_name="gpt-5",
64+
agent_name="test-agent",
65+
response_id="resp-123",
6366
)
6467

6568
assert request_usage.input_tokens == 100
6669
assert request_usage.output_tokens == 200
6770
assert request_usage.total_tokens == 300
6871
assert request_usage.input_tokens_details.cached_tokens == 10
6972
assert request_usage.output_tokens_details.reasoning_tokens == 20
73+
assert request_usage.model_name == "gpt-5"
74+
assert request_usage.agent_name == "test-agent"
75+
assert request_usage.response_id == "resp-123"
7076

7177

7278
def test_usage_add_preserves_single_request():
@@ -81,7 +87,7 @@ def test_usage_add_preserves_single_request():
8187
total_tokens=300,
8288
)
8389

84-
u1.add(u2)
90+
u1.add(u2, model_name="gpt-5", agent_name="test-agent", response_id="resp-1")
8591

8692
# Should preserve the request usage details
8793
assert len(u1.request_usage_entries) == 1
@@ -91,6 +97,9 @@ def test_usage_add_preserves_single_request():
9197
assert request_usage.total_tokens == 300
9298
assert request_usage.input_tokens_details.cached_tokens == 10
9399
assert request_usage.output_tokens_details.reasoning_tokens == 20
100+
assert request_usage.model_name == "gpt-5"
101+
assert request_usage.agent_name == "test-agent"
102+
assert request_usage.response_id == "resp-1"
94103

95104

96105
def test_usage_add_ignores_zero_token_requests():
@@ -105,7 +114,7 @@ def test_usage_add_ignores_zero_token_requests():
105114
total_tokens=0,
106115
)
107116

108-
u1.add(u2)
117+
u1.add(u2, model_name="gpt-5", agent_name="test-agent", response_id="resp-1")
109118

110119
# Should not create a request_usage_entry for zero tokens
111120
assert len(u1.request_usage_entries) == 0
@@ -123,7 +132,7 @@ def test_usage_add_ignores_multi_request_usage():
123132
total_tokens=300,
124133
)
125134

126-
u1.add(u2)
135+
u1.add(u2, model_name="gpt-5", agent_name="test-agent", response_id="resp-1")
127136

128137
# Should not create a request usage entry for multi-request usage
129138
assert len(u1.request_usage_entries) == 0
@@ -141,7 +150,7 @@ def test_usage_add_merges_existing_request_usage_entries():
141150
output_tokens_details=OutputTokensDetails(reasoning_tokens=20),
142151
total_tokens=300,
143152
)
144-
u1.add(u2)
153+
u1.add(u2, model_name="gpt-5", agent_name="agent-1", response_id="resp-1")
145154

146155
# Create second usage with request_usage_entries
147156
u3 = Usage(
@@ -153,7 +162,7 @@ def test_usage_add_merges_existing_request_usage_entries():
153162
total_tokens=125,
154163
)
155164

156-
u1.add(u3)
165+
u1.add(u3, model_name="gpt-5", agent_name="agent-2", response_id="resp-2")
157166

158167
# Should have both request_usage_entries
159168
assert len(u1.request_usage_entries) == 2
@@ -163,12 +172,16 @@ def test_usage_add_merges_existing_request_usage_entries():
163172
assert first.input_tokens == 100
164173
assert first.output_tokens == 200
165174
assert first.total_tokens == 300
175+
assert first.agent_name == "agent-1"
176+
assert first.response_id == "resp-1"
166177

167178
# Second request
168179
second = u1.request_usage_entries[1]
169180
assert second.input_tokens == 50
170181
assert second.output_tokens == 75
171182
assert second.total_tokens == 125
183+
assert second.agent_name == "agent-2"
184+
assert second.response_id == "resp-2"
172185

173186

174187
def test_usage_add_with_pre_existing_request_usage_entries():
@@ -184,7 +197,7 @@ def test_usage_add_with_pre_existing_request_usage_entries():
184197
output_tokens_details=OutputTokensDetails(reasoning_tokens=20),
185198
total_tokens=300,
186199
)
187-
u1.add(u2)
200+
u1.add(u2, model_name="gpt-5", agent_name="agent-1", response_id="resp-1")
188201

189202
# Create another usage with request_usage_entries
190203
u3 = Usage(
@@ -197,7 +210,7 @@ def test_usage_add_with_pre_existing_request_usage_entries():
197210
)
198211

199212
# Add u3 to u1
200-
u1.add(u3)
213+
u1.add(u3, model_name="gpt-5", agent_name="agent-2", response_id="resp-2")
201214

202215
# Should have both request_usage_entries
203216
assert len(u1.request_usage_entries) == 2
@@ -227,7 +240,7 @@ def test_anthropic_cost_calculation_scenario():
227240
output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
228241
total_tokens=150_000,
229242
)
230-
usage.add(req1)
243+
usage.add(req1, model_name="gpt-5", agent_name="test-agent", response_id="resp-1")
231244

232245
# Second request: 150K input tokens
233246
req2 = Usage(
@@ -238,7 +251,7 @@ def test_anthropic_cost_calculation_scenario():
238251
output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
239252
total_tokens=225_000,
240253
)
241-
usage.add(req2)
254+
usage.add(req2, model_name="gpt-5", agent_name="test-agent", response_id="resp-2")
242255

243256
# Third request: 80K input tokens
244257
req3 = Usage(
@@ -249,7 +262,7 @@ def test_anthropic_cost_calculation_scenario():
249262
output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
250263
total_tokens=120_000,
251264
)
252-
usage.add(req3)
265+
usage.add(req3, model_name="gpt-5", agent_name="test-agent", response_id="resp-3")
253266

254267
# Verify aggregated totals
255268
assert usage.requests == 3

0 commit comments

Comments
 (0)