Skip to content

Commit 9c82b2e

Browse files
Deprecate Usage in favour of RequestUsage and RunUsage (#2378)
Co-authored-by: Alex Hall <[email protected]>
1 parent 851df07 commit 9c82b2e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1683
-1905
lines changed

docs/agents.md

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,7 @@ async def main():
302302
CallToolsNode(
303303
model_response=ModelResponse(
304304
parts=[TextPart(content='The capital of France is Paris.')],
305-
usage=Usage(
306-
requests=1, request_tokens=56, response_tokens=7, total_tokens=63
307-
),
305+
usage=RequestUsage(input_tokens=56, output_tokens=7),
308306
model_name='gpt-4o',
309307
timestamp=datetime.datetime(...),
310308
)
@@ -367,12 +365,7 @@ async def main():
367365
CallToolsNode(
368366
model_response=ModelResponse(
369367
parts=[TextPart(content='The capital of France is Paris.')],
370-
usage=Usage(
371-
requests=1,
372-
request_tokens=56,
373-
response_tokens=7,
374-
total_tokens=63,
375-
),
368+
usage=RequestUsage(input_tokens=56, output_tokens=7),
376369
model_name='gpt-4o',
377370
timestamp=datetime.datetime(...),
378371
)
@@ -391,7 +384,7 @@ _(This example is complete, it can be run "as is" — you'll need to add `asynci
391384

392385
#### Accessing usage and final output
393386

394-
You can retrieve usage statistics (tokens, requests, etc.) at any time from the [`AgentRun`][pydantic_ai.agent.AgentRun] object via `agent_run.usage()`. This method returns a [`Usage`][pydantic_ai.usage.Usage] object containing the usage data.
387+
You can retrieve usage statistics (tokens, requests, etc.) at any time from the [`AgentRun`][pydantic_ai.agent.AgentRun] object via `agent_run.usage()`. This method returns a [`RunUsage`][pydantic_ai.usage.RunUsage] object containing the usage data.
395388

396389
Once the run finishes, `agent_run.result` becomes a [`AgentRunResult`][pydantic_ai.agent.AgentRunResult] object containing the final output (and related metadata).
397390

@@ -570,7 +563,7 @@ result_sync = agent.run_sync(
570563
print(result_sync.output)
571564
#> Rome
572565
print(result_sync.usage())
573-
#> Usage(requests=1, request_tokens=62, response_tokens=1, total_tokens=63)
566+
#> RunUsage(input_tokens=62, output_tokens=1, requests=1)
574567

575568
try:
576569
result_sync = agent.run_sync(
@@ -579,7 +572,7 @@ try:
579572
)
580573
except UsageLimitExceeded as e:
581574
print(e)
582-
#> Exceeded the response_tokens_limit of 10 (response_tokens=32)
575+
#> Exceeded the output_tokens_limit of 10 (output_tokens=32)
583576
```
584577

585578
Restricting the number of requests can be useful in preventing infinite loops or excessive tool calling:
@@ -1018,9 +1011,7 @@ with capture_run_messages() as messages: # (2)!
10181011
tool_call_id='pyd_ai_tool_call_id',
10191012
)
10201013
],
1021-
usage=Usage(
1022-
requests=1, request_tokens=62, response_tokens=4, total_tokens=66
1023-
),
1014+
usage=RequestUsage(input_tokens=62, output_tokens=4),
10241015
model_name='gpt-4o',
10251016
timestamp=datetime.datetime(...),
10261017
),
@@ -1042,9 +1033,7 @@ with capture_run_messages() as messages: # (2)!
10421033
tool_call_id='pyd_ai_tool_call_id',
10431034
)
10441035
],
1045-
usage=Usage(
1046-
requests=1, request_tokens=72, response_tokens=8, total_tokens=80
1047-
),
1036+
usage=RequestUsage(input_tokens=72, output_tokens=8),
10481037
model_name='gpt-4o',
10491038
timestamp=datetime.datetime(...),
10501039
),

docs/direct.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ model_response = model_request_sync(
2828
print(model_response.parts[0].content)
2929
#> The capital of France is Paris.
3030
print(model_response.usage)
31-
#> Usage(requests=1, request_tokens=56, response_tokens=7, total_tokens=63)
31+
#> RequestUsage(input_tokens=56, output_tokens=7)
3232
```
3333

3434
_(This example is complete, it can be run "as is")_
@@ -83,7 +83,7 @@ async def main():
8383
tool_call_id='pyd_ai_2e0e396768a14fe482df90a29a78dc7b',
8484
)
8585
],
86-
usage=Usage(requests=1, request_tokens=55, response_tokens=7, total_tokens=62),
86+
usage=RequestUsage(input_tokens=55, output_tokens=7),
8787
model_name='gpt-4.1-nano',
8888
timestamp=datetime.datetime(...),
8989
)

docs/message-history.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ print(result.all_messages())
5858
content='Did you hear about the toothpaste scandal? They called it Colgate.'
5959
)
6060
],
61-
usage=Usage(requests=1, request_tokens=60, response_tokens=12, total_tokens=72),
61+
usage=RequestUsage(input_tokens=60, output_tokens=12),
6262
model_name='gpt-4o',
6363
timestamp=datetime.datetime(...),
6464
),
@@ -126,7 +126,7 @@ async def main():
126126
content='Did you hear about the toothpaste scandal? They called it Colgate.'
127127
)
128128
],
129-
usage=Usage(request_tokens=50, response_tokens=12, total_tokens=62),
129+
usage=RequestUsage(input_tokens=50, output_tokens=12),
130130
model_name='gpt-4o',
131131
timestamp=datetime.datetime(...),
132132
),
@@ -180,7 +180,7 @@ print(result2.all_messages())
180180
content='Did you hear about the toothpaste scandal? They called it Colgate.'
181181
)
182182
],
183-
usage=Usage(requests=1, request_tokens=60, response_tokens=12, total_tokens=72),
183+
usage=RequestUsage(input_tokens=60, output_tokens=12),
184184
model_name='gpt-4o',
185185
timestamp=datetime.datetime(...),
186186
),
@@ -198,7 +198,7 @@ print(result2.all_messages())
198198
content='This is an excellent joke invented by Samuel Colvin, it needs no explanation.'
199199
)
200200
],
201-
usage=Usage(requests=1, request_tokens=61, response_tokens=26, total_tokens=87),
201+
usage=RequestUsage(input_tokens=61, output_tokens=26),
202202
model_name='gpt-4o',
203203
timestamp=datetime.datetime(...),
204204
),
@@ -299,7 +299,7 @@ print(result2.all_messages())
299299
content='Did you hear about the toothpaste scandal? They called it Colgate.'
300300
)
301301
],
302-
usage=Usage(requests=1, request_tokens=60, response_tokens=12, total_tokens=72),
302+
usage=RequestUsage(input_tokens=60, output_tokens=12),
303303
model_name='gpt-4o',
304304
timestamp=datetime.datetime(...),
305305
),
@@ -317,7 +317,7 @@ print(result2.all_messages())
317317
content='This is an excellent joke invented by Samuel Colvin, it needs no explanation.'
318318
)
319319
],
320-
usage=Usage(requests=1, request_tokens=61, response_tokens=26, total_tokens=87),
320+
usage=RequestUsage(input_tokens=61, output_tokens=26),
321321
model_name='gemini-1.5-pro',
322322
timestamp=datetime.datetime(...),
323323
),

