Skip to Content

How do I group the traces of one conversation into a session with LlamaIndex?

Rius groups the traces of one LlamaIndex conversation into a session when you wrap each turn in rius.session with your conversation id. Every span the turn creates, including the retriever, embedding and model-call spans from LlamaIndex, carries that id as session.id. In the console, the traces of all the turns then read as one session instead of separate single-trace sessions.

How it works

A trace is one run of your agent, so each turn of a chat is its own trace. A session is the id you assign that ties those traces together.

  • rius.session(conversation_id) is a block scope in the Python SDK. Every span started inside the block gets session.id, and that includes the spans the LlamaIndex integration creates for query engines, retrievers, embeddings and synthesis calls.
  • Scopes ride OpenTelemetry context, so they nest (the innermost wins), end with the block even on an error, and follow async work the same way the active span does.
  • Because the grouping rides on traces and not on the process, a conversation that outlives a deploy or moves between replicas stays one session.
  • Calling rius.session() with no argument mints a fresh UUID for the scope. The block yields the id, so you can log it or hand it to the next turn.

The LlamaIndex integration is Python only, so this is the Python form. Tracing your code covers sessions in full.

Set it up

  1. Install the SDK with the LlamaIndex extra. The extra installs the instrumentation, not llama-index itself. The LlamaIndex integration page has the full setup.

    pip install "glassflow-rius[llama-index]" llama-index
  2. Call rius.init once at startup, then wrap each turn of the conversation in rius.session. Pass an id you already have, such as a conversation id or a job id, so the session correlates with your own records. The names handle_turn and query_engine are your own code.

    import rius rius.init(api_key="ri_...", service_name="my-agent") # build indexes and query engines exactly as before def handle_turn(conversation_id: str, message: str) -> str: with rius.session(conversation_id): return str(query_engine.query(message))
  3. Use the same id for every turn of the same conversation. A new id for each turn gives you one session per turn, which is no grouping at all.

  4. Scope the session around each request in a server that handles many users. Do not set a process-wide default there, because every user’s traces would merge into one session.

  5. Send two turns with the same id, then open the traces in the console and check that they appear under one session. The Monitor AI agents in production guide shows where to go from there.

What you get

  • Each turn stays its own trace, with the nested spans of the query: retrieval steps as RETRIEVER spans, embedding calls as EMBEDDING spans, and synthesis model calls as LLM spans with model, tokens and cost.
  • All the traces that share your conversation id are grouped as one session, so you read a whole conversation across turns instead of hunting for related traces one by one.
  • The session id is your own id, so you can match what you see in Rius to a conversation or job in your application.
  • A check on whether it works: if an agent’s session count equals its trace count, nothing is setting session.id, and each trace is only its own single-trace session.

When something else fits

If your process handles exactly one session, such as a CLI run or a batch job, set the id once with init(session_id=...) or the RIUS_SESSION_ID environment variable instead of scoping every call. An active rius.session scope overrides that default. LlamaIndex.TS has no Rius integration, so there the TypeScript withSession scope groups only the spans you create yourself and the provider calls traced through the OpenAI integration.

Still stuck? Get help

Last updated on