How does Rius read OpenTelemetry GenAI tool call attributes?
Rius reads OpenTelemetry GenAI tool call attributes from execute_tool spans, shows them as tool spans, and moves the arguments and result to input.value and output.value. You set the operation name, the tool name, the call id and the tool type. Rius derives the span kind, groups the span under its agent, and renders the tool’s input and output in the console. The full contract is in the span attribute reference.
How it works
A tool call is a span whose gen_ai.operation.name is execute_tool. Rius reads these attributes on it:
gen_ai.tool.name: the tool’s own name, never the span name.gen_ai.tool.call.id: the id of the model’s tool call this execution answers.gen_ai.tool.type:function,extension,datastoreand so on.gen_ai.agent.name: on anexecute_toolspan, the agent executing the tool.error.type: a low-cardinality error class such astool_error, on a span that ended in an error.gen_ai.tool.call.argumentsandgen_ai.tool.call.result: the arguments the tool received and the result it returned.
Rius normalizes these in a few ways:
- Span kind. Set
gen_ai.operation.nameoropeninference.span.kindand Rius derives the other.execute_toolbecomes theTOOLkind, andTOOLspans are the ones the trace views classify as tools. - Arguments and result. At ingest,
gen_ai.tool.call.argumentsandgen_ai.tool.call.resultmove toinput.valueandoutput.value, so the tool’s input and output render like any other span’s. A nativeinput.valueoroutput.valuewins, and the two source keys are removed either way. Search and filter oninput.valueandoutput.value, not on the source keys. - Native keys win. Normalization fills gaps and never overwrites a
gen_ai.*key you sent. - MCP tools. A tool called over the Model Context Protocol is an
execute_toolspan that also carriesmcp.method.nameset totools/call. That attribute marks it as an MCP tool. A trace’s context attribution reports how many context tokens MCP tools took only when at least one span carries it. Without it the MCP share is unknown, not zero.
The model’s side of a tool call lives on the LLM spans, in the GenAI message shape: the assistant part of type tool_call in the output messages of the call that requested it, and the tool part of type tool_call_response in the input messages of the call after it. The tool definitions the model was shown go in gen_ai.tool.definitions.
Spans from other conventions, such as OpenInference, OpenLLMetry and the Vercel AI SDK, are mapped onto the same keys at ingest. See Trace LLM calls with OpenTelemetry for the sender paths.
Set it up
- Point your OpenTelemetry exporter at Rius with OTLP over HTTP. Use
http/protobuf(recommended) orhttp/json, send tohttps://ingest.eu.console.rius-glassflow.com/v1/traces, and put your API key in anAuthorization: Bearerheader. Setup for each sender is on the interoperability page. - Name the span the way the GenAI conventions ask, the operation followed by what it acted on, for example
execute_tool get_weather, and setgen_ai.operation.nametoexecute_tool. - Set
gen_ai.tool.name,gen_ai.tool.call.idandgen_ai.tool.typeon the span. Setgen_ai.agent.nameto the agent that ran the tool. - Set
gen_ai.tool.call.argumentsandgen_ai.tool.call.result, or setinput.valueandoutput.valuedirectly. Rius ends up with the second pair either way. - For an MCP tool, also set
mcp.method.nametotools/call. - On the LLM span that asked for the tool, put the assistant’s
tool_callpart ingen_ai.output.messages. The tool’s result goes in the input of the next LLM call: itsgen_ai.input.messagesrepeat the history, the assistanttool_calland atoolmessage with atool_call_responsepart, as below:
[
{"role": "user", "parts": [{"type": "text", "content": "What is 2+2?"}]},
{
"role": "assistant",
"parts": [
{"type": "tool_call", "id": "call_1", "name": "calculator", "arguments": "{\"expr\": \"2+2\"}"}
]
},
{
"role": "tool",
"parts": [{"type": "tool_call_response", "id": "call_1", "response": "4"}]
}
]- Open the trace in the console and check the tool span. It should show as a tool, with its input and output on the Input / Output tab.
The tool’s gen_ai.tool.call.id should match the id in the model’s tool_call part, so you can tie the execution to the request that caused it.
What you get
- Tool spans classified as tools in trace views and per-kind analytics, because
execute_toolmaps to theTOOLkind. - The tool’s name, call id, type and agent on the span, so you can tell which tool ran, for which model request, and under which agent.
- The tool’s arguments and result on the span’s Input / Output tab. A span without content attributes shows “Content not captured” there, and its timings are unaffected.
- A tool failure recorded with
error.type, so it appears in error analysis. - Context attribution that separates MCP tools from local tools, when
mcp.method.nameis set. - Model calls that show the tool calls they made, from the
tool_callpart in their output, and the responses they were given, from thetool_call_responsepart in the next call’s input.
Content attributes are stored for trace inspection. If you strip content at export, the structure of the call remains and the arguments and result do not.
When something else fits
If you use the Rius SDK or one of its integrations, it already sets these attributes, so you do not write them by hand. If your senders are OpenInference, OpenLLMetry or the Vercel AI SDK, you can keep their spellings, because Rius maps them onto the same keys at ingest; the interoperability page links each guide. Hand-written attributes fit when you instrument a custom language or runtime yourself.