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
observeinside the scope. The call then nests in your turn’s trace. Withoutobserve, 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
- 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- 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)- In TypeScript, use
withSessionwith 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))
}-
If you have no id of your own, call
rius.session()with no argument, orwithSessionwith 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. -
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, orRIUS_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.userin Python orwithUserin TypeScript. It combines freely with a session scope. - For the wider picture of watching agents in production, see Monitor AI agents in production.