Skip to Content

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.name is the OpenTelemetry GenAI operation taxonomy. Values include chat, text_completion, generate_content, embeddings, execute_tool, invoke_agent, create_agent, retrieval, invoke_workflow and plan.
  • openinference.span.kind is the kind Rius classifies on. Values are AGENT, LLM, TOOL, RETRIEVER, EMBEDDING and CHAIN, plus RERANKER.

When a span carries one of them, Rius derives the other:

OperationKind
chat, text_completion, generate_contentLLM
invoke_agent, create_agentAGENT
execute_toolTOOL
embeddingsEMBEDDING
retrievalRETRIEVER
invoke_workflow, planCHAIN

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 RERANKER kind with the rerank operation. Rius has no reranker category yet, so a reranking span shows as LLM when it carries gen_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 from chat, text_completion or generate_content. A span with neither a kind nor an operation counts when it carries gen_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

  1. Set gen_ai.operation.name on every span that has a GenAI operation, or openinference.span.kind if your instrumentation already uses it. You do not need both.
  2. 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.
  3. 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.id and gen_ai.tool.type.
    • Agent spans: gen_ai.agent.name.
    • Retrieval spans: gen_ai.data_source.id.
    • Model calls: gen_ai.request.model and the gen_ai.usage.* token counts. Rius computes cost from these, so never send a cost attribute.
  4. 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-protobuf

The 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_tool is a TOOL span, 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.name and still get the taxonomy.
  • Readable content. Spans that carry gen_ai.input.messages and gen_ai.output.messages in 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.

Still stuck? Get help

Last updated on