What does Rius record when my agent calls the OpenAI Agents SDK?
Rius records each model call your OpenAI Agents SDK agent makes as a generation with the model, tokens, cost and time to first token. The run and your tools appear as spans when you wrap the entrypoint and the tool bodies with observe. Handoffs get no span of their own, and generation spans share generic names. The integration is available in Python only.
How it works
The OpenAI Agents SDK drives the OpenAI client underneath, so the openai instrumentation captures its model calls without framework-specific code. The framework keeps the agent loop, tool executions and handoffs to itself, so those need observe wrappers to show up in the trace.
This is what lands in the trace:
| In the framework | In the trace |
|---|---|
Your entrypoint around Runner.run() | The root AGENT span, from your observe wrapper |
| A model call | An LLM generation with model, tokens, cost and time to first token |
| A function tool | A TOOL span, from your observe wrapper on the function body |
| A handoff | No span of its own |
Rius computes the cost of each generation from the model and the token counts. You never send a cost value. Costs are list-price estimates in US dollars, not billed amounts.
A run that crosses a handoff looks like this:
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
├─ execute_tool account_status TOOL 0ms
└─ Response LLM 4.6s billing agent answersThe framework also has its own tracing, which uploads to the OpenAI platform and is independent of OpenTelemetry. Leaving it on does not interfere with Rius, but it reports to two backends. Turn it off with set_tracing_disabled(True) if Rius should be your only telemetry path.
Set it up
-
Install the Rius SDK with the OpenAI extra, next to the framework. The extra installs the instrumentation, not the library itself. Full installation steps are on the OpenAI Agents SDK page.
pip install "glassflow-rius[openai]" openai-agents -
Call
rius.init, disable the framework’s own tracing if you want a single path, and wrap the entrypoint and each tool body withobserve.import rius from agents import Agent, Runner, function_tool, set_tracing_disabled from rius import SpanKind rius.init(api_key="ri_...", 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) -
Keep the decorator order on tools:
@function_tooloutermost,observeon the function body. The framework reads the function signature for its tool schema, and theTOOLspan parents under the run through ambient context. -
Scope each run with
rius.session(), reusing your conversation or ticket id, so every turn of one conversation groups together. -
Run one request that calls a tool, then open the trace in the console. You should see the root
AGENTspan, theLLMgenerations with model, tokens and cost, and your namedTOOLspans.
What you get
- One generation per model call, with model, tokens, cost and time to first token.
- A named run tree. Your entrypoint is the root
AGENTspan, and each tool you wrapped is aTOOLspan under it. - Grouped conversations when you scope runs with
rius.session().
These limits apply, verified against openai-agents 0.22.0:
- Handoffs produce no span. A handoff shows only as consecutive generations that belong to different agents. If the boundaries matter, add an
observewrapper per agent entry, or use the framework’sRunHooksto open spans at agent boundaries. - Generation spans have generic names. Responses API calls are all named
Response, so the span name does not tell you which agent spoke. - Hyphens in agent names are rewritten in the generated handoff tool name, and the framework warns on every run. Use underscores.
- Streamed runs need usage enabled on the underlying client to carry token counts. Without token counts there is no cost. In the Agents SDK, set
model_settings=ModelSettings(include_usage=True)on the agent. It maps tostream_options={"include_usage": True}on the underlying call. - There is no TypeScript integration for this framework.
When something else fits
- If you need handoff boundaries as spans, add per-agent
observewrappers orRunHooksas described above. - If your agent is in TypeScript, there is no integration for this framework, so trace those calls with manual generations. The Integrations page lists what each language supports.