9201ef759e
Harness Compat / harness compat (push) Failing after 0s
CI / test on 3.12 (standard) (push) Has been cancelled
CI / test on 3.13 (standard) (push) Has been cancelled
CI / test on 3.14 (standard) (push) Has been cancelled
CI / test on 3.10 (all-extras) (push) Has been cancelled
CI / test on 3.11 (all-extras) (push) Has been cancelled
CI / test on 3.12 (all-extras) (push) Has been cancelled
CI / test on 3.14 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.10 (pydantic-evals) (push) Has been cancelled
CI / test on 3.11 (pydantic-evals) (push) Has been cancelled
CI / test on 3.12 (pydantic-evals) (push) Has been cancelled
CI / deploy-docs-preview (push) Has been cancelled
CI / build release artifacts (push) Has been cancelled
CI / publish to PyPI (push) Has been cancelled
CI / Send tweet (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / mypy (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / test on 3.10 (standard) (push) Has been cancelled
CI / test on 3.11 (standard) (push) Has been cancelled
CI / test on 3.13 (all-extras) (push) Has been cancelled
CI / test on 3.14 (all-extras) (push) Has been cancelled
CI / test on 3.10 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.11 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.12 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-evals) (push) Has been cancelled
CI / test on 3.14 (pydantic-evals) (push) Has been cancelled
CI / test on 3.10 (lowest-versions) (push) Has been cancelled
CI / test on 3.11 (lowest-versions) (push) Has been cancelled
CI / test on 3.12 (lowest-versions) (push) Has been cancelled
CI / test on 3.13 (lowest-versions) (push) Has been cancelled
CI / test on 3.14 (lowest-versions) (push) Has been cancelled
CI / test examples on 3.11 (push) Has been cancelled
CI / test examples on 3.12 (push) Has been cancelled
CI / test examples on 3.13 (push) Has been cancelled
CI / test examples on 3.14 (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / check (push) Has been cancelled
CI / deploy-docs (push) Has been cancelled
311 lines
11 KiB
Markdown
311 lines
11 KiB
Markdown
# Dependencies
|
|
|
|
Pydantic AI uses a dependency injection system to provide data and services to your agent's [system prompts](agent.md#system-prompts), [tools](tools.md) and [output validators](output.md#output-validator-functions).
|
|
|
|
Matching Pydantic AI's design philosophy, our dependency system tries to use existing best practice in Python development rather than inventing esoteric "magic", this should make dependencies type-safe, understandable, easier to test, and ultimately easier to deploy in production.
|
|
|
|
## Defining Dependencies
|
|
|
|
Dependencies can be any python type. While in simple cases you might be able to pass a single object as a dependency (e.g. an HTTP connection), [dataclasses][] are generally a convenient container when your dependencies included multiple objects.
|
|
|
|
Here's an example of defining an agent that requires dependencies.
|
|
|
|
(**Note:** dependencies aren't actually used in this example, see [Accessing Dependencies](#accessing-dependencies) below)
|
|
|
|
```python {title="unused_dependencies.py"}
|
|
from dataclasses import dataclass
|
|
|
|
import httpx
|
|
|
|
from pydantic_ai import Agent
|
|
|
|
|
|
@dataclass
|
|
class MyDeps: # (1)!
|
|
api_key: str
|
|
http_client: httpx.AsyncClient
|
|
|
|
|
|
agent = Agent(
|
|
'openai:gpt-5.2',
|
|
deps_type=MyDeps, # (2)!
|
|
)
|
|
|
|
|
|
async def main():
|
|
async with httpx.AsyncClient() as client:
|
|
deps = MyDeps('foobar', client)
|
|
result = await agent.run(
|
|
'Tell me a joke.',
|
|
deps=deps, # (3)!
|
|
)
|
|
print(result.output)
|
|
#> Did you hear about the toothpaste scandal? They called it Colgate.
|
|
```
|
|
|
|
1. Define a dataclass to hold dependencies.
|
|
2. Pass the dataclass type to the `deps_type` argument of the [`Agent` constructor][pydantic_ai.agent.Agent.__init__]. **Note**: we're passing the type here, NOT an instance, this parameter is not actually used at runtime, it's here so we can get full type checking of the agent.
|
|
3. When running the agent, pass an instance of the dataclass to the `deps` parameter.
|
|
|
|
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
|
|
|
|
## Accessing Dependencies
|
|
|
|
Dependencies are accessed through the [`RunContext`][pydantic_ai.tools.RunContext] type, this should be the first parameter of system prompt functions etc.
|
|
|
|
```python {title="system_prompt_dependencies.py" hl_lines="20-27"}
|
|
from dataclasses import dataclass
|
|
|
|
import httpx
|
|
|
|
from pydantic_ai import Agent, RunContext
|
|
|
|
|
|
@dataclass
|
|
class MyDeps:
|
|
api_key: str
|
|
http_client: httpx.AsyncClient
|
|
|
|
|
|
agent = Agent(
|
|
'openai:gpt-5.2',
|
|
deps_type=MyDeps,
|
|
)
|
|
|
|
|
|
@agent.system_prompt # (1)!
|
|
async def get_system_prompt(ctx: RunContext[MyDeps]) -> str: # (2)!
|
|
response = await ctx.deps.http_client.get( # (3)!
|
|
'https://example.com',
|
|
headers={'Authorization': f'Bearer {ctx.deps.api_key}'}, # (4)!
|
|
)
|
|
response.raise_for_status()
|
|
return f'Prompt: {response.text}'
|
|
|
|
|
|
async def main():
|
|
async with httpx.AsyncClient() as client:
|
|
deps = MyDeps('foobar', client)
|
|
result = await agent.run('Tell me a joke.', deps=deps)
|
|
print(result.output)
|
|
#> Did you hear about the toothpaste scandal? They called it Colgate.
|
|
```
|
|
|
|
1. [`RunContext`][pydantic_ai.tools.RunContext] may optionally be passed to a [`system_prompt`][pydantic_ai.agent.Agent.system_prompt] function as the only argument.
|
|
2. [`RunContext`][pydantic_ai.tools.RunContext] is parameterized with the type of the dependencies, if this type is incorrect, static type checkers will raise an error.
|
|
3. Access dependencies through the [`.deps`][pydantic_ai.tools.RunContext.deps] attribute.
|
|
4. Access dependencies through the [`.deps`][pydantic_ai.tools.RunContext.deps] attribute.
|
|
|
|
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
|
|
|
|
In addition to [`.deps`][pydantic_ai.tools.RunContext.deps], [`RunContext`][pydantic_ai.tools.RunContext] provides access to the running agent via [`.agent`][pydantic_ai.tools.RunContext.agent], which is useful when [tools](tools.md), [hooks](hooks.md), or [capabilities](capabilities.md) need to read agent properties like [`name`][pydantic_ai.agent.Agent.name] or [`output_type`][pydantic_ai.agent.Agent.output_type].
|
|
|
|
Dependency fields can also be referenced in instructions and descriptions via [template strings](agent-spec.md#template-strings) — for example, `TemplateStr('Hello {{name}}')` renders `name` from the deps object at runtime. This is especially useful in [agent specs](agent-spec.md) where callables aren't available.
|
|
|
|
### Asynchronous vs. Synchronous dependencies
|
|
|
|
[System prompt functions](agent.md#system-prompts), [function tools](tools.md) and [output validators](output.md#output-validator-functions) are all run in the async context of an agent run.
|
|
|
|
If these functions are not coroutines (e.g. `async def`) they are called with
|
|
[`run_in_executor`][asyncio.loop.run_in_executor] in a thread pool. It's therefore marginally preferable
|
|
to use `async` methods where dependencies perform IO, although synchronous dependencies should work fine too.
|
|
|
|
!!! note "`run` vs. `run_sync` and Asynchronous vs. Synchronous dependencies"
|
|
Whether you use synchronous or asynchronous dependencies is completely independent of whether you use `run` or `run_sync` — `run_sync` is just a wrapper around `run` and agents are always run in an async context.
|
|
|
|
Here's the same example as above, but with a synchronous dependency:
|
|
|
|
```python {title="sync_dependencies.py"}
|
|
from dataclasses import dataclass
|
|
|
|
import httpx
|
|
|
|
from pydantic_ai import Agent, RunContext
|
|
|
|
|
|
@dataclass
|
|
class MyDeps:
|
|
api_key: str
|
|
http_client: httpx.Client # (1)!
|
|
|
|
|
|
agent = Agent(
|
|
'openai:gpt-5.2',
|
|
deps_type=MyDeps,
|
|
)
|
|
|
|
|
|
@agent.system_prompt
|
|
def get_system_prompt(ctx: RunContext[MyDeps]) -> str: # (2)!
|
|
response = ctx.deps.http_client.get(
|
|
'https://example.com', headers={'Authorization': f'Bearer {ctx.deps.api_key}'}
|
|
)
|
|
response.raise_for_status()
|
|
return f'Prompt: {response.text}'
|
|
|
|
|
|
async def main():
|
|
deps = MyDeps('foobar', httpx.Client())
|
|
result = await agent.run(
|
|
'Tell me a joke.',
|
|
deps=deps,
|
|
)
|
|
print(result.output)
|
|
#> Did you hear about the toothpaste scandal? They called it Colgate.
|
|
```
|
|
|
|
1. Here we use a synchronous `httpx.Client` instead of an asynchronous `httpx.AsyncClient`.
|
|
2. To match the synchronous dependency, the system prompt function is now a plain function, not a coroutine.
|
|
|
|
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
|
|
|
|
## Full Example
|
|
|
|
As well as system prompts, dependencies can be used in [tools](tools.md) and [output validators](output.md#output-validator-functions).
|
|
|
|
```python {title="full_example.py" hl_lines="27-35 38-48"}
|
|
from dataclasses import dataclass
|
|
|
|
import httpx
|
|
|
|
from pydantic_ai import Agent, ModelRetry, RunContext
|
|
|
|
|
|
@dataclass
|
|
class MyDeps:
|
|
api_key: str
|
|
http_client: httpx.AsyncClient
|
|
|
|
|
|
agent = Agent(
|
|
'openai:gpt-5.2',
|
|
deps_type=MyDeps,
|
|
)
|
|
|
|
|
|
@agent.system_prompt
|
|
async def get_system_prompt(ctx: RunContext[MyDeps]) -> str:
|
|
response = await ctx.deps.http_client.get('https://example.com')
|
|
response.raise_for_status()
|
|
return f'Prompt: {response.text}'
|
|
|
|
|
|
@agent.tool # (1)!
|
|
async def get_joke_material(ctx: RunContext[MyDeps], subject: str) -> str:
|
|
response = await ctx.deps.http_client.get(
|
|
'https://example.com#jokes',
|
|
params={'subject': subject},
|
|
headers={'Authorization': f'Bearer {ctx.deps.api_key}'},
|
|
)
|
|
response.raise_for_status()
|
|
return response.text
|
|
|
|
|
|
@agent.output_validator # (2)!
|
|
async def validate_output(ctx: RunContext[MyDeps], output: str) -> str:
|
|
response = await ctx.deps.http_client.post(
|
|
'https://example.com#validate',
|
|
headers={'Authorization': f'Bearer {ctx.deps.api_key}'},
|
|
params={'query': output},
|
|
)
|
|
if response.status_code == 400:
|
|
raise ModelRetry(f'invalid response: {response.text}')
|
|
response.raise_for_status()
|
|
return output
|
|
|
|
|
|
async def main():
|
|
async with httpx.AsyncClient() as client:
|
|
deps = MyDeps('foobar', client)
|
|
result = await agent.run('Tell me a joke.', deps=deps)
|
|
print(result.output)
|
|
#> Did you hear about the toothpaste scandal? They called it Colgate.
|
|
```
|
|
|
|
1. To pass `RunContext` to a tool, use the [`tool`][pydantic_ai.agent.Agent.tool] decorator.
|
|
2. `RunContext` may optionally be passed to a [`output_validator`][pydantic_ai.agent.Agent.output_validator] function as the first argument.
|
|
|
|
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
|
|
|
|
## Overriding Dependencies
|
|
|
|
When testing agents, it's useful to be able to customise dependencies.
|
|
|
|
While this can sometimes be done by calling the agent directly within unit tests, we can also override dependencies
|
|
while calling application code which in turn calls the agent.
|
|
|
|
This is done via the [`override`][pydantic_ai.agent.Agent.override] method on the agent.
|
|
|
|
```python {title="joke_app.py"}
|
|
from dataclasses import dataclass
|
|
|
|
import httpx
|
|
|
|
from pydantic_ai import Agent, RunContext
|
|
|
|
|
|
@dataclass
|
|
class MyDeps:
|
|
api_key: str
|
|
http_client: httpx.AsyncClient
|
|
|
|
async def system_prompt_factory(self) -> str: # (1)!
|
|
response = await self.http_client.get('https://example.com')
|
|
response.raise_for_status()
|
|
return f'Prompt: {response.text}'
|
|
|
|
|
|
joke_agent = Agent('openai:gpt-5.2', deps_type=MyDeps)
|
|
|
|
|
|
@joke_agent.system_prompt
|
|
async def get_system_prompt(ctx: RunContext[MyDeps]) -> str:
|
|
return await ctx.deps.system_prompt_factory() # (2)!
|
|
|
|
|
|
async def application_code(prompt: str) -> str: # (3)!
|
|
...
|
|
...
|
|
# now deep within application code we call our agent
|
|
async with httpx.AsyncClient() as client:
|
|
app_deps = MyDeps('foobar', client)
|
|
result = await joke_agent.run(prompt, deps=app_deps) # (4)!
|
|
return result.output
|
|
```
|
|
|
|
1. Define a method on the dependency to make the system prompt easier to customise.
|
|
2. Call the system prompt factory from within the system prompt function.
|
|
3. Application code that calls the agent, in a real application this might be an API endpoint.
|
|
4. Call the agent from within the application code, in a real application this call might be deep within a call stack. Note `app_deps` here will NOT be used when deps are overridden.
|
|
|
|
_(This example is complete, it can be run "as is")_
|
|
|
|
```python {title="test_joke_app.py" hl_lines="10-12" call_name="test_application_code" requires="joke_app.py"}
|
|
from joke_app import MyDeps, application_code, joke_agent
|
|
|
|
|
|
class TestMyDeps(MyDeps): # (1)!
|
|
async def system_prompt_factory(self) -> str:
|
|
return 'test prompt'
|
|
|
|
|
|
async def test_application_code():
|
|
test_deps = TestMyDeps('test_key', None) # (2)!
|
|
with joke_agent.override(deps=test_deps): # (3)!
|
|
joke = await application_code('Tell me a joke.') # (4)!
|
|
assert joke.startswith('Did you hear about the toothpaste scandal?')
|
|
```
|
|
|
|
1. Define a subclass of `MyDeps` in tests to customise the system prompt factory.
|
|
2. Create an instance of the test dependency, we don't need to pass an `http_client` here as it's not used.
|
|
3. Override the dependencies of the agent for the duration of the `with` block, `test_deps` will be used when the agent is run.
|
|
4. Now we can safely call our application code, the agent will use the overridden dependencies.
|
|
|
|
## Examples
|
|
|
|
The following examples demonstrate how to use dependencies in Pydantic AI:
|
|
|
|
- [Weather Agent](examples/weather-agent.md)
|
|
- [SQL Generation](examples/sql-gen.md)
|
|
- [RAG](examples/rag.md)
|