Skip to Content

How do I trace an agent built with Pydantic AI and deployed to AWS Lambda?

Rius traces a Pydantic AI agent on AWS Lambda when you call rius.init and Agent.instrument_all(True) once at module level and call client.flush() at the end of every invocation. Pydantic AI emits its own spans, so the agent run, each model request and each tool call arrive with no wrappers. The root span is named after your agent, and service_name sets the name Rius groups it under.

How it works

Pydantic AI is OpenTelemetry-native. rius.init() installs the global OpenTelemetry tracer provider, and Agent.instrument_all(True) tells Pydantic AI to emit its spans through it. No auto-instrumentation extra is involved. Rius reads the framework’s GenAI attributes and computes cost on the server.

Spans leave your function from a background thread, in batches. Lambda freezes the process between invocations, so a frozen thread exports nothing and the exit hook may never run. That is why Lambda needs an explicit flush at the end of each invocation. The details are on Reliability.

Three choices matter for this runtime:

  • Where to call init. Call rius.init and Agent.instrument_all(True) once, at module level, outside your handler. Both are global, so they do not belong inside the per-request path.
  • What to flush. Call client.flush() in a finally block so spans are exported even when the run raises.
  • What to name the agent. Set service_name. Rius groups the agent under it, or under RIUS_AGENT_NAME if you set that variable. The root span is invoke_agent followed by your agent’s name, model requests are chat followed by the model, and tool calls are execute_tool followed by the tool name.

Set it up

  1. Add the SDK and the framework to your function’s dependencies. No extra is needed.
pip install glassflow-rius pydantic-ai
  1. Initialize at module level, turn off the heartbeat, and flush inside the handler. The event keys below are your own. Replace ri_... with your API key, or set RIUS_API_KEY instead.
import rius from pydantic_ai import Agent client = rius.init( api_key="ri_...", service_name="support-agent", heartbeat=False, ) 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) def handler(event, context): try: with rius.session(event["conversation_id"]): support_agent.run_sync(event["ticket"]) return {"statusCode": 200} finally: client.flush(timeout_millis=30_000)
  1. Keep heartbeat=False. Heartbeats are a signal for long-lived processes. On Lambda the runtime suspends the process between invocations, so the instance would flap between alive and stale or gone. You can also set RIUS_HEARTBEAT=false.

  2. Keep the flush at the invocation boundary and out of any inner loop. flush() blocks until the export completes. It returns False on timeout or when the latest export was rejected, for example a 401 on a bad key, so you can log that case.

  3. Scope each run with rius.session() and your conversation id. The framework’s spans inherit the session.

  4. Invoke the function with a request that calls a tool, then open the trace in the console. You should see the invoke_agent root, chat generations with model, tokens and cost, and execute_tool spans. See the Pydantic AI integration page for the full recipe and quirks.

What you get

  • A trace for each invocation, with the agent run as the root and model requests and tool calls underneath it, named after your agent, model and tools.
  • Tokens and cost for each model request. Costs are list-price estimates in US dollars, computed by Rius from the spans.
  • Time to first token with glassflow-rius 1.0.1 or later, and reasoning tokens with 1.0.2 or later.
  • Invocations grouped by conversation through rius.session().
  • The agent listed under your service_name, with no registration step.

For other frameworks and client libraries, see Integrations.

When something else fits

  • If your Lambda runs Pydantic AI without the Rius SDK, you can point any OpenTelemetry setup at the Rius ingest endpoint instead, but then you wire the exporter and the flush yourself.
  • If you need per-agent control over what the spans carry, such as prompt and completion content, set it in Pydantic AI’s InstrumentationSettings, on top of the SDK’s privacy controls.

Still stuck? Get help

Last updated on