Skip to Content

How do I set the span kind attribute on an OpenTelemetry span in Python?

Rius reads the span kind from the openinference.span.kind attribute, which you set in Python with span.set_attribute("openinference.span.kind", "LLM"). The accepted values are AGENT, LLM, TOOL, RETRIEVER, EMBEDDING and CHAIN. If you set gen_ai.operation.name instead, Rius derives the kind from it, so either attribute is enough. Rius then uses the kind to classify the span in trace views and per-kind analytics.

How it works

Rius has two attributes that describe what a span is:

  • openinference.span.kind takes AGENT, LLM, TOOL, RETRIEVER, EMBEDDING or CHAIN.
  • gen_ai.operation.name takes the OpenTelemetry GenAI operation names, such as chat, execute_tool, invoke_agent, embeddings and retrieval.

Set either one and Rius derives the other. chat, text_completion and generate_content are LLM. invoke_agent and create_agent are AGENT. execute_tool is TOOL, embeddings is EMBEDDING and retrieval is RETRIEVER. invoke_workflow and plan are CHAIN. A CHAIN span gets no operation back, because the GenAI conventions define none for a generic step. Setting both yourself is the clearest option.

In Python you set the attribute with the standard OpenTelemetry API, on a span you start from a tracer. The span only reaches Rius if a tracer provider with an OTLP exporter is registered, as the next section shows. Reranking steps use the RERANKER kind with the rerank operation, but Rius has no reranker category yet. A reranking span shows as LLM when it carries gen_ai.request.model, and as Other when it does not.

The full list of keys Rius reads is in the span attribute reference.

Set it up

  1. Register a tracer provider with the http/protobuf exporter, once at startup. The vanilla OpenTelemetry SDKs page covers this setup in full, including other languages. Install the packages:

    pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http

    Then register the provider:

    from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # With no arguments, the exporter reads the OTEL_EXPORTER_OTLP_* variables provider = TracerProvider() provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) trace.set_tracer_provider(provider)
  2. Point the exporter at Rius. The OTEL_EXPORTER_OTLP_* variables only configure that provider and send nothing on their own:

    export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.eu.console.rius-glassflow.com" export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer <your API key>"
  3. Start a span and set the kind. Name the span the way the GenAI conventions ask, the operation followed by what it acted on, such as chat gpt-4o. Set the model before the call and the token counts after it:

    from opentelemetry import trace tracer = trace.get_tracer("my-agent") with tracer.start_as_current_span("chat gpt-4o") as span: span.set_attribute("openinference.span.kind", "LLM") span.set_attribute("gen_ai.operation.name", "chat") span.set_attribute("gen_ai.request.model", "gpt-4o") response = call_the_model() # your own model call span.set_attribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens) span.set_attribute("gen_ai.usage.output_tokens", response.usage.completion_tokens)
  4. For a tool, use the TOOL kind with the execute_tool operation, and set gen_ai.tool.name to the tool’s own name, not the span name. For an agent run, use AGENT with invoke_agent and set gen_ai.agent.name.

  5. Wrap each agent run in one root span. Nest the others under it the normal OpenTelemetry way and they arrive as one trace.

Do not send a cost attribute. Rius computes cost from the model name and the token counts.

What you get

  • Each span is classified by kind in trace views and per-kind analytics.
  • A span whose kind is LLM, set directly or derived from chat, counts as an LLM call in the console and in alerts. A span with neither a kind nor an operation also counts when it carries gen_ai.request.model.
  • Agent, tool, retrieval and embedding spans are not counted as LLM calls, even when they carry a model. Their cost still appears in the per-model breakdown and the cost totals.
  • Tool spans carry the TOOL kind, which is what the Tool Loop alert counts. A tool span with kind CHAIN is never counted.
  • A span with the model and token counts gets a cost computed by Rius and appears in model analytics. Costs are list-price estimates in US dollars.

For how the kind sits alongside the other conventions Rius reads, see Trace LLM calls with OpenTelemetry.

When something else fits

If your spans come from OpenInference or OpenLLMetry instrumentation, you do not need to set the kind by hand, because Rius maps their kinds onto the same taxonomy at ingest. The Rius SDKs also add conveniences on top of raw OpenTelemetry, such as auto-instrumentation integrations, and code written against raw OpenTelemetry keeps working if you adopt one later.

Still stuck? Get help

Last updated on