Pydantic AI
Trace agents built with Pydantic AI with Rius: the framework is OpenTelemetry-native and emits its own spans for agent runs, model requests, and tool calls, so the whole structure arrives with no wrappers at all. This guide covers the Python framework; the runnable example behind it lives in agent-observability-examples .
How coverage works
This integration works the other way around from the client-library ones:
no auto-instrumentation extra is involved. rius.init() installs the
global OpenTelemetry tracer provider, and Agent.instrument_all(True)
tells Pydantic AI to emit its spans through it. The framework provides the
run root, the generations, and the tool spans itself; Rius reads its
OpenTelemetry GenAI attributes natively and computes cost server-side.
Setup
pip install glassflow-rius pydantic-aiNo extra is needed. If your project uses the base SDK only, that is enough.
import rius
from pydantic_ai import Agent
rius.init(api_key="gf_...", service_name="my-agent")
Agent.instrument_all(True)
support_agent = Agent(
"openai:gpt-5-mini",
instructions="Answer support questions; check accounts with the tool.",
)
@support_agent.tool_plain
def account_status(email: str) -> str:
"""Fetch a customer's account standing by email."""
return lookup(email)
with rius.session(conversation_id):
result = support_agent.run_sync(ticket)instrument_all switches instrumentation on for every agent; per-agent
control and content settings live on the framework’s
InstrumentationSettings.
What lands where
| In the framework | In the trace |
|---|---|
| An agent run | The root span, named invoke_agent <agent name> |
| A model request | A generation named chat <model>, with tokens and cost |
| A tool call | A span named execute_tool <tool name> |
A run that calls a tool looks like this (real trace from the example,
pydantic-ai-slim 2.36.0):
invoke_agent support_agent 6.0s
├─ chat gpt-5-mini 3.3s decides to call the tool
├─ execute_tool search_knowledge_base 0ms
└─ chat gpt-5-mini 2.7s answersCompared to frameworks covered through a client-library integration, this is the richer default: spans are named after your agents, models, and tools, and tool calls appear without any wrapping.
Quirks
Verified against pydantic-ai-slim 2.36.0:
- Span statuses arrive UNSET rather than OK on successful spans; the trace status still reads Ok.
- Content capture is framework-side. What the spans carry (prompts,
completions, tool arguments) is controlled by Pydantic AI’s
InstrumentationSettings(for exampleinclude_content), on top of the SDK’s own privacy controls at export time.
Sessions
One conversation is one session: scope each run with
rius.session(), reusing your conversation
id. The framework’s spans inherit the scope like any other span.
Verify
Run one request that calls a tool, then open the trace in the console: the
invoke_agent root, chat generations with model, tokens, and cost, and
execute_tool spans should all be there.
Using Pydantic AI without the Rius SDK works too: point any OTel setup at the ingest endpoint per vanilla OpenTelemetry SDKs. The SDK path above adds the exporter wiring, sessions, heartbeats, and privacy controls for free.