Skip to Content
Rius

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

Rius groups the traces of one conversation into a session when you wrap each turn in a session scope that carries your conversation id. The SDK stamps that id as session.id on every span started inside the scope, including the spans of your OpenAI calls. Each turn stays its own trace, and the session ties the turns together in the console. The scope works the same way in Python (rius.session) and TypeScript (withSession).

How it works

A trace is one turn of work, and a session is the id you assign to correlate several turns. You choose the id, usually the conversation id you already store, and the SDK writes it as session.id on every span in scope. Details are in Tracing your code and Concepts.

  • The OpenAI integration turns each chat completion into a span, as described on the OpenAI integration page. A span created inside the scope carries the session id like any other span.
  • Wrap the OpenAI call in observe inside the scope. The call then nests in your turn’s trace. Without observe, each call shows up as a standalone trace, still in the same session.
  • Scopes follow OpenTelemetry context. They nest, and the innermost one wins. They unwind with the block even on an error, and they follow async work.
  • The grouping rides on traces, not on the process. A conversation that outlives a deploy or moves between replicas stays one session.

Set it up

  1. Install the OpenAI instrumentation and call init() before you create the client. The OpenAI integration page has the full setup.
pip install "glassflow-rius[openai]" openai npm install @arizeai/openinference-instrumentation-openai
  1. In Python, scope the session around each turn. Pass your own conversation id, the same value on every turn of that conversation.
import rius from openai import OpenAI rius.init(api_key="ri_...", service_name="my-agent") client = OpenAI() @rius.observe def handle_turn(messages): response = client.chat.completions.create(model="gpt-4o-mini", messages=messages) return response.choices[0].message.content def reply(conversation_id, messages): with rius.session(conversation_id): return handle_turn(messages)
  1. In TypeScript, use withSession with the same idea. Name the wrapped function, because an anonymous one traces as anonymous.
import { init, observe, withSession } from '@glassflow-ai/rius' import OpenAI from 'openai' init({ apiKey: 'ri_...', serviceName: 'my-agent' }) const client = new OpenAI() const handleTurn = observe(async function handleTurn(messages: OpenAI.Chat.ChatCompletionMessageParam[]) { const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages }) return response.choices[0]?.message.content }) async function reply(conversationId: string, messages: OpenAI.Chat.ChatCompletionMessageParam[]) { return await withSession(conversationId, async () => await handleTurn(messages)) }
  1. If you have no id of your own, call rius.session() with no argument, or withSession with only a callback. Rius mints a fresh UUID for the scope and hands it to you, so you can log it or pass it to the next turn. Reuse that id on later turns, or each turn becomes a new session.

  2. Send two or three turns with the same id, then open the traces in the console to confirm they share a session.

What you get

  • One session per conversation, made of one trace per turn. Each trace holds the spans for your function and the model calls inside it.
  • The model, token counts and a list-price cost estimate on each OpenAI span. Rius computes cost from the model and tokens, so you never send it.
  • A check on whether grouping works. If an agent’s session count equals its trace count, nothing is setting session.id, and each trace is its own single-trace session.
  • A session that survives restarts and replica changes, because the id lives in your application and not in the process.

When something else fits

  • If the agent handles exactly one session per process, such as a CLI run or a batch job, set the id once with init(session_id=...) in Python, init({ sessionId }) in TypeScript, or RIUS_SESSION_ID. An active scope overrides it. Never do this in a server that handles many users, because every user’s traces would merge into one session.
  • To follow one person across conversations rather than one conversation, scope a user with rius.user in Python or withUser in TypeScript. It combines freely with a session scope.
  • For the wider picture of watching agents in production, see Monitor AI agents in production.

Still stuck? Get help

Last updated on