What does Rius record when my agent calls Pydantic AI?
Rius records each Pydantic AI agent run as one trace: a root span for the run, a generation for every model request, and a span for every tool call. The generations carry the model, token counts and cost. Prompts, replies and tool arguments arrive when Pydantic AI’s content settings allow them. This integration is available in Python only.
How it works
Pydantic AI emits its own OpenTelemetry spans for agent runs, model requests and tool calls. Rius reads those spans natively, so no auto-instrumentation extra and no wrappers are involved. rius.init() installs the global tracer provider, and Agent.instrument_all(True) tells Pydantic AI to send its spans through it. Rius then computes cost on the server from the model and token counts. You never send a cost value.
This is what each part of a run becomes in the trace:
| In Pydantic AI | On the Rius trace |
|---|---|
| An agent run | The root span, named invoke_agent followed by your agent’s name |
| A model request | A generation named chat followed by the model, with tokens and cost |
| A tool call | A span named execute_tool followed by the tool’s name |
Because spans are named after your agents, models and tools, you can read a run without adding any observe wrappers. A run that calls one tool looks like this:
invoke_agent support_agent 6.0s
├─ chat gpt-5-mini 3.3s decides to call the tool
├─ execute_tool account_status 0ms
└─ chat gpt-5-mini 2.7s answersOn the generations you also get time to first token (glassflow-rius 1.0.1 or later) and reasoning tokens (1.0.2 or later). Cost is a list-price estimate in US dollars from LiteLLM’s public model-price catalog. A model that is not in the catalog gets an empty cost, not zero.
Set it up
-
Install the SDK and Pydantic AI. No extra is needed, because the base package is enough. Python 3.10 or newer is required.
pip install glassflow-rius pydantic-ai -
Call
rius.init()and switch on Pydantic AI’s instrumentation for every agent. Define tools as you normally do. They are traced without any wrapping.import rius from pydantic_ai import Agent rius.init(api_key="ri_...", 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) -
Group a conversation by wrapping each run in
rius.session()with your own conversation id, as the last lines above show. The framework’s spans inherit the session like any other span. -
Choose what content leaves your process. Pydantic AI’s
InstrumentationSettings(for exampleinclude_content) decides whether its spans carry prompts, completions and tool arguments. Rius’s own content controls apply on top. SettingRIUS_CAPTURE_CONTENTtofalsestrips prompt and response content at export. -
Run one request that calls a tool and open the trace in the console. You should see the
invoke_agentroot,chatgenerations with model, tokens and cost, andexecute_toolspans.
The full recipe is on the Pydantic AI integration page. The Integrations page covers install and enabling for every other library and framework.
What you get
- The run structure. One root span per agent run, with a child for each model request and each tool call, in the order they happened.
- Model and tokens on every generation. Each
chatspan shows the model and its token counts, including reasoning tokens. - Cost per call. Rius prices each generation from the model and tokens, with cache reads and writes at their own rates, and rolls it up to the trace.
- Timing. Duration for every span, plus time to first token on the generations.
- Tool visibility. Each tool call has its own span named after the tool, so you can see which tools ran and how long they took.
- Messages and tool arguments, when you allow them. They appear only if Pydantic AI’s content settings and Rius’s content controls both let them through.
- Sessions. Runs scoped with
rius.session()are grouped as one conversation.
Known limits, checked against pydantic-ai-slim 2.36.0:
- Successful spans arrive with an unset status rather than OK. The trace status still reads Ok.
- What the spans contain is controlled first by Pydantic AI. If it does not put content on a span, Rius has nothing to show.
- There is no TypeScript integration for Pydantic AI.
When something else fits
- If your agent runs on a different framework or calls a model library directly, the Integrations page lists which integration covers it.
- If you use Pydantic AI without the Rius SDK, you can point any OpenTelemetry setup at the Rius ingest endpoint instead. You then wire the exporter yourself, and you do not get sessions, heartbeats or the SDK’s privacy controls automatically.