Skip to Content

How do I attribute the OpenAI Agents SDK traces to end users?

Rius attributes OpenAI Agents SDK traces to end users when you wrap each request in rius.user(...) with your own user ID. Every span started inside that scope carries user.id, including the model calls the framework makes through the OpenAI client. The Users page then shows each user’s traces, failures, cost and time to first token. Start from the Monitor AI agents in production guide if you are new to Rius.

How it works

A user is the person a request served, identified by an ID your application already has. The Rius SDK stamps that ID as user.id on every span started inside a user scope, and Rius reads it to group traces per user.

  • The scope wraps the run. The model calls inside the OpenAI Agents SDK are captured by the OpenAI integration, and spans created by auto-instrumentation carry the user like any other span. Your observe wrappers for the entrypoint and the tools carry it too. See OpenAI Agents SDK for how those wrappers are set up.
  • Open the scope outside the entrypoint. observe takes no user argument, because a decorator is bound once at import time and a user belongs to each request. Put rius.user(...) around the call to your wrapped function, so the root span carries the user as well.
  • Scopes nest and combine. The innermost user scope wins, it unwinds with the block even on an error, and it works alongside a session scope. See Tracing your code for the full behavior.
  • No default exists. There is no process-wide user and no user environment variable, because that would attribute every request to one person. A span outside any user scope is anonymous.

Set it up

  1. Set up the SDK and wrap your run and tools as described on the OpenAI Agents SDK page. The result is an entrypoint such as handle_ticket, wrapped with observe.
  2. Scope the user around each request. In this example, request.user_id and request.conversation_id are your own names, and the session scope is optional. It groups the turns of one conversation, as the integration page recommends.
import rius def handle_request(request): with rius.user(request.user_id): with rius.session(request.conversation_id): return handle_ticket(request.ticket)
  1. Use an opaque identifier, not an email or a name. The value is stored on every span and shown in the console. If only sensitive identifiers exist, hash them first.
  2. Cover every code path. Any place that calls Runner.run_sync or another run method outside a scope sends anonymous traces.
  3. Verify in one minute. Run one request with a test ID such as test-user-1. Open Users, type test-user-1, click Find, and open the row. Once the trace has finished, the page shows 1 trace, your agent under Agents that served this user, and the trace under Recent traces.

What you get

The Users page lists the end users your agents serve, over a window of 15m, 1h, 1d, 7d (the default) or 30d. It shows each user’s traces, failed traces and cost, the agents that served them, and when they were last seen. You can sort by traces, failed traces or cost. The list shows the top 50 users for the sort, and you reach anyone else by searching their exact user ID.

Opening a user gives you:

  • Tiles: Traces, Failed traces, Error rate, Cost with the average per trace, Sessions, and TTFT p50 over their streaming calls. Cost is a list-price estimate in US dollars that Rius computes from the model and token counts.
  • Agents that served this user: up to three, most first.
  • Recent traces: the 8 newest, each opening the full trace.
  • Failed traces: the 6 latest with at least one errored span, recovered runs included.
  • A summary: when root-cause analysis is on for the workspace, an admin can click Summarise this user for a written account of that user’s experience.

Figures count finished traces only, so a run appears once every span has ended. A line above the list shows how many traces carry an end user, for example 400 of 471. Everything else sits in the anonymous row, which always comes first. If that row is large, some code paths run outside a user scope. A customer who reports a problem can be found by exact ID, and from there you reach the failing span in a few clicks. The trace list also has a User column and a user_id filter.

When something else fits

  • If one process serves a single person, such as a CLI run, a user scope per request is still the only way to set the user, since Rius has no process-wide user default.
  • If your agents run outside a request, such as batch jobs with no end user, the traces stay anonymous. Group them with a session instead, using rius.session().

Still stuck? Get help

Last updated on