Skip to Content
DocsTracesSemantic Conventions

Semantic Conventions

Rhesis uses a semantic layer for consistent, framework-agnostic span naming across all AI operations.

Semantic conventions answer a specific question: what actually happened inside this span? Rather than naming spans after framework constructs like chains, pipelines, or agents, Rhesis names spans after the primitive operation they perform — an LLM call, a tool execution, a retrieval. This makes traces readable regardless of which framework or orchestration layer produced them, and lets Rhesis correctly interpret, visualize, and evaluate spans across different stacks.

The same span from LangChain, LlamaIndex, or a custom implementation looks identical in the trace viewer as long as it follows the convention.

Naming Pattern

All span names follow the pattern: ai.<domain>.<action>

Valid Span Names

Primitive Operations

Span NameConstantDescription
ai.llm.invokeAIOperationType.LLM_INVOKELLM API call
ai.tool.invokeAIOperationType.TOOL_INVOKETool/function execution
ai.retrievalAIOperationType.RETRIEVALInformation retrieval
ai.embedding.generateAIOperationType.EMBEDDING_GENERATEGenerate embeddings
ai.rerankAIOperationType.RERANKReranking operation
ai.evaluationAIOperationType.EVALUATIONEvaluation operation
ai.guardrailAIOperationType.GUARDRAILSafety check
ai.transformAIOperationType.TRANSFORMData transformation

Agent Operations

Span NameConstantDescription
ai.agent.invokeAIOperationType.AGENT_INVOKEAgent execution
ai.agent.handoffAIOperationType.AGENT_HANDOFFTransition between agents

Using Constants

app.py
from rhesis.telemetry.schemas import AIOperationType

# Primitive operations
AIOperationType.LLM_INVOKE          # "ai.llm.invoke"
AIOperationType.TOOL_INVOKE         # "ai.tool.invoke"
AIOperationType.RETRIEVAL           # "ai.retrieval"
AIOperationType.EMBEDDING_GENERATE  # "ai.embedding.generate"
AIOperationType.RERANK              # "ai.rerank"
AIOperationType.EVALUATION          # "ai.evaluation"
AIOperationType.GUARDRAIL           # "ai.guardrail"
AIOperationType.TRANSFORM           # "ai.transform"

# Agent operations
AIOperationType.AGENT_INVOKE        # "ai.agent.invoke"
AIOperationType.AGENT_HANDOFF       # "ai.agent.handoff"

Forbidden Span Names

Framework composition concepts are rejected with HTTP 422:

Invalid NameReason
ai.chain.execute”Chain” is an orchestration pattern
ai.workflow.start”Workflow” is a composition
ai.pipeline.process”Pipeline” is infrastructure

Note: ai.agent.* spans are valid — see Agent Operations above.

Attribute Constants

Use AIAttributes for span attributes:

app.py
from rhesis.telemetry.attributes import AIAttributes

span.set_attribute(AIAttributes.MODEL_PROVIDER, "openai")
span.set_attribute(AIAttributes.MODEL_NAME, "gpt-4")
span.set_attribute(AIAttributes.LLM_TOKENS_INPUT, 150)
span.set_attribute(AIAttributes.LLM_TOKENS_OUTPUT, 200)

Model Attributes

ConstantKeyDescription
MODEL_PROVIDERai.model.providerProvider (openai, anthropic)
MODEL_NAMEai.model.nameModel identifier (gpt-4)

LLM Attributes

ConstantKeyDescription
LLM_TOKENS_INPUTai.llm.tokens.inputInput token count
LLM_TOKENS_OUTPUTai.llm.tokens.outputOutput token count
LLM_TOKENS_TOTALai.llm.tokens.totalTotal token count, including any cached tokens
LLM_TOKENS_CACHE_WRITEai.llm.tokens.cache_writePrompt tokens written to the provider’s cache
LLM_TOKENS_CACHE_READai.llm.tokens.cache_readPrompt tokens served from the provider’s cache
LLM_TEMPERATUREai.llm.temperatureTemperature parameter
LLM_MAX_TOKENSai.llm.max_tokensMax tokens parameter

Tool Attributes

ConstantKeyDescription
TOOL_NAMEai.tool.nameName of the tool
TOOL_TYPEai.tool.typeType (http, function, database)

Retrieval Attributes

ConstantKeyDescription
RETRIEVAL_BACKENDai.retrieval.backendBackend (pinecone, weaviate)
RETRIEVAL_TOP_Kai.retrieval.top_kNumber of results

Embedding Attributes

ConstantKeyDescription
EMBEDDING_MODELai.embedding.modelModel name
EMBEDDING_VECTOR_SIZEai.embedding.vector.sizeVector dimensions

Agent Attributes

