How does Rius read OpenTelemetry GenAI span kind attributes?
Rius reads gen_ai.operation.name from the OpenTelemetry GenAI conventions as the span kind and fills in the matching openinference.span.kind, so each span is classified in the console. Set either attribute and Rius derives the other. The result drives how trace views, kind filters and LLM call counts treat the span.
How it works
Rius uses two attributes for the kind of a span:
gen_ai.operation.nameis the OpenTelemetry GenAI operation taxonomy. Values includechat,text_completion,generate_content,embeddings,execute_tool,invoke_agent,create_agent,retrieval,invoke_workflowandplan.openinference.span.kindis the kind Rius classifies on. Values areAGENT,LLM,TOOL,RETRIEVER,EMBEDDINGandCHAIN, plusRERANKER.
When a span carries one of them, Rius derives the other:
| Operation | Kind |
|---|---|
chat, text_completion, generate_content | LLM |
invoke_agent, create_agent | AGENT |
execute_tool | TOOL |
embeddings | EMBEDDING |
retrieval | RETRIEVER |
invoke_workflow, plan | CHAIN |
A CHAIN span gets no operation in return, because the conventions define none for a generic step. The Rius SDKs set both attributes for you.
A few details are worth knowing:
- Reranking. The SDKs emit the
RERANKERkind with thererankoperation. Rius has no reranker category yet, so a reranking span shows asLLMwhen it carriesgen_ai.request.model, and as Other when it does not. - LLM calls. The console and alerts count a span as an LLM call when its kind is
LLM, set directly or derived fromchat,text_completionorgenerate_content. A span with neither a kind nor an operation counts when it carriesgen_ai.request.model. Agent, tool, retrieval and embedding spans are not LLM calls even with a model, but their cost still appears in the per-model breakdown and the cost totals. - Other conventions. OpenLLMetry and Traceloop kinds, and Laminar span types, are normalized at ingest onto the same taxonomy. The values move to their canonical keys and the source keys Rius mapped are removed. Native
gen_ai.*keys always win, and normalization only fills gaps.
Set it up
- Set
gen_ai.operation.nameon every span that has a GenAI operation, oropeninference.span.kindif your instrumentation already uses it. You do not need both. - Name each span the way the GenAI conventions ask, the operation followed by what it acted on:
chat gpt-4o,execute_tool get_weather,invoke_agent planner,retrieval product-kb. Rius does not require it, but the SDKs emit this form, so mixed fleets read alike. - Add the attributes that belong to the kind:
- Tool spans:
gen_ai.tool.name(the tool’s own name, never the span name),gen_ai.tool.call.idandgen_ai.tool.type. - Agent spans:
gen_ai.agent.name. - Retrieval spans:
gen_ai.data_source.id. - Model calls:
gen_ai.request.modeland thegen_ai.usage.*token counts. Rius computes cost from these, so never send a cost attribute.
- Tool spans:
- Send the spans as OTLP over HTTP to the Rius ingest endpoint with your API key:
POST https://ingest.eu.console.rius-glassflow.com/v1/traces
Authorization: Bearer <your API key>
Content-Type: application/x-protobufThe span attribute reference lists every attribute and what Rius does with it. The interoperability overview links the setup guide for each sender, and Trace LLM calls with OpenTelemetry compares the sender paths on one page.
What you get
- Classified spans. Trace views and per-kind analytics group each span as an agent, model call, tool call, retrieval, embedding or generic step.
- Kind filters. The Spans tab filters by kind with the All kinds menu. A tool you emit with
execute_toolis aTOOLspan, which is the kind the Tool Loop alert counts. - Consistent LLM call counts. Spans from any sender are counted as LLM calls by the same rule, so counts match across a mixed fleet.
- One attribute is enough. Because Rius derives the other attribute, you can follow the GenAI conventions with only
gen_ai.operation.nameand still get the taxonomy. - Readable content. Spans that carry
gen_ai.input.messagesandgen_ai.output.messagesin the GenAI message shape render their prompt and completion on the span’s Input / Output tab. Spans without them show “Content not captured”, and their timings, tokens and cost are unaffected.
When something else fits
- If your app already emits OpenInference or OpenLLMetry spans, send them as they are. Rius normalizes them at ingest, so you do not need to rewrite attributes.
- If you use Python or TypeScript, the Rius SDKs set both the operation and the kind for you, so there is nothing to map by hand.