Skip to main content
GitHub

Pydantic AI

Auto-instrument Pydantic AI for type-safe agents.

Risicare automatically instruments Pydantic AI for type-safe agent development.

Python only

This framework integration is available in the Python SDK only. No JavaScript package exists for Pydantic AI.

Installation

pip install risicare[pydantic-ai]
# or
pip install risicare pydantic-ai

Version 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

FeatureDescription
Agent RunsFull run/run_sync/run_stream calls
Model CallsUnderlying LLM API calls

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-slim 2.31) 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. You still get the underlying LLM calls as provider spans.

Span Hierarchy

pydantic_ai.agent.run/{name} (AGENT kind)
├── openai.chat.completions.create (provider span, natural child)
└── openai.chat.completions.create (provider span, natural child)

Provider Spans

Pydantic AI instrumentation creates agent/framework-level spans. Underlying LLM calls (e.g., OpenAI, Anthropic) are traced separately by provider instrumentation, giving you both framework-level and LLM-level visibility.

Structured Outputs

Type-safe outputs are captured:

from pydantic import BaseModel
 
class CityInfo(BaseModel):
    name: str
    country: str
    population: int
 
agent = Agent(
    "openai:gpt-4o",
    result_type=CityInfo
)
 
result = agent.run_sync("Tell me about Paris")
# result.output is CityInfo, schema captured in span

Tools

Tool executions are traced:

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

async with agent.run_stream("Write a story") as response:
    async for chunk in response.stream():
        print(chunk, end="")

Multiple Models

# OpenAI
agent = Agent("openai:gpt-4o")
 
# Anthropic
agent = Agent("anthropic:claude-3-sonnet-20240229")
 
# Gemini
agent = Agent("gemini-1.5-pro")
 
# Each model is traced with correct provider

Next Steps