docs/models/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ print(response.all_messages())
117117
model_name='claude-3-5-sonnet-latest',
118118
timestamp=datetime.datetime(...),
119119
kind='response',
120-
vendor_id=None,
120+
provider_request_id=None,
121121
),
122122
]
123123
"""

docs/models/openai.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ result = agent.run_sync('Where were the olympics held in 2012?')
275275
print(result.output)
276276
#> city='London' country='United Kingdom'
277277
print(result.usage())
278-
#> Usage(requests=1, request_tokens=57, response_tokens=8, total_tokens=65)
278+
#> RunUsage(input_tokens=57, output_tokens=8, requests=1)
279279
```
280280

281281
#### Example using a remote server
@@ -304,7 +304,7 @@ result = agent.run_sync('Where were the olympics held in 2012?')
304304
print(result.output)
305305
#> city='London' country='United Kingdom'
306306
print(result.usage())
307-
#> Usage(requests=1, request_tokens=57, response_tokens=8, total_tokens=65)
307+
#> RunUsage(input_tokens=57, output_tokens=8, requests=1)
308308
```
309309

310310
1. The name of the model running on the remote server

docs/multi-agent-applications.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ result = joke_selection_agent.run_sync(
5353
print(result.output)
5454
#> Did you hear about the toothpaste scandal? They called it Colgate.
5555
print(result.usage())
56-
#> Usage(requests=3, request_tokens=204, response_tokens=24, total_tokens=228)
56+
#> RunUsage(input_tokens=204, output_tokens=24, requests=3)
5757
```
5858

5959
1. The "parent" or controlling agent.
@@ -144,7 +144,7 @@ async def main():
144144
print(result.output)
145145
#> Did you hear about the toothpaste scandal? They called it Colgate.
146146
print(result.usage()) # (6)!
147-
#> Usage(requests=4, request_tokens=309, response_tokens=32, total_tokens=341)
147+
#> RunUsage(input_tokens=309, output_tokens=32, requests=4)
148148
```
149149

150150
1. Define a dataclass to hold the client and API key dependencies.
@@ -188,7 +188,7 @@ from rich.prompt import Prompt
188188

189189
from pydantic_ai import Agent, RunContext
190190
from pydantic_ai.messages import ModelMessage
191-
from pydantic_ai.usage import Usage, UsageLimits
191+
from pydantic_ai.usage import RunUsage, UsageLimits
192192

193193

194194
class FlightDetails(BaseModel):
@@ -221,7 +221,7 @@ async def flight_search(
221221
usage_limits = UsageLimits(request_limit=15) # (3)!
222222

223223

224-
async def find_flight(usage: Usage) -> Union[FlightDetails, None]: # (4)!
224+
async def find_flight(usage: RunUsage) -> Union[FlightDetails, None]: # (4)!
225225
message_history: Union[list[ModelMessage], None] = None
226226
for _ in range(3):
227227
prompt = Prompt.ask(
@@ -259,7 +259,7 @@ seat_preference_agent = Agent[None, Union[SeatPreference, Failed]]( # (5)!
259259
)
260260

261261

262-
async def find_seat(usage: Usage) -> SeatPreference: # (6)!
262+
async def find_seat(usage: RunUsage) -> SeatPreference: # (6)!
263263
message_history: Union[list[ModelMessage], None] = None
264264
while True:
265265
answer = Prompt.ask('What seat would you like?')
@@ -278,7 +278,7 @@ async def find_seat(usage: Usage) -> SeatPreference: # (6)!
278278

279279

280280
async def main(): # (7)!
281-
usage: Usage = Usage()
281+
usage: RunUsage = RunUsage()
282282

283283
opt_flight_details = await find_flight(usage)
284284
if opt_flight_details is not None:

docs/output.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"Output" refers to the final value returned from [running an agent](agents.md#running-agents). This can be either plain text, [structured data](#structured-output), or the result of a [function](#output-functions) called with arguments provided by the model.
22

3-
The output is wrapped in [`AgentRunResult`][pydantic_ai.agent.AgentRunResult] or [`StreamedRunResult`][pydantic_ai.result.StreamedRunResult] so that you can access other data, like [usage][pydantic_ai.usage.Usage] of the run and [message history](message-history.md#accessing-messages-from-results).
3+
The output is wrapped in [`AgentRunResult`][pydantic_ai.agent.AgentRunResult] or [`StreamedRunResult`][pydantic_ai.result.StreamedRunResult] so that you can access other data, like [usage][pydantic_ai.usage.RunUsage] of the run and [message history](message-history.md#accessing-messages-from-results).
44

55
Both `AgentRunResult` and `StreamedRunResult` are generic in the data they wrap, so typing information about the data returned by the agent is preserved.
66

@@ -24,7 +24,7 @@ result = agent.run_sync('Where were the olympics held in 2012?')
2424
print(result.output)
2525
#> city='London' country='United Kingdom'
2626
print(result.usage())
27-
#> Usage(requests=1, request_tokens=57, response_tokens=8, total_tokens=65)
27+
#> RunUsage(input_tokens=57, output_tokens=8, requests=1)
2828
```
2929

3030
_(This example is complete, it can be run "as is")_

docs/testing.md

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ from pydantic_ai.messages import (
9797
UserPromptPart,
9898
ModelRequest,
9999
)
100-
from pydantic_ai.usage import Usage
100+
from pydantic_ai.usage import RequestUsage
101101

102102
from fake_database import DatabaseConn
103103
from weather_app import run_weather_forecast, weather_agent
@@ -141,12 +141,9 @@ async def test_forecast():
141141
tool_call_id=IsStr(),
142142
)
143143
],
144-
usage=Usage(
145-
requests=1,
146-
request_tokens=71,
147-
response_tokens=7,
148-
total_tokens=78,
149-
details=None,
144+
usage=RequestUsage(
145+
input_tokens=71,
146+
output_tokens=7,
150147
),
151148
model_name='test',
152149
timestamp=IsNow(tz=timezone.utc),
@@ -167,12 +164,9 @@ async def test_forecast():
167164
content='{"weather_forecast":"Sunny with a chance of rain"}',
168165
)
169166
],
170-
usage=Usage(
171-
requests=1,
172-
request_tokens=77,
173-
response_tokens=16,
174-
total_tokens=93,
175-
details=None,
167+
usage=RequestUsage(
168+
input_tokens=77,
169+
output_tokens=16,
176170
),
177171
model_name='test',
178172
timestamp=IsNow(tz=timezone.utc),

docs/tools.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ print(dice_result.all_messages())
9595
tool_name='roll_dice', args={}, tool_call_id='pyd_ai_tool_call_id'
9696
)
9797
],
98-
usage=Usage(requests=1, request_tokens=90, response_tokens=2, total_tokens=92),
98+
usage=RequestUsage(input_tokens=90, output_tokens=2),
9999
model_name='gemini-1.5-flash',
100100
timestamp=datetime.datetime(...),
101101
),
@@ -115,7 +115,7 @@ print(dice_result.all_messages())
115115
tool_name='get_player_name', args={}, tool_call_id='pyd_ai_tool_call_id'
116116
)
117117
],
118-
usage=Usage(requests=1, request_tokens=91, response_tokens=4, total_tokens=95),
118+
usage=RequestUsage(input_tokens=91, output_tokens=4),
119119
model_name='gemini-1.5-flash',
120120
timestamp=datetime.datetime(...),
121121
),
@@ -135,9 +135,7 @@ print(dice_result.all_messages())
135135
content="Congratulations Anne, you guessed correctly! You're a winner!"
136136
)
137137
],
138-
usage=Usage(
139-
requests=1, request_tokens=92, response_tokens=12, total_tokens=104
140-
),
138+
usage=RequestUsage(input_tokens=92, output_tokens=12),
141139
model_name='gemini-1.5-flash',
142140
timestamp=datetime.datetime(...),
143141
),

examples/pydantic_ai_examples/flight_booking.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from pydantic_ai import Agent, ModelRetry, RunContext
1515
from pydantic_ai.messages import ModelMessage
16-
from pydantic_ai.usage import Usage, UsageLimits
16+
from pydantic_ai.usage import RunUsage, UsageLimits
1717

1818
# 'if-token-present' means nothing will be sent (and the example will work) if you don't have logfire configured
1919
logfire.configure(send_to_logfire='if-token-present')
@@ -182,7 +182,7 @@ async def main():
182182
req_date=datetime.date(2025, 1, 10),
183183
)
184184
message_history: list[ModelMessage] | None = None
185-
usage: Usage = Usage()
185+
usage: RunUsage = RunUsage()
186186
# run the agent until a satisfactory flight is found
187187
while True:
188188
result = await search_agent.run(
@@ -213,7 +213,7 @@ async def main():
213213
)
214214

215215

216-
async def find_seat(usage: Usage) -> SeatPreference:
216+
async def find_seat(usage: RunUsage) -> SeatPreference:
217217
message_history: list[ModelMessage] | None = None
218218
while True:
219219
answer = Prompt.ask('What seat would you like?')

0 commit comments

Comments
 (0)