Skip to Content
RiusSDKIntegrationsOpenAI Agents SDK

OpenAI Agents SDK

Trace agents built with the OpenAI Agents SDK  with Rius: every model call the framework makes arrives as a generation with tokens and cost, and a thin layer of observe wrappers gives the run and its tools their shape. This guide covers the Python SDK; the runnable example behind it lives in agent-observability-examples .

How coverage works

The framework drives the OpenAI client underneath, so the openai integration captures its model calls without any framework-specific code. Two things the framework keeps to itself need one line each:

  • The run structure. The agent loop, tool executions, and handoffs run inside the framework; auto-instrumentation only sees the client calls that come out of it. Wrapping the entrypoint and the tool bodies with observe puts that structure in the trace.
  • The built-in tracing. The framework ships its own tracing, which uploads to the OpenAI platform and is independent of OpenTelemetry. Disable it with set_tracing_disabled(True) if Rius should be the single telemetry path; leaving it on double-reports to two backends but does not interfere with Rius.

Setup

pip install "glassflow-rius[openai]" openai-agents
import rius from agents import Agent, Runner, function_tool, set_tracing_disabled from rius import SpanKind rius.init(api_key="gf_...", service_name="my-agent") set_tracing_disabled(True) @function_tool @rius.observe(kind=SpanKind.TOOL) def account_status(email: str) -> str: """Fetch a customer's account standing by email.""" return lookup(email) support_agent = Agent( name="support_agent", instructions="Answer support questions; check accounts with the tool.", tools=[account_status], model="gpt-5-mini", ) @rius.observe(name="handle-ticket", kind=SpanKind.AGENT) def handle_ticket(ticket: str) -> str: result = Runner.run_sync(support_agent, ticket) return str(result.final_output)

The decorator order on tools matters: @function_tool outermost, observe on the function body. The framework reads the function signature for its tool schema, and the TOOL span parents under the run through ambient context.

What lands where

In the frameworkIn the trace
Your entrypoint around Runner.run()The root AGENT span (your observe wrapper)
A model callAn LLM generation with model, tokens, cost, TTFT
A function toolA TOOL span (your observe wrapper on the body)
A handoffNo span of its own; see blind spots

A run that crosses a handoff looks like this (real trace from the example, openai-agents 0.22.0):

handle-ticket AGENT 9.8s ├─ Response LLM 2.3s triage decides to hand off ├─ Response LLM 2.8s billing agent decides to call the tool ├─ account_status TOOL 0ms └─ Response LLM 4.6s billing agent answers

Blind spots

Verified against openai-agents 0.22.0:

  • Handoffs produce no span. A handoff is visible only as consecutive generations belonging to different agents. If handoff boundaries matter to you, add an observe wrapper per agent entry or use the framework’s RunHooks to open spans at agent boundaries.
  • Generation span names are generic. Responses API calls are all named Response; which agent spoke is not visible from the span name.
  • Hyphens in agent names get rewritten in the generated handoff tool name and the framework warns on every run; use underscores.

Sessions

One conversation is one session: scope each run with rius.session(), reusing your conversation or ticket id, so every turn on the same conversation groups together.

Verify

Run one request that calls a tool, then open the trace in the console: the root AGENT span, the LLM generations with model, tokens, and cost, and your named TOOL spans should all be there.

Streamed runs need usage enabled on the underlying client to carry token counts; see the OpenAI integration for the include_usage note that applies here too.

Last updated on