Matching PydanticAI'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 below)
unused_dependencies.py
fromdataclassesimportdataclassimporthttpxfrompydantic_aiimportAgent@dataclassclassMyDeps:api_key:strhttp_client:httpx.AsyncClientagent=Agent('openai:gpt-4o',deps_type=MyDeps,)asyncdefmain():asyncwithhttpx.AsyncClient()asclient:deps=MyDeps('foobar',client)result=awaitagent.run('Tell me a joke.',deps=deps,)print(result.data)#> Did you hear about the toothpaste scandal? They called it Colgate.
(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 type, this should be the first parameter of system prompt functions etc.
system_prompt_dependencies.py
fromdataclassesimportdataclassimporthttpxfrompydantic_aiimportAgent,RunContext@dataclassclassMyDeps:api_key:strhttp_client:httpx.AsyncClientagent=Agent('openai:gpt-4o',deps_type=MyDeps,)@agent.system_promptasyncdefget_system_prompt(ctx:RunContext[MyDeps])->str:response=awaitctx.deps.http_client.get('https://example.com',headers={'Authorization':f'Bearer {ctx.deps.api_key}'},)response.raise_for_status()returnf'Prompt: {response.text}'asyncdefmain():asyncwithhttpx.AsyncClient()asclient:deps=MyDeps('foobar',client)result=awaitagent.run('Tell me a joke.',deps=deps)print(result.data)#> Did you hear about the toothpaste scandal? They called it Colgate.
(This example is complete, it can be run "as is" — you'll need to add asyncio.run(main()) to run main)
If these functions are not coroutines (e.g. async def) they are called with
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.
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:
sync_dependencies.py
fromdataclassesimportdataclassimporthttpxfrompydantic_aiimportAgent,RunContext@dataclassclassMyDeps:api_key:strhttp_client:httpx.Clientagent=Agent('openai:gpt-4o',deps_type=MyDeps,)@agent.system_promptdefget_system_prompt(ctx:RunContext[MyDeps])->str:response=ctx.deps.http_client.get('https://example.com',headers={'Authorization':f'Bearer {ctx.deps.api_key}'})response.raise_for_status()returnf'Prompt: {response.text}'asyncdefmain():deps=MyDeps('foobar',httpx.Client())result=awaitagent.run('Tell me a joke.',deps=deps,)print(result.data)#> Did you hear about the toothpaste scandal? They called it Colgate.
(This example is complete, it can be run "as is" — you'll need to add asyncio.run(main()) to run main)
fromdataclassesimportdataclassimporthttpxfrompydantic_aiimportAgent,ModelRetry,RunContext@dataclassclassMyDeps:api_key:strhttp_client:httpx.AsyncClientagent=Agent('openai:gpt-4o',deps_type=MyDeps,)@agent.system_promptasyncdefget_system_prompt(ctx:RunContext[MyDeps])->str:response=awaitctx.deps.http_client.get('https://example.com')response.raise_for_status()returnf'Prompt: {response.text}'@agent.toolasyncdefget_joke_material(ctx:RunContext[MyDeps],subject:str)->str:response=awaitctx.deps.http_client.get('https://example.com#jokes',params={'subject':subject},headers={'Authorization':f'Bearer {ctx.deps.api_key}'},)response.raise_for_status()returnresponse.text@agent.result_validatorasyncdefvalidate_result(ctx:RunContext[MyDeps],final_response:str)->str:response=awaitctx.deps.http_client.post('https://example.com#validate',headers={'Authorization':f'Bearer {ctx.deps.api_key}'},params={'query':final_response},)ifresponse.status_code==400:raiseModelRetry(f'invalid response: {response.text}')response.raise_for_status()returnfinal_responseasyncdefmain():asyncwithhttpx.AsyncClient()asclient:deps=MyDeps('foobar',client)result=awaitagent.run('Tell me a joke.',deps=deps)print(result.data)#> Did you hear about the toothpaste scandal? They called it Colgate.
(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 method on the agent.
joke_app.py
fromdataclassesimportdataclassimporthttpxfrompydantic_aiimportAgent,RunContext@dataclassclassMyDeps:api_key:strhttp_client:httpx.AsyncClientasyncdefsystem_prompt_factory(self)->str:response=awaitself.http_client.get('https://example.com')response.raise_for_status()returnf'Prompt: {response.text}'joke_agent=Agent('openai:gpt-4o',deps_type=MyDeps)@joke_agent.system_promptasyncdefget_system_prompt(ctx:RunContext[MyDeps])->str:returnawaitctx.deps.system_prompt_factory()asyncdefapplication_code(prompt:str)->str:......# now deep within application code we call our agentasyncwithhttpx.AsyncClient()asclient:app_deps=MyDeps('foobar',client)result=awaitjoke_agent.run(prompt,deps=app_deps)returnresult.data
(This example is complete, it can be run "as is")
test_joke_app.py
fromjoke_appimportMyDeps,application_code,joke_agentclassTestMyDeps(MyDeps):asyncdefsystem_prompt_factory(self)->str:return'test prompt'asyncdeftest_application_code():test_deps=TestMyDeps('test_key',None)withjoke_agent.override(deps=test_deps):joke=awaitapplication_code('Tell me a joke.')assertjoke.startswith('Did you hear about the toothpaste scandal?')
Examples
The following examples demonstrate how to use dependencies in PydanticAI: