Skip to Content

What does the gen_ai.tool.definitions attribute mean in Rius?

In Rius, gen_ai.tool.definitions holds the tool definitions the model was shown, and it sits on the span for the model call. It is a content attribute: Rius stores it so you can inspect a call, and uses it to estimate what share of a call’s input went to tool definitions. OpenInference spans that carry tools under older names are moved onto this key at ingest.

How it works

  • What it holds. The OpenTelemetry GenAI conventions define it as “the list of tool definitions available to the GenAI agent or model”, type any, at Development stability and opt-in. Upstream warns that it may contain sensitive information and, because it can be large, advises against populating non-required properties by default. Its registry entry links the definition. It describes the tools on offer, not the tools that ran.
  • Which spans carry it. Spans for model calls (LLM spans). Spans for tool executions (execute_tool) use other keys: gen_ai.tool.name for the tool’s own name, gen_ai.tool.call.id for the model’s tool call, and gen_ai.tool.call.arguments and gen_ai.tool.call.result for what went in and out.
  • What Rius does with it. Rius stores it with the span for trace inspection. It also uses it for context attribution, which divides a call’s input tokens across the parts of its context. The Rius SDKs send rius.context.sizes, a compact record of the byte size of each tool definition and each message part. Without it, Rius estimates the same breakdown from the captured messages, gen_ai.system_instructions and the tool definitions.
  • Older names it is read from. For OpenInference spans, the tools member of llm.invocation_parameters becomes gen_ai.tool.definitions, and so do the indexed llm.tools.* schemas. Rius moves the values onto the canonical key before storage and removes the source keys it mapped. A native gen_ai.* key always wins, and normalization only fills gaps.
  • Privacy. It is one of the content attributes covered by the SDK’s privacy controls. Content capture is on by default, so the SDKs send it. With RIUS_CAPTURE_CONTENT=false it is stripped at export, and rius.context.sizes survives, because it holds no content.
  • What it does not drive. Cost is computed from gen_ai.request.model and the gen_ai.usage.* token counts, never from this attribute.

Set it up

  1. If you use the Rius SDK, initialize it once. The SDKs apply the same OpenInference mapping in your process before export, so tool schemas from an OpenInference-instrumented call reach Rius under the canonical key.
import rius client = rius.init( api_key="ri_...", service_name="my-agent", )
  1. If you send spans from a vanilla OpenTelemetry SDK, set gen_ai.tool.definitions on the span for each model call, next to gen_ai.request.model and the gen_ai.usage.* token counts. Do not set it on execute_tool spans.
  2. On the execute_tool span for each tool run, set gen_ai.tool.name, gen_ai.tool.call.id and gen_ai.tool.type, so the run can be matched to the model call that requested it.
  3. If you emit OpenInference attributes yourself, you can leave llm.tools.* or the tools member of llm.invocation_parameters as they are. Rius maps them at ingest.
  4. Search and filter on gen_ai.tool.definitions. A filter on llm.tools.* matches nothing, because the source keys are removed after mapping.
  5. Keep the span attribute limit in mind. Instrumentations that flatten messages and tools into one attribute per field can pass OpenTelemetry’s default of 128 attributes per span, and the span then loses its model, tools, usage or messages. The Rius SDKs raise the limit to 4096.

For the full list of keys and where to send spans, see the span attribute reference. For the wider setup, see Trace LLM calls with OpenTelemetry.

What you get

  • The tools the model was offered on a call, visible when you inspect that span.
  • A context breakdown that counts tool definitions as their own part of a call’s input, from rius.context.sizes when the SDK sent it and from an estimate when it did not.
  • One key to search and filter on, whether the tools arrived as gen_ai.tool.definitions, as the tools member of llm.invocation_parameters, or as llm.tools.*.
  • Tool definitions kept separate from the tool runs, which you read from the execute_tool spans.

When something else fits

  • To see which tool actually ran, read the execute_tool span and its gen_ai.tool.name, not the definitions on the model call.
  • If you want the size of each tool definition without storing the definitions themselves, rely on rius.context.sizes, which survives content stripping.
  • To check how another attribute family is mapped, use the span attribute reference.

Still stuck? Get help

Last updated on