-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Closed as not planned
Labels
Description
Initial Checks
- I confirm that I'm using the latest version of Pydantic AI
- I confirm that I searched for my issue in https://github.com/pydantic/pydantic-ai/issues before opening this issue
Description
When I try the documented example on https://ai.pydantic.dev/durable_execution/temporal/#durable-agent, I get the following error:
(assistant_poc) % python main.py
Traceback (most recent call last):
File "/Users/peter.kim/proj/assistant_poc/main.py", line 54, in <module>
asyncio.run(main())
~~~~~~~~~~~^^^^^^^^
File "/Users/peter.kim/Library/Application Support/uv/python/cpython-3.13.1-macos-aarch64-none/lib/python3.13/asyncio/runners.py", line 194, in run
return runner.run(main)
~~~~~~~~~~^^^^^^
File "/Users/peter.kim/Library/Application Support/uv/python/cpython-3.13.1-macos-aarch64-none/lib/python3.13/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/Users/peter.kim/Library/Application Support/uv/python/cpython-3.13.1-macos-aarch64-none/lib/python3.13/asyncio/base_events.py", line 720, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/Users/peter.kim/proj/assistant_poc/main.py", line 34, in main
plugins=[PydanticAIPlugin()],
~~~~~~~~~~~~~~~~^^
TypeError: Can't instantiate abstract class PydanticAIPlugin without an implementation for abstract methods 'configure_replayer', 'connect_service_client', 'init_client_plugin', 'init_worker_plugin', 'run_replayer', 'run_worker'
The package installation instruction was missing, so I installed pydantic-ai and temporalio separately, and that's probably part of the reason:
uv add temporalio. # installs 1.18.1
uv add pydantic-ai. # installs 1.6.0
If I install pydantic-ai using one of these methods, tempralio 1.18.0 is installed and there's no error:
uv add 'pydantic-ai[temporal]'
uv add 'pydantic-ai-slim[temporal,openai]'
The documentation issue was reported separatedly: #3256
With a corresponding PR: #3257
Example Code
from dataclasses import dataclass
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
from bank_database import DatabaseConn
# SupportDependencies is used to pass data, connections, and logic into the model that will be needed when running
# instructions and tool functions. Dependency injection provides a type-safe way to customise the behavior of your agents.
@dataclass
class SupportDependencies:
customer_id: int
db: DatabaseConn
# This Pydantic model defines the structure of the output returned by the agent.
class SupportOutput(BaseModel):
support_advice: str = Field(description='Advice returned to the customer')
block_card: bool = Field(description="Whether to block the customer's card")
risk: int = Field(description='Risk level of query', ge=0, le=10)
# This agent will act as first-tier support in a bank.
# Agents are generic in the type of dependencies they accept and the type of output they return.
# In this case, the support agent has type `Agent[SupportDependencies, SupportOutput]`.
support_agent = Agent(
'openai:gpt-5',
deps_type=SupportDependencies,
# The response from the agent will, be guaranteed to be a SupportOutput,
# if validation fails the agent is prompted to try again.
output_type=SupportOutput,
instructions=(
'You are a support agent in our bank, give the '
'customer support and judge the risk level of their query.'
),
)
# Dynamic instructions can make use of dependency injection.
# Dependencies are carried via the `RunContext` argument, which is parameterized with the `deps_type` from above.
# If the type annotation here is wrong, static type checkers will catch it.
@support_agent.instructions
async def add_customer_name(ctx: RunContext[SupportDependencies]) -> str:
customer_name = await ctx.deps.db.customer_name(id=ctx.deps.customer_id)
return f"The customer's name is {customer_name!r}"
# The `tool` decorator let you register functions which the LLM may call while responding to a user.
# Again, dependencies are carried via `RunContext`, 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.
@support_agent.tool
async def customer_balance(
ctx: RunContext[SupportDependencies], include_pending: bool
) -> float:
"""Returns the customer's current account balance."""
# The docstring of a tool is also passed to the LLM as the description of the tool.
# Parameter descriptions are extracted from the docstring and added to the parameter schema sent to the LLM.
balance = await ctx.deps.db.customer_balance(
id=ctx.deps.customer_id,
include_pending=include_pending,
)
return balance
... # In a real use case, you'd add more tools and a longer system prompt
async def main():
deps = SupportDependencies(customer_id=123, db=DatabaseConn())
# Run the agent 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 an output.
result = await support_agent.run('What is my balance?', deps=deps)
# The `result.output` will be validated with Pydantic to guarantee it is a `SupportOutput`. Since the agent is generic,
# it'll also be typed as a `SupportOutput` to aid with static type checking.
print(result.output)
"""
support_advice='Hello John, your current account balance, including pending transactions, is $123.45.' block_card=False risk=1
"""
result = await support_agent.run('I just lost my card!', deps=deps)
print(result.output)
"""
support_advice="I'm sorry to hear that, John. We are temporarily blocking your card to prevent unauthorized transactions." block_card=True risk=8
"""Python, Pydantic AI & LLM client version
Reproducible with Python 3.12.8 and 3.13.1
Pydantic AI 1.6.0
Temporalio 1.18.1