Skip to Content

How do I trace Pydantic AI in Python?

Rius traces Pydantic AI in Python when you install glassflow-rius next to pydantic-ai, call rius.init(), and call Agent.instrument_all(True). Pydantic AI emits its own OpenTelemetry spans for agent runs, model requests and tool calls, so you wrap nothing. Each model request arrives as a generation with tokens and cost, and each tool call arrives as its own span. No extra is needed, so the base SDK is enough.

How it works

This integration works differently from the client-library ones such as OpenAI or Anthropic. No auto-instrumentation extra is involved. Two things connect Pydantic AI to Rius:

  • rius.init() installs the global OpenTelemetry tracer provider and exports spans to Rius.
  • Agent.instrument_all(True) tells Pydantic AI to emit its spans through that provider, for every agent in the process.

The framework provides the run root, the generations and the tool spans itself. Rius reads its OpenTelemetry GenAI attributes natively and computes cost on the server from the model and token counts. You never send a cost value. Costs are list-price estimates in US dollars, not billed amounts.

The SDK requires Python 3.10 or newer. This integration exists for Python only. The Integrations support matrix lists TypeScript as having no Pydantic AI integration.

Set it up

  1. Install the SDK and Pydantic AI together. There is no glassflow-rius[...] extra for this framework:

    pip install glassflow-rius pydantic-ai
  2. Call rius.init() once, as early as possible, then switch on Pydantic AI instrumentation. Set service_name to a constant name, because it is how you find your agent in trace filters and cost breakdowns. The installation page covers every init() argument and its environment variable.

  3. Run an agent. This example has one tool and scopes the run to a session. lookup, ticket and conversation_id stand in for your own code and values:

    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)
  4. Open the trace in the console. The invoke_agent root, the chat generations with model, tokens and cost, and the execute_tool spans should all be there. Use a request that calls a tool, so you see all three kinds of span.

The tool function needs no observe wrapper. Pydantic AI already records the call, so wrapping it would only add a second span. Agent.instrument_all(True) switches instrumentation on for every agent. For per-agent control, use the framework’s own InstrumentationSettings.

One conversation is one session. Scope each run with rius.session() and reuse your conversation id, as the example does. The framework’s spans inherit that scope like any other span.

What you get

Spans are named after your agents, models and tools:

In Pydantic AIIn the trace
An agent runThe root span, named invoke_agent followed by the agent name
A model requestA generation named chat followed by the model, with tokens and cost
A tool callA span named execute_tool followed by the tool name

A run that calls a 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 answers

The first model request decides to call the tool, the tool span records the call, and the second model request writes the answer. You see the duration of each step and the tokens and cost of each model request.

Time to first token shows up for Pydantic AI runs with glassflow-rius 1.0.1 or later, and reasoning tokens with 1.0.2 or later.

Two behaviors are worth knowing:

  • Successful spans arrive with an unset status rather than OK. The trace status still reads Ok.
  • What the spans carry (prompts, completions, tool arguments) is controlled by Pydantic AI’s InstrumentationSettings, for example include_content. The SDK’s own privacy controls apply on top of that at export.

The full recipe is on the Pydantic AI integration page.

When something else fits

  • If your Pydantic AI code runs without the Rius SDK, point any OpenTelemetry setup at the Rius ingest endpoint instead, using the vanilla OpenTelemetry SDK route.
  • If your agent uses another framework or a client library directly, pick its page from the Integrations list.

Still stuck? Get help

Last updated on