Skip to Content
Rius

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

Rius groups the traces of one Anthropic conversation into a session when you wrap each turn in rius.session in Python, or withSession in TypeScript, with your conversation id. The SDK stamps that id as session.id on every span in scope, including the span for each Anthropic call. Each turn stays its own trace, and the session ties the turns together. Your Anthropic calls need no changes.

How it works

A trace is one turn of your chat, from request to answer. A session is the id your application assigns to tie those turns together. You choose the id, so it can be the conversation id you already store.

  • Open the session scope around the turn, not inside it, so the root span of the trace carries session.id too.
  • Anthropic calls are traced by the Anthropic integration once init() has run. Each messages.create call becomes a model-call span, and it picks up the session id from the surrounding scope.
  • Scopes follow OpenTelemetry context. They nest (the innermost wins), unwind when the block ends even on an error, and follow async work.
  • A session does not depend on the process. A conversation that outlives a deploy or moves between replicas is still one session, because the grouping rides on the traces.
  • Wrapping the turn in observe gives you one trace per turn with the Anthropic call nested inside it. See Tracing your code for the details.

Set it up

  1. Install the SDK and the Anthropic instrumentation, then call init() once at startup. The Anthropic integration page covers this in full, including ordering rules for CommonJS apps.
pip install "glassflow-rius[anthropic]" anthropic npm install @glassflow-ai/rius @opentelemetry/api npm install @arizeai/openinference-instrumentation-anthropic
  1. In Python, wrap each turn in rius.session with your conversation id:
import anthropic import rius rius.init(api_key="ri_...", service_name="my-agent") claude = anthropic.Anthropic() # reads ANTHROPIC_API_KEY @rius.observe def handle_turn(message: str) -> str: response = claude.messages.create( model="claude-haiku-4-5-20251001", max_tokens=200, messages=[{"role": "user", "content": message}], ) return response.content[0].text def on_message(conversation_id: str, message: str) -> str: with rius.session(conversation_id): return handle_turn(message) # every span of the turn carries session.id
  1. In TypeScript, use withSession the same way. Name the observed function, or its spans trace as anonymous:
import Anthropic from '@anthropic-ai/sdk' import { init, observe, withSession } from '@glassflow-ai/rius' const rius = init({ apiKey: 'ri_...', serviceName: 'my-agent' }) await rius.ready // auto-instrumentation is attached before the first call const claude = new Anthropic() // reads ANTHROPIC_API_KEY const handleTurn = observe(async function handleTurn(message: string) { const reply = await claude.messages.create({ model: 'claude-haiku-4-5-20251001', max_tokens: 200, messages: [{ role: 'user', content: message }], }) const [block] = reply.content return block.type === 'text' ? block.text : '' }) async function onMessage(conversationId: string, message: string) { return await withSession(conversationId, async () => { return await handleTurn(message) // every span of the turn carries session.id }) }
  1. Pass the same conversation id on every turn. If you have no id yet, call rius.session() with no argument (or withSession with only a callback) to mint a UUID. The scope hands you that id, so you can store it and reuse it on the next turn.

  2. In a short script, await rius.shutdown() before the process exits so queued spans are exported.

What you get

  • One session per conversation. Each turn appears as its own trace, and all of them share the session id you chose.
  • Model, token counts and a cost on each Anthropic call. Cost is a list-price estimate in US dollars, and Rius computes it from the model and tokens.
  • A quick check that grouping works. If an agent’s session count equals its trace count, nothing is setting session.id, and each trace is counted as its own single-trace session.
  • Sessions that survive deploys and replica changes, because they depend on the traces and not on the process.
  • A path to per-person views. Add rius.user in Python or withUser in TypeScript around the same turn to tie several conversations to one person. For the wider picture, see Monitor AI agents in production.

When something else fits

  • If your 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 the RIUS_SESSION_ID environment variable. An active scope overrides it.
  • Do not use a process-wide id in a server that handles many users, because every user’s traces would merge into one session. Scope each request instead.
  • For one-shot calls with no conversation, skip the scope. Each trace is then its own session.

Still stuck? Get help

Last updated on