Skip to content

Commit 0592972

Browse files
authored
rename CallContext -> RunContext (#101)
1 parent ec6649f commit 0592972

22 files changed

+137
-137
lines changed

docs/agents.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ In typing terms, agents are generic in their dependency and result types, e.g.,
1818
Here's a toy example of an agent that simulates a roulette wheel:
1919

2020
```py title="roulette_wheel.py"
21-
from pydantic_ai import Agent, CallContext
21+
from pydantic_ai import Agent, RunContext
2222

2323
roulette_agent = Agent( # (1)!
2424
'openai:gpt-4o',
@@ -32,7 +32,7 @@ roulette_agent = Agent( # (1)!
3232

3333

3434
@roulette_agent.tool
35-
async def roulette_wheel(ctx: CallContext[int], square: int) -> str: # (2)!
35+
async def roulette_wheel(ctx: RunContext[int], square: int) -> str: # (2)!
3636
"""check if the square is a winner"""
3737
return 'winner' if square == ctx.deps else 'loser'
3838

@@ -49,7 +49,7 @@ print(result.data)
4949
```
5050

5151
1. Create an agent, which expects an integer dependency and returns a boolean result. This agent will have type `#!python Agent[int, bool]`.
52-
2. Define a tool that checks if the square is a winner. Here [`CallContext`][pydantic_ai.dependencies.CallContext] is parameterized with the dependency type `int`; if you got the dependency type wrong you'd get a typing error.
52+
2. Define a tool that checks if the square is a winner. Here [`RunContext`][pydantic_ai.dependencies.RunContext] is parameterized with the dependency type `int`; if you got the dependency type wrong you'd get a typing error.
5353
3. In reality, you might want to use a random number here e.g. `random.randint(0, 36)`.
5454
4. `result.data` will be a boolean indicating if the square is a winner. Pydantic performs the result validation, it'll be typed as a `bool` since its type is derived from the `result_type` generic parameter of the agent.
5555

@@ -135,7 +135,7 @@ Here's an example using both types of system prompts:
135135
```py title="system_prompts.py"
136136
from datetime import date
137137

138-
from pydantic_ai import Agent, CallContext
138+
from pydantic_ai import Agent, RunContext
139139

140140
agent = Agent(
141141
'openai:gpt-4o',
@@ -145,7 +145,7 @@ agent = Agent(
145145

146146

147147
@agent.system_prompt # (3)!
148-
def add_the_users_name(ctx: CallContext[str]) -> str:
148+
def add_the_users_name(ctx: RunContext[str]) -> str:
149149
return f"The user's named is {ctx.deps}."
150150

151151

@@ -161,8 +161,8 @@ print(result.data)
161161

162162
1. The agent expects a string dependency.
163163
2. Static system prompt defined at agent creation time.
164-
3. Dynamic system prompt defined via a decorator with [`CallContext`][pydantic_ai.dependencies.CallContext], this is called just after `run_sync`, not when the agent is created, so can benefit from runtime information like the dependencies used on that run.
165-
4. Another dynamic system prompt, system prompts don't have to have the `CallContext` parameter.
164+
3. Dynamic system prompt defined via a decorator with [`RunContext`][pydantic_ai.dependencies.RunContext], this is called just after `run_sync`, not when the agent is created, so can benefit from runtime information like the dependencies used on that run.
165+
4. Another dynamic system prompt, system prompts don't have to have the `RunContext` parameter.
166166

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

@@ -179,8 +179,8 @@ They're useful when it is impractical or impossible to put all the context an ag
179179

180180
There are two different decorator functions to register tools:
181181

182-
1. [`@agent.tool`][pydantic_ai.Agent.tool] — for tools that need access to the agent [context][pydantic_ai.dependencies.CallContext]
183-
2. [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain] — for tools that do not need access to the agent [context][pydantic_ai.dependencies.CallContext]
182+
1. [`@agent.tool`][pydantic_ai.Agent.tool] — for tools that need access to the agent [context][pydantic_ai.dependencies.RunContext]
183+
2. [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain] — for tools that do not need access to the agent [context][pydantic_ai.dependencies.RunContext]
184184

185185
`@agent.tool` is the default since in the majority of cases tools will need access to the agent context.
186186

@@ -189,7 +189,7 @@ Here's an example using both:
189189
```py title="dice_game.py"
190190
import random
191191

192-
from pydantic_ai import Agent, CallContext
192+
from pydantic_ai import Agent, RunContext
193193

194194
agent = Agent(
195195
'gemini-1.5-flash', # (1)!
@@ -209,7 +209,7 @@ def roll_die() -> str:
209209

210210

211211
@agent.tool # (4)!
212-
def get_player_name(ctx: CallContext[str]) -> str:
212+
def get_player_name(ctx: RunContext[str]) -> str:
213213
"""Get the player's name."""
214214
return ctx.deps
215215

@@ -222,7 +222,7 @@ print(dice_result.data)
222222
1. This is a pretty simple task, so we can use the fast and cheap Gemini flash model.
223223
2. We pass the user's name as the dependency, to keep things simple we use just the name as a string as the dependency.
224224
3. This tool doesn't need any context, it just returns a random number. You could probably use a dynamic system prompt in this case.
225-
4. This tool needs the player's name, so it uses `CallContext` to access dependencies which are just the player's name in this case.
225+
4. This tool needs the player's name, so it uses `RunContext` to access dependencies which are just the player's name in this case.
226226
5. Run the agent, passing the player's name as the dependency.
227227

228228
_(This example is complete, it can be run "as is")_
@@ -325,7 +325,7 @@ As the name suggests, function tools use the model's "tools" or "functions" API
325325

326326
### Function tools and schema
327327

328-
Function parameters are extracted from the function signature, and all parameters except `CallContext` are used to build the schema for that tool call.
328+
Function parameters are extracted from the function signature, and all parameters except `RunContext` are used to build the schema for that tool call.
329329

330330
Even better, PydanticAI extracts the docstring from functions and (thanks to [griffe](https://mkdocstrings.github.io/griffe/)) extracts parameter descriptions from the docstring and adds them to the schema.
331331

@@ -395,15 +395,15 @@ Validation errors from both function tool parameter validation and [structured r
395395
You can also raise [`ModelRetry`][pydantic_ai.exceptions.ModelRetry] from within a [tool](#function-tools) or [result validator function](results.md#result-validators-functions) to tell the model it should retry generating a response.
396396

397397
- The default retry count is **1** but can be altered for the [entire agent][pydantic_ai.Agent.__init__], a [specific tool][pydantic_ai.Agent.tool], or a [result validator][pydantic_ai.Agent.__init__].
398-
- You can access the current retry count from within a tool or result validator via [`ctx.retry`][pydantic_ai.dependencies.CallContext].
398+
- You can access the current retry count from within a tool or result validator via [`ctx.retry`][pydantic_ai.dependencies.RunContext].
399399

400400
Here's an example:
401401

402402
```py title="tool_retry.py"
403403
from fake_database import DatabaseConn
404404
from pydantic import BaseModel
405405

406-
from pydantic_ai import Agent, CallContext, ModelRetry
406+
from pydantic_ai import Agent, RunContext, ModelRetry
407407

408408

409409
class ChatResult(BaseModel):
@@ -419,7 +419,7 @@ agent = Agent(
419419

420420

421421
@agent.tool(retries=2)
422-
def get_user_by_name(ctx: CallContext[DatabaseConn], name: str) -> int:
422+
def get_user_by_name(ctx: RunContext[DatabaseConn], name: str) -> int:
423423
"""Get a user's ID from their full name."""
424424
print(name)
425425
#> John
@@ -533,7 +533,7 @@ Consider the following script with type mistakes:
533533
```py title="type_mistakes.py" hl_lines="18 28"
534534
from dataclasses import dataclass
535535

536-
from pydantic_ai import Agent, CallContext
536+
from pydantic_ai import Agent, RunContext
537537

538538

539539
@dataclass
@@ -549,7 +549,7 @@ agent = Agent(
549549

550550

551551
@agent.system_prompt
552-
def add_user_name(ctx: CallContext[str]) -> str: # (2)!
552+
def add_user_name(ctx: RunContext[str]) -> str: # (2)!
553553
return f"The user's name is {ctx.deps}."
554554

555555

@@ -569,7 +569,7 @@ Running `mypy` on this will give the following output:
569569

570570
```bash
571571
➤ uv run mypy type_mistakes.py
572-
type_mistakes.py:18: error: Argument 1 to "system_prompt" of "Agent" has incompatible type "Callable[[CallContext[str]], str]"; expected "Callable[[CallContext[User]], str]" [arg-type]
572+
type_mistakes.py:18: error: Argument 1 to "system_prompt" of "Agent" has incompatible type "Callable[[RunContext[str]], str]"; expected "Callable[[RunContext[User]], str]" [arg-type]
573573
type_mistakes.py:28: error: Argument 1 to "foobar" has incompatible type "bool"; expected "bytes" [arg-type]
574574
Found 2 errors in 1 file (checked 1 source file)
575575
```

docs/dependencies.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ _(This example is complete, it can be run "as is")_
5151

5252
## Accessing Dependencies
5353

54-
Dependencies are accessed through the [`CallContext`][pydantic_ai.dependencies.CallContext] type, this should be the first parameter of system prompt functions etc.
54+
Dependencies are accessed through the [`RunContext`][pydantic_ai.dependencies.RunContext] type, this should be the first parameter of system prompt functions etc.
5555

5656

5757
```py title="system_prompt_dependencies.py" hl_lines="20-27"
5858
from dataclasses import dataclass
5959

6060
import httpx
6161

62-
from pydantic_ai import Agent, CallContext
62+
from pydantic_ai import Agent, RunContext
6363

6464

6565
@dataclass
@@ -75,7 +75,7 @@ agent = Agent(
7575

7676

7777
@agent.system_prompt # (1)!
78-
async def get_system_prompt(ctx: CallContext[MyDeps]) -> str: # (2)!
78+
async def get_system_prompt(ctx: RunContext[MyDeps]) -> str: # (2)!
7979
response = await ctx.deps.http_client.get( # (3)!
8080
'https://example.com',
8181
headers={'Authorization': f'Bearer {ctx.deps.api_key}'}, # (4)!
@@ -92,10 +92,10 @@ async def main():
9292
#> Did you hear about the toothpaste scandal? They called it Colgate.
9393
```
9494

95-
1. [`CallContext`][pydantic_ai.dependencies.CallContext] may optionally be passed to a [`system_prompt`][pydantic_ai.Agent.system_prompt] function as the only argument.
96-
2. [`CallContext`][pydantic_ai.dependencies.CallContext] is parameterized with the type of the dependencies, if this type is incorrect, static type checkers will raise an error.
97-
3. Access dependencies through the [`.deps`][pydantic_ai.dependencies.CallContext.deps] attribute.
98-
4. Access dependencies through the [`.deps`][pydantic_ai.dependencies.CallContext.deps] attribute.
95+
1. [`RunContext`][pydantic_ai.dependencies.RunContext] may optionally be passed to a [`system_prompt`][pydantic_ai.Agent.system_prompt] function as the only argument.
96+
2. [`RunContext`][pydantic_ai.dependencies.RunContext] is parameterized with the type of the dependencies, if this type is incorrect, static type checkers will raise an error.
97+
3. Access dependencies through the [`.deps`][pydantic_ai.dependencies.RunContext.deps] attribute.
98+
4. Access dependencies through the [`.deps`][pydantic_ai.dependencies.RunContext.deps] attribute.
9999

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

@@ -117,7 +117,7 @@ from dataclasses import dataclass
117117

118118
import httpx
119119

120-
from pydantic_ai import Agent, CallContext
120+
from pydantic_ai import Agent, RunContext
121121

122122

123123
@dataclass
@@ -133,7 +133,7 @@ agent = Agent(
133133

134134

135135
@agent.system_prompt
136-
def get_system_prompt(ctx: CallContext[MyDeps]) -> str: # (2)!
136+
def get_system_prompt(ctx: RunContext[MyDeps]) -> str: # (2)!
137137
response = ctx.deps.http_client.get(
138138
'https://example.com', headers={'Authorization': f'Bearer {ctx.deps.api_key}'}
139139
)
@@ -165,7 +165,7 @@ from dataclasses import dataclass
165165

166166
import httpx
167167

168-
from pydantic_ai import Agent, CallContext, ModelRetry
168+
from pydantic_ai import Agent, ModelRetry, RunContext
169169

170170

171171
@dataclass
@@ -181,14 +181,14 @@ agent = Agent(
181181

182182

183183
@agent.system_prompt
184-
async def get_system_prompt(ctx: CallContext[MyDeps]) -> str:
184+
async def get_system_prompt(ctx: RunContext[MyDeps]) -> str:
185185
response = await ctx.deps.http_client.get('https://example.com')
186186
response.raise_for_status()
187187
return f'Prompt: {response.text}'
188188

189189

190190
@agent.tool # (1)!
191-
async def get_joke_material(ctx: CallContext[MyDeps], subject: str) -> str:
191+
async def get_joke_material(ctx: RunContext[MyDeps], subject: str) -> str:
192192
response = await ctx.deps.http_client.get(
193193
'https://example.com#jokes',
194194
params={'subject': subject},
@@ -199,7 +199,7 @@ async def get_joke_material(ctx: CallContext[MyDeps], subject: str) -> str:
199199

200200

201201
@agent.result_validator # (2)!
202-
async def validate_result(ctx: CallContext[MyDeps], final_response: str) -> str:
202+
async def validate_result(ctx: RunContext[MyDeps], final_response: str) -> str:
203203
response = await ctx.deps.http_client.post(
204204
'https://example.com#validate',
205205
headers={'Authorization': f'Bearer {ctx.deps.api_key}'},
@@ -219,8 +219,8 @@ async def main():
219219
#> Did you hear about the toothpaste scandal? They called it Colgate.
220220
```
221221

222-
1. To pass `CallContext` to a tool, use the [`tool`][pydantic_ai.Agent.tool] decorator.
223-
2. `CallContext` may optionally be passed to a [`result_validator`][pydantic_ai.Agent.result_validator] function as the first argument.
222+
1. To pass `RunContext` to a tool, use the [`tool`][pydantic_ai.Agent.tool] decorator.
223+
2. `RunContext` may optionally be passed to a [`result_validator`][pydantic_ai.Agent.result_validator] function as the first argument.
224224

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

@@ -238,7 +238,7 @@ from dataclasses import dataclass
238238

239239
import httpx
240240

241-
from pydantic_ai import Agent, CallContext
241+
from pydantic_ai import Agent, RunContext
242242

243243

244244
@dataclass
@@ -256,7 +256,7 @@ joke_agent = Agent('openai:gpt-4o', deps_type=MyDeps)
256256

257257

258258
@joke_agent.system_prompt
259-
async def get_system_prompt(ctx: CallContext[MyDeps]) -> str:
259+
async def get_system_prompt(ctx: RunContext[MyDeps]) -> str:
260260
return await ctx.deps.system_prompt_factory() # (2)!
261261

262262

@@ -303,7 +303,7 @@ Since dependencies can be any python type, and agents are just python objects, a
303303
```py title="agents_as_dependencies.py"
304304
from dataclasses import dataclass
305305

306-
from pydantic_ai import Agent, CallContext
306+
from pydantic_ai import Agent, RunContext
307307

308308

309309
@dataclass
@@ -324,7 +324,7 @@ factory_agent = Agent('gemini-1.5-pro', result_type=list[str])
324324

325325

326326
@joke_agent.tool
327-
async def joke_factory(ctx: CallContext[MyDeps], count: int) -> str:
327+
async def joke_factory(ctx: RunContext[MyDeps], count: int) -> str:
328328
r = await ctx.deps.factory_agent.run(f'Please generate {count} jokes.')
329329
return '\n'.join(r.data)
330330

docs/index.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Here is a concise example using PydanticAI to build a support agent for a bank:
5858
from dataclasses import dataclass
5959

6060
from pydantic import BaseModel, Field
61-
from pydantic_ai import Agent, CallContext
61+
from pydantic_ai import Agent, RunContext
6262

6363
from bank_database import DatabaseConn
6464

@@ -87,14 +87,14 @@ support_agent = Agent( # (1)!
8787

8888

8989
@support_agent.system_prompt # (5)!
90-
async def add_customer_name(ctx: CallContext[SupportDependencies]) -> str:
90+
async def add_customer_name(ctx: RunContext[SupportDependencies]) -> str:
9191
customer_name = await ctx.deps.db.customer_name(id=ctx.deps.customer_id)
9292
return f"The customer's name is {customer_name!r}"
9393

9494

9595
@support_agent.tool # (6)!
9696
async def customer_balance(
97-
ctx: CallContext[SupportDependencies], include_pending: bool
97+
ctx: RunContext[SupportDependencies], include_pending: bool
9898
) -> str:
9999
"""Returns the customer's current account balance.""" # (7)!
100100
balance = await ctx.deps.db.customer_balance(
@@ -126,8 +126,8 @@ async def main():
126126
2. Here we configure the agent to use [OpenAI's GPT-4o model](api/models/openai.md), you can also set the model when running the agent.
127127
3. The `SupportDependencies` dataclass is used to pass data, connections, and logic into the model that will be needed when running [system prompt](agents.md#system-prompts) and [tool](agents.md#function-tools) functions. PydanticAI's system of dependency injection provides a type-safe way to customise the behavior of your agents, and can be especially useful when running unit tests and evals.
128128
4. Static [system prompts](agents.md#system-prompts) can be registered with the [`system_prompt` keyword argument][pydantic_ai.Agent.__init__] to the agent.
129-
5. Dynamic [system prompts](agents.md#system-prompts) can be registered with the [`@agent.system_prompt`][pydantic_ai.Agent.system_prompt] decorator, and can make use of dependency injection. Dependencies are carried via the [`CallContext`][pydantic_ai.dependencies.CallContext] argument, which is parameterized with the `deps_type` from above. If the type annotation here is wrong, static type checkers will catch it.
130-
6. [Tools](agents.md#function-tools) let you register "tools" which the LLM may call while responding to a user. Again, dependencies are carried via [`CallContext`][pydantic_ai.dependencies.CallContext], and any other arguments become the tool schema passed to the LLM. Pydantic is used to validate these arguments, and errors are passed back to the LLM so it can retry.
129+
5. Dynamic [system prompts](agents.md#system-prompts) can be registered with the [`@agent.system_prompt`][pydantic_ai.Agent.system_prompt] decorator, and can make use of dependency injection. Dependencies are carried via the [`RunContext`][pydantic_ai.dependencies.RunContext] argument, which is parameterized with the `deps_type` from above. If the type annotation here is wrong, static type checkers will catch it.
130+
6. [Tools](agents.md#function-tools) let you register "tools" which the LLM may call while responding to a user. Again, dependencies are carried via [`RunContext`][pydantic_ai.dependencies.RunContext], and any other arguments become the tool schema passed to the LLM. Pydantic is used to validate these arguments, and errors are passed back to the LLM so it can retry.
131131
7. The docstring of a tool is also passed to the LLM as the description of the tool. Parameter descriptions are [extracted](agents.md#function-tools-and-schema) from the docstring and added to the tool schema sent to the LLM.
132132
8. [Run the agent](agents.md#running-agents) asynchronously, conducting a conversation with the LLM until a final response is reached. Even in this fairly simple case, the agent will exchange multiple messages with the LLM as tools are called to retrieve a result.
133133
9. The response from the agent will, be guaranteed to be a `SupportResult`, if validation fails [reflection](agents.md#reflection-and-self-correction) will mean the agent is prompted to try again.

docs/results.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ from typing import Union
114114
from fake_database import DatabaseConn, QueryError
115115
from pydantic import BaseModel
116116

117-
from pydantic_ai import Agent, CallContext, ModelRetry
117+
from pydantic_ai import Agent, RunContext, ModelRetry
118118

119119

120120
class Success(BaseModel):
@@ -135,7 +135,7 @@ agent: Agent[DatabaseConn, Response] = Agent(
135135

136136

137137
@agent.result_validator
138-
async def validate_result(ctx: CallContext[DatabaseConn], result: Response) -> Response:
138+
async def validate_result(ctx: RunContext[DatabaseConn], result: Response) -> Response:
139139
if isinstance(result, InvalidRequest):
140140
return result
141141
try:

docs/testing-evals.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Let's write unit tests for the following application code:
4040
import asyncio
4141
from datetime import date
4242

43-
from pydantic_ai import Agent, CallContext
43+
from pydantic_ai import Agent, RunContext
4444

4545
from fake_database import DatabaseConn # (1)!
4646
from weather_service import WeatherService # (2)!
@@ -54,7 +54,7 @@ weather_agent = Agent(
5454

5555
@weather_agent.tool
5656
def weather_forecast(
57-
ctx: CallContext[WeatherService], location: str, forecast_date: date
57+
ctx: RunContext[WeatherService], location: str, forecast_date: date
5858
) -> str:
5959
if forecast_date < date.today(): # (3)!
6060
return ctx.deps.get_historic_weather(location, forecast_date)
@@ -301,7 +301,7 @@ import json
301301
from pathlib import Path
302302
from typing import Union
303303

304-
from pydantic_ai import Agent, CallContext
304+
from pydantic_ai import Agent, RunContext
305305

306306
from fake_database import DatabaseConn
307307

@@ -349,7 +349,7 @@ sql_agent = Agent(
349349

350350

351351
@sql_agent.system_prompt
352-
async def system_prompt(ctx: CallContext[SqlSystemPrompt]) -> str:
352+
async def system_prompt(ctx: RunContext[SqlSystemPrompt]) -> str:
353353
return ctx.deps.build_prompt()
354354

355355

0 commit comments

Comments
 (0)