Skip to Content

How do I attribute LiteLLM traces to end users?

Rius attributes LiteLLM traces to end users when you wrap your litellm.completion calls in rius.user(...), which stamps user.id on every span started inside the block. The Users page then lists each person with their traces, failures, cost and time to first token. Spans created outside a user scope are counted as anonymous.

How it works

  • rius.user(user_id) opens a user scope around one request. Every span started inside it carries user.id, including the spans the LiteLLM integration creates for each routed completion.
  • If the openai extra is installed too, LiteLLM’s call through the OpenAI client is traced as a nested span. That nested span is inside the same scope, so it carries the user as well.
  • Scopes follow async work and nest, and the innermost one wins. They combine freely with a session scope, so you can group a conversation and name its user in the same request.
  • There is no process-wide default and no environment variable for the user, by design. A global user would attribute every request a process handles to one person. A call made outside any scope is anonymous.
  • Rius computes cost on its servers from the model and token counts, so you send only the user id. See Tracing your code for how scopes work in full.

The LiteLLM integration is Python only. From TypeScript, calls to a LiteLLM proxy over its OpenAI-compatible endpoint are traced by the OpenAI integration, and you scope the user there with withUser.

Set it up

  1. Install the integration with pip install "glassflow-rius[litellm]" litellm. The LiteLLM integration page covers setup and what each span captures.
  2. Wrap each request handler in a user scope, passing the identifier your application already has for that person. request.user_id below is your own code, not part of Rius:
import rius import litellm rius.init(api_key="ri_...", service_name="my-agent") def answer(request): with rius.user(request.user_id): return litellm.completion( model="gpt-4o-mini", # or any provider LiteLLM routes to messages=[{"role": "user", "content": request.text}], )
  1. Scope the whole request, not only the LiteLLM call, so retrieval, tool and agent spans around it carry the same user.
  2. Send an opaque identifier rather than 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.
  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 and your agent under Agents that served this user.

What you get

The Users page is a register of the end users your agents serve. It counts only finished traces, so a run appears once every span of it has ended. For each user you get:

  • Traces, Failed traces and Error rate, so you can see who is hitting failures. Failed traces are those whose root span ended in error. Error rate includes runs that recovered.
  • Cost, with the average per trace. It is a list-price estimate in US dollars, computed from the model and tokens of each routed call. Sort the list by cost to find your most expensive users.
  • Sessions, TTFT p50 and the agents that served the user.
  • Recent traces and Failed traces lists, each opening the trace itself. That takes you from a support ticket with a user ID to the failing span in a few clicks.

The list covers 15m, 1h, 1d, 7d (the default) or 30d, shows the top 50 users for the chosen sort, and reaches anyone else by exact user ID. The anonymous row comes first, with a line such as “400 of 471 traces carry an end user”. A large anonymous row means some code paths run outside a user scope. You can also filter the trace list by the user_id field.

TTFT p50 covers streaming calls. A LiteLLM stream that goes through the openai integration records it on its nested OpenAI span. Other LiteLLM streams do not record it, so the tile can be empty for a user whose calls are not streamed that way. For the wider picture, see Monitor AI agents in production.

When something else fits

  • If you run LiteLLM as a proxy server, the proxy is a separate process. Instrument the proxy itself, or send its OpenTelemetry traces by the route the LiteLLM page describes.
  • If your code has no per-request place to name a user, such as a batch job with one fixed identity, a session scope or RIUS_SESSION_ID groups the work instead, and the traces stay anonymous on the Users page.

Still stuck? Get help

Last updated on