Skip to Content

How does Rius read OpenInference span kind attributes?

Rius reads the openinference.span.kind attribute as the span’s kind and uses it to classify the span in trace views and per-kind analytics. The attribute is stored as sent, and Rius derives the matching gen_ai.operation.name from it. Kinds that Rius has no category for are handled as described below, so you know what to expect in the console.

How it works

OpenInference instrumentors write one attribute for the kind, openinference.span.kind, a string the OpenInference spec makes required on every span it defines. Rius classifies these values: AGENT, LLM, TOOL, RETRIEVER, EMBEDDING and CHAIN, plus RERANKER. Its registry entry lists them. The spec also defines GUARDRAIL, EVALUATOR, PROMPT and DECISION, which the Rius docs give no category, so do not rely on them for classification.

  • Kind and operation are paired. Set either openinference.span.kind or gen_ai.operation.name and Rius derives 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 to EMBEDDING and retrieval to RETRIEVER. invoke_workflow and plan map to CHAIN.
  • CHAIN gets no operation back. The GenAI conventions define no operation for a generic step, so a CHAIN span has a kind and no gen_ai.operation.name.
  • The key is stored as sent. Rius does not rename openinference.span.kind, so you search and filter on that exact key.
  • RERANKER has no category yet. A reranking span shows as LLM when it carries gen_ai.request.model, and as Other when it does not.
  • LLM calls follow the kind. The console and alerts count a span as an LLM call when its kind is LLM, set directly or derived from an operation. 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 when they carry a model. Their cost still appears in the per-model breakdown and the cost totals.

The other OpenInference attributes are handled separately. The llm.* attributes, such as llm.model_name and llm.token_count.*, are normalized at ingest onto their gen_ai.* keys, which is what makes model analytics and cost work. input.value and output.value are kept as span content. Where a native gen_ai.* key is already present, it wins.

Set it up

  1. Point an OpenInference instrumentor at the Rius ingest endpoint. This example uses the OpenAI instrumentor. The exporter class is the protobuf-over-HTTP one, and the span attribute limit is raised to 4096 because OpenInference writes one attribute per message field and per tool field, and OpenTelemetry keeps only 128 by default.
from opentelemetry import trace 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>"}, ) ) ) trace.set_tracer_provider(provider) OpenAIInstrumentor().instrument(tracer_provider=provider) # ... run your agent ... provider.shutdown() # flush queued spans before the process exits
  1. Make a model call. The instrumentor sets openinference.span.kind to LLM on the span for that call, and Rius classifies it from there.
  2. If you emit spans by hand instead, set either openinference.span.kind or gen_ai.operation.name on each span. Name spans the way the GenAI conventions ask, the operation followed by what it acted on, such as execute_tool get_weather or retrieval product-kb.
  3. When you search or filter, use gen_ai.usage.input_tokens rather than llm.token_count.prompt, because the normalized values are stored under the gen_ai.* keys.

The full wiring is on the OpenInference page. The Span attribute reference has the complete kind and operation table. For every way to send traces, see Trace LLM calls with OpenTelemetry.

What you get

  • Each span is classified by its kind in trace views, so model calls, tool calls, agent steps, retrievals, embeddings and generic chain steps are told apart.
  • Per-kind analytics and kind filters work for OpenInference spans the same way they do for spans from the Rius SDKs.
  • You set one attribute and get both: Rius fills in the matching gen_ai.operation.name, except for CHAIN.
  • Model calls get model analytics, token usage and server-side cost, because the llm.* attributes are normalized onto gen_ai.* keys at ingest. Costs are list-price estimates in US dollars.
  • Reranking spans stay visible. They show as LLM when they carry a request model and as Other when they do not.
  • The Interoperability section lists the other conventions Rius reads, if your fleet mixes senders.

When something else fits

  • If your app is Python, the Rius SDK ships the same OpenInference instrumentors as integrations. It sets both the kind and the operation, and it wires the exporter and the attribute limit for you.
  • If you want a reranking step counted in its own category, note that Rius has no reranker category yet.
  • If your instrumentation follows another convention, such as OpenLLMetry, see the supported-conventions table in the attribute reference for what each one gets.

Still stuck? Get help

Last updated on