Skip to Content

Which OpenTelemetry attribute should my spans use for span kind?

Rius reads span kind from gen_ai.operation.name, the OpenTelemetry GenAI convention key, and from openinference.span.kind. Set either one and Rius derives the other, so you do not need both. Rius also reads the span kind names that OpenLLMetry, Laminar and the Vercel AI SDK emit, and moves them onto the standard keys at ingest.

How it works

Rius has two keys that classify a span in trace views and per-kind analytics:

  • gen_ai.operation.name takes the GenAI operation values: chat, text_completion, generate_content, embeddings, execute_tool, invoke_agent, create_agent, retrieval, invoke_workflow, plan and others.
  • openinference.span.kind takes AGENT, LLM, TOOL, RETRIEVER, EMBEDDING or CHAIN.

When you set one, Rius fills in the other:

  • chat, text_completion and generate_content map to LLM.
  • invoke_agent and create_agent map to AGENT.
  • execute_tool maps to TOOL.
  • embeddings maps to EMBEDDING.
  • retrieval maps to RETRIEVER.
  • invoke_workflow and plan map to CHAIN.

A CHAIN span gets no operation in return, because the conventions define none for a generic step. The Rius SDKs set both keys on every span they emit.

The older and third-party names Rius still reads:

  • OpenLLMetry and Traceloop: traceloop.span.kind and the provider span’s llm.request.type. The Traceloop decorator kinds (workflow, task, agent, tool) become the span kind.
  • Laminar: lmnr.span.type, which maps to LLM or TOOL.
  • Vercel AI SDK, versions 5 and 6: ai.operationId.

This happens at ingest. Native gen_ai.* keys always win, and the mapping only fills gaps. For most of these, the source key is removed after it is mapped, so search and filter on the standard key. ai.operationId is one that stays on the span.

Rerank steps are a special case. The SDKs emit RERANKER with the rerank operation, but Rius has no reranker category yet. In trace views and kind filters, such a span shows as LLM when it carries gen_ai.request.model, and as Other when it does not.

The kind also decides what counts as an LLM call in the console and in alerts. A span counts 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 on them. Their cost still appears in the per-model breakdown and the cost totals.

Set it up

  1. Pick the GenAI key. Set gen_ai.operation.name on every span you emit by hand. If your tooling already produces openinference.span.kind, you can keep it, and Rius derives the operation from it.

  2. Name spans 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 this, but the SDKs emit it, so a mix of SDK and hand-written spans reads alike.

  3. Add the attributes that belong with each kind. An LLM span needs gen_ai.request.model and token counts so Rius can compute cost. A tool span needs gen_ai.tool.name, which is the tool’s own name and never the span name. Never send a cost attribute.

  4. Here is an example LLM span, shown as its name and attributes:

name: chat gpt-4o attributes: gen_ai.operation.name: chat openinference.span.kind: LLM gen_ai.provider.name: openai gen_ai.request.model: gpt-4o gen_ai.usage.input_tokens: 812 gen_ai.usage.output_tokens: 96

Rius derives LLM from chat, so openinference.span.kind is optional here. A tool span follows the same pattern:

name: execute_tool get_weather attributes: gen_ai.operation.name: execute_tool gen_ai.tool.name: get_weather gen_ai.tool.type: function
  1. Send the spans as OTLP over HTTP to https://ingest.eu.console.rius-glassflow.com/v1/traces, with your API key in an Authorization: Bearer header. The span attribute reference lists the full contract and the other attributes each kind carries. For the wider OpenTelemetry setup, see Trace LLM calls with OpenTelemetry.

What you get

  • Each span lands in the right category in trace views and per-kind analytics, whether you set the GenAI key or the OpenInference key.
  • Spans from OpenLLMetry, Laminar and the Vercel AI SDK are classified without you renaming their attributes.
  • Inference calls are counted as LLM calls in the console and in alerts, and LLM spans get a computed cost from the model name and token counts.
  • Spans in a mixed fleet, some from the SDKs and some hand-written, read the same because the operation and the kind stay in step.

When something else fits

  • If you use the Rius SDKs, you do not need to set either key yourself, because they set both on every span they emit.
  • If a framework already emits OpenInference, OpenLLMetry, Laminar or Vercel AI SDK spans, send them as they are and let Rius normalize the kind at ingest.

Still stuck? Get help

Last updated on