How do I set the conversation id attribute on an OpenTelemetry span in JavaScript?
Rius reads the OpenTelemetry GenAI key gen_ai.conversation.id as the session when a span has no session.id, so you set it with span.setAttribute from @opentelemetry/api. Set the same value on every span of the conversation, and Rius groups those traces into one session. If a span carries both keys, session.id is used. If it carries neither, the trace is grouped by its own trace id.
How it works
- The convention key is
gen_ai.conversation.id, a string. It is the OpenTelemetry GenAI conventions’ session key. - Rius reads the session per span. It takes
session.idfirst, thengen_ai.conversation.id, then falls back to the trace id, so a trace with no id is its own session. - Because the read is per span, put the id on every span in the trace, not only the root. A child span without it is grouped by its trace id instead.
- The attribute is plain OpenTelemetry. Any JavaScript setup that already exports to Rius, including a vanilla Node.js SDK, carries it unchanged. The Vanilla OpenTelemetry SDKs page covers that setup, and the span attribute reference lists every key Rius reads.
- This is the same wire format described in Trace LLM calls with OpenTelemetry.
Set it up
- Register a tracer provider that exports to Rius. Without one, spans are not sent. In Node.js, shut it down before the process exits so queued spans are flushed. The exporter reads the
OTEL_EXPORTER_OTLP_*variables, which the Vanilla OpenTelemetry SDKs page lists in full.
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
const sdk = new NodeSDK({
// No url or headers in code, so the exporter reads OTEL_EXPORTER_OTLP_*
traceExporter: new OTLPTraceExporter(),
// Rius ingests traces only
metricReaders: [],
logRecordProcessors: [],
})
sdk.start()
// ... your agent code ...
// Flushes queued spans before the process exits (top-level await needs an ES module)
await sdk.shutdown()-
Keep one conversation id for the whole conversation. Use your own stable value, such as the id of a chat thread in your app. Reuse it on every turn, so each turn’s trace lands in the same session.
-
Set
gen_ai.conversation.idon each span you create, next to the attributes that classify it:
import { trace } from '@opentelemetry/api'
const tracer = trace.getTracer('my-agent')
const conversationId = 'conv-8f2a'
const span = tracer.startSpan('chat gpt-4o')
span.setAttributes({
'gen_ai.conversation.id': conversationId,
'openinference.span.kind': 'LLM',
'gen_ai.operation.name': 'chat',
'gen_ai.request.model': 'gpt-4o',
'gen_ai.provider.name': 'openai',
})
const response = await callTheModel()
span.setAttributes({
'gen_ai.usage.input_tokens': response.usage.prompt_tokens,
'gen_ai.usage.output_tokens': response.usage.completion_tokens,
})
span.end()-
Do the same for the root span of the run and for every tool or agent span under it. Rius reads the id on each span, so a missing one on a child span breaks that span out of the session.
-
If you also want the key Rius reads first, add
session.idwith the same value. Use a single value in both keys so the grouping never disagrees.
What you get
- Traces that share a conversation id appear together as one session, so a multi-turn conversation reads as a single unit instead of a set of unrelated traces.
- Spans that follow the GenAI conventions group into sessions without any Rius-specific key.
- Cost still comes from the model and the token counts on each model-call span. Rius computes it, so you never send a cost attribute.
- Any attribute you send that Rius does not map, such as your own keys, is stored as sent and is never an error.
When something else fits
- If you use the Rius TypeScript SDK instead of raw OpenTelemetry, its
withSessionscope stampssession.idon your spans for you, so you do not set the attribute by hand. - If your conversation id already lives in
session.idfrom another convention, keep sending that key. Rius reads it ahead ofgen_ai.conversation.id.
Related
- Span attribute reference
- How do I set the prompt and completion message attribute on an OpenTelemetry span in Python?
- How do I set the prompt and completion message attribute on an OpenTelemetry span in JavaScript?
- How do I set the prompt cache token attribute on an OpenTelemetry span in Python?
Still stuck? Get help