How do I group spans into sessions with OpenInference instrumentors?
Rius groups OpenInference spans into sessions by the session.id attribute, which you set on every span of a conversation or run. The standard OpenInference wiring for Rius does not set it, so until you add it each trace counts as its own session. Once every span carries the same session.id, Rius groups those traces into one session.
How it works
- Rius reads
session.idexactly as sent on OpenInference spans. It does not rename or rewrite it. - Rius reads it per span, so set it on every span of the trace, not only the root. A span without it is not grouped with the rest.
- If a span has no
session.id, Rius usesgen_ai.conversation.id, the OpenTelemetry GenAI conventions’ session key. If neither is present, the span is grouped by its trace id, so each trace is its own session. - The instrumentor code on the OpenInference page only builds the tracer provider and the exporter. It sets no session value, so the session is yours to add.
- The Rius SDKs ship the same OpenInference instrumentors as integrations. They also offer session scopes that stamp the attribute for you.
Set it up
-
Choose a session id: any string that stays the same for one conversation or agent run and differs between them. Use an opaque value, such as a generated id, not personal data.
-
Send your OpenInference spans to Rius. This is the wiring from the OpenInference page, shown here for OpenAI:
from opentelemetry.sdk.trace import SpanLimits, TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from openinference.instrumentation.openai import OpenAIInstrumentor
# Raise the attribute limit: see "Raise the span attribute limit" below.
provider = TracerProvider(span_limits=SpanLimits(max_span_attributes=4096))
provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
endpoint="https://ingest.eu.console.rius-glassflow.com/v1/traces",
headers={"Authorization": "Bearer <your API key>"},
)
)
)
OpenAIInstrumentor().instrument(tracer_provider=provider)-
Add
session.idto every span of the trace, the root and every LLM or tool span under it, with the same value for the whole conversation. In a plain OpenTelemetry setup like the one above, you do this yourself with your own OpenTelemetry code. Start your own spans from the same provider, withprovider.get_tracer(__name__), or register it first withtrace.set_tracer_provider(provider): a tracer from the unregistered global provider exports nothing. Setting it only on the root span is not enough. If your spans already follow the GenAI conventions, you can setgen_ai.conversation.idinstead, and Rius reads it as the session whensession.idis absent. -
If you use the Rius SDK instead of your own provider, let it stamp the value. In Python,
rius.sessionsetssession.idfor the code inside its scope. In TypeScript,withSessiondoes the same. To give a whole process one session, setRIUS_SESSION_ID. Every trace from that process then lands in the same session, so use it only when one process is one session. Install the SDK with the extra for your library, for example:
pip install "glassflow-rius[openai]" openai- Keep all attributes on each span. OpenInference writes one attribute per message field and per tool field, and OpenTelemetry keeps only 128 attributes per span by default. Beyond that, a span silently loses attributes. The code above raises the limit in the provider. For any language you can set it through the environment instead:
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=4096The Rius SDKs already set 4096. For the full list of attributes Rius reads, including user.id, see the span attribute reference. For the other ways to send traces, see Trace LLM calls with OpenTelemetry.
What you get
- Traces that share a
session.idare grouped as one session, so a multi-turn conversation or a long agent run reads as one unit instead of separate traces. - Model, token and cost analytics stay in place. Rius normalizes the OpenInference model and token attributes into their
gen_ai.*keys at ingest and computes cost on the server, so adding a session id changes grouping only. - A predictable fallback. Spans without a session id are still ingested and shown, each trace on its own.
When something else fits
- If you want each trace to stand alone, set no session value. Every trace is already its own session.
- If you would rather not stamp attributes by hand, run the instrumentors through the Rius SDK and use its session scope, or
RIUS_SESSION_IDwhen one process is one session. - If your instrumentation follows the OpenTelemetry GenAI conventions,
gen_ai.conversation.idalready groups spans into sessions without a separatesession.id.