ConstantKeyDescription
AGENT_NAMEai.agent.nameName of the agent
AGENT_HANDOFF_FROMai.agent.handoff.fromAgent initiating the handoff
AGENT_HANDOFF_TOai.agent.handoff.toAgent receiving the handoff
AGENT_INPUT_CONTENTai.agent.inputAgent input
AGENT_OUTPUT_CONTENTai.agent.outputAgent output

Operation Type Values

ConstantValueDescription
OPERATION_LLM_INVOKEllm.invokeLLM operation
OPERATION_TOOL_INVOKEtool.invokeTool operation
OPERATION_RETRIEVALretrievalRetrieval operation
OPERATION_EMBEDDING_CREATEembedding.createEmbedding operation
OPERATION_RERANKrerankRerank operation
OPERATION_EVALUATIONevaluationEvaluation operation
OPERATION_GUARDRAILguardrailGuardrail operation
OPERATION_TRANSFORMtransformTransform operation
OPERATION_AGENT_INVOKEagent.invokeAgent invocation
OPERATION_AGENT_HANDOFFagent.handoffAgent handoff

Supported Providers

Every provider reports its token counts under different key names. Rhesis reads all of them onto the ai.llm.tokens.* attributes above, so a trace looks the same whichever provider produced it.

ProviderInput keyOutput keyCached prompt tokens
OpenAIprompt_tokenscompletion_tokensprompt_tokens_details.cached_tokens
Anthropicinput_tokensoutput_tokenscache_creation_input_tokens, cache_read_input_tokens
Googleprompt_token_countcandidates_token_countgen_ai.usage.cache_read.input_tokens
Mistralprompt_tokenscompletion_tokensnot reported
Coherebilled_units.input_tokensbilled_units.output_tokensnot reported

The camelCase spelling of each key is read too, and Cohere’s counts are nested one level down under billed_units, which is the figure its invoice charges. Where a provider reports both a billed and a raw count, the billed one wins.

OpenAI includes cached tokens in prompt_tokens. Rhesis subtracts prompt_tokens_details.cached_tokens from the input count and records it separately, so the total still includes every prompt token and cached calls use the cache-read rate. Anthropic’s input_tokens already excludes cached tokens; its cache counts stay separate and are included when Rhesis derives a total.

LangChain’s usage_metadata, the only usage a streamed LangChain call carries, counts cached tokens inside input_tokens for every provider and reports them under input_token_details.cache_read and input_token_details.cache_creation. Rhesis subtracts both from the input count, so a streamed call records the same counts as the same call made with invoke().

A provider not listed here still works if it uses one of those key names, which most do. One that does not reports no tokens, and the trace then shows No cost data rather than a cost of zero.

Who served the call

ai.model.provider says which provider served a call. An integration sets it when it can place the call, and leaves it unset otherwise so Rhesis can derive it from the model name instead.

IntegrationSets the provider
LangChain, LangGraphFrom the module path, the class name, or the model name
Google ADKDerived from the model id
HaystackDerived from the model name
Microsoft Agent Framework, Pydantic AIOnly where the framework reports gen_ai.provider.name or gen_ai.system
Raw OTLP, anything elseNo, derived from the model name on the way in

Provider names are normalised so one provider appears once, whichever route the call took. See Token Costs for how those names are folded together and how each figure is priced.

Events

Use AIEvents for span events:

app.py
from rhesis.telemetry.attributes import AIEvents, AIAttributes

with tracer.start_as_current_span("ai.llm.invoke") as span:
    # Prompt event
    span.add_event(
        name=AIEvents.PROMPT,
        attributes={
            AIAttributes.PROMPT_ROLE: "user",
            AIAttributes.PROMPT_CONTENT: prompt_text,
        }
    )

    response = llm.invoke(prompt_text)

    # Completion event
    span.add_event(
        name=AIEvents.COMPLETION,
        attributes={
            AIAttributes.COMPLETION_CONTENT: response.text,
        }
    )

Event Names

ConstantValueDescription
AIEvents.PROMPTai.promptPrompt sent to LLM
AIEvents.COMPLETIONai.completionLLM completion
AIEvents.TOOL_INPUTai.tool.inputTool input
AIEvents.TOOL_OUTPUTai.tool.outputTool output
AIEvents.RETRIEVAL_QUERYai.retrieval.queryRetrieval query
AIEvents.RETRIEVAL_RESULTSai.retrieval.resultsRetrieval results
AIEvents.AGENT_INPUTai.agent.inputAgent input
AIEvents.AGENT_OUTPUTai.agent.outputAgent output

Trace Hierarchy

Spans nest inside each other to represent how operations compose at runtime. The examples below show two common shapes — a RAG pipeline and a multi-agent system.

Single-Agent (RAG)

A retrieval-augmented generation flow: the user query is first embedded, the embedding is used to retrieve relevant context, and then a single LLM call synthesizes the final response.

Multi-Agent

An orchestrator agent plans and delegates. When it needs a specialist, it hands off via ai.agent.handoff — the specialist then runs its own set of operations independently.


Next: Learn about auto-instrumentation for zero-config tracing.