Pydantic AI
Auto-instrument Pydantic AI for type-safe agents.
Risicare automatically instruments Pydantic AI for type-safe agent development.
Python only
Installation
pip install 'risicare[pydantic-ai]'
# or
pip install risicare pydantic-aiVersion Compatibility
Requires pydantic-ai >= 0.1.0.
Auto-Instrumentation
import risicare
from pydantic_ai import Agent
risicare.init()
agent = Agent(
"openai:gpt-4o",
system_prompt="You are a helpful assistant."
)
# Automatically traced
result = agent.run_sync("Hello!")What's Captured
| Feature | Description |
|---|---|
| Agent Runs | run, run_sync and run_stream calls (run_stream works from Python 0.5.1; in 0.5.0 it raised TypeError) — see Streaming |
| Model Calls | Underlying LLM API calls, when the model uses an API that the SDK traces — see the notice below |
Only the model is captured on current Pydantic AI
The agent span reliably carries one framework attribute,
framework.pydantic_ai.model. Three others the integration tries to set —
framework.pydantic_ai.result_type, .tools and .system_prompt_length —
read attribute names (result_type, tools/_tools, system_prompt) that
Pydantic AI no longer exposes on the Agent object. On a current release
(measured against pydantic-ai 2.54.0 with Python 0.6.0) all three are silently absent: the
reads are defensive, so nothing errors and nothing is recorded.
Tool executions and result validation are not captured as framework attributes or as child spans.
In 0.6.0 you get the underlying LLM calls as provider spans only when the model
uses an API that the SDK traces. An openai: model uses the OpenAI Responses API,
which 0.6.0 does not trace: the run then has no LLM-call span and no cost. The agent
spans still carry the run's token counts (gen_ai.usage.prompt_tokens,
gen_ai.usage.completion_tokens, from result.usage()). Agent("openai-chat:gpt-4o") uses the Chat
Completions API, and the provider spans arrive. The value of
framework.pydantic_ai.model is the model class (for example
OpenAIResponsesModel()), not the model id.
Span Hierarchy
pydantic_ai.agent.run/{name} (AGENT kind)
└── pydantic_ai.agent.run/{name} (run_sync only: the inner span is operation "run")
└── openai.chat.completions.create/{model} (chat-completions models only, one per model call)
An async run() sends one agent span. Without name=, the first call on an agent names the outer
span Agent (and, for run_sync(), the inner span instance); Pydantic AI then keeps the
inferred name instance for later run() and run_sync() calls. Set name= for stable span names. Both agent spans carry the run's token counts.
Provider Spans
Pydantic AI instrumentation creates the agent spans. A model call is traced by the provider instrumentation when the model uses an API that the SDK traces (for OpenAI, the Chat Completions API), and its span is a child of the agent span.
Structured Outputs
A structured output works with tracing on. In 0.6.0 the span does not record the output type or the schema:
from pydantic import BaseModel
class CityInfo(BaseModel):
name: str
country: str
population: int
agent = Agent(
"openai:gpt-4o",
output_type=CityInfo
)
result = agent.run_sync("Tell me about Paris")
# result.output is a CityInfoTools
A tool runs with tracing on. In 0.6.0, tool executions are not traced: no span is sent for a tool call.
from pydantic_ai import Agent, RunContext
agent = Agent("openai:gpt-4o")
@agent.tool
def get_weather(ctx: RunContext, location: str) -> str:
"""Get weather for a location."""
return f"Sunny in {location}"
# Tool executions are not traced: no per-tool child spans are emitted, and on
# current Pydantic AI the framework.pydantic_ai.tools attribute is not set
# either (see "What's Captured" above).
result = agent.run_sync("What's the weather in Paris?")Dependencies
Dependency injection works normally with Risicare — but nothing about your dependencies is recorded:
from dataclasses import dataclass
@dataclass
class Deps:
user_id: str
api_key: str
agent = Agent("openai:gpt-4o", deps_type=Deps)
@agent.tool
def get_user_data(ctx: RunContext[Deps]) -> str:
return f"Data for {ctx.deps.user_id}"
result = agent.run_sync(
"Get my data",
deps=Deps(user_id="123", api_key="secret")
)
# No dependency is recorded on the span — not the values, not the field names,
# not the type. There is no deps capture anywhere in the SDK, so there is also
# no secret-filtering step to rely on.Streaming
run_stream() works with the SDK active (Python 0.5.1 and later) and sends one agent span, pydantic_ai.agent.run_stream/{name}. The span opens when the async with block starts and ends when the block ends; its status is error when your code raises inside the block. For a Chat Completions model, the provider span is a child of the agent span. In Python 0.5.0, run_stream() raised TypeError: 'coroutine' object does not support the asynchronous context manager protocol when the SDK was active; with 0.5.0, use agent.run() or agent.run_sync().
async with agent.run_stream("Write a story") as response:
async for chunk in response.stream_text(delta=True):
print(chunk, end="")Multiple Models
# OpenAI (needs OPENAI_API_KEY)
agent = Agent("openai:gpt-4o")
# Anthropic (needs ANTHROPIC_API_KEY)
agent = Agent("anthropic:claude-sonnet-5-5")
# Gemini (needs GOOGLE_API_KEY)
agent = Agent("google:gemini-2.5-pro")