Skip to main content

Span attribute reference

Every attribute the plugin may set, grouped by span type. See Attribute conventions for the narrative version of the dual-convention mapping.

Attributes marked optional are only set when the underlying data is available.

Resource (on every span)

AttributeSource
service.nameOTEL_PROJECT_NAME / project_name (fallback: hermes-agent)
service.versionhermes-otel plugin version
otel.scope.namehermes-otel
openinference.project.nameSame as service.name
telemetry.sdk.*Set by OTel SDK
any resource_attributes.*From config.yaml
any global_tags.*From config.yaml (overridden by resource_attributes on key conflict)

session.* / cron

Set at start:

AttributeTypeMeaning
hermes.session.kindstringcli · telegram · discord · cron · ...
hermes.session.idstringHermes session ID
session.idstringStandard OTel alias
user.idstringHermes user ID (optional)

Set at end (turn summary):

AttributeTypeMeaning
hermes.turn.tool_countintDistinct tool names invoked
hermes.turn.toolsstringSorted CSV of distinct tool names (≤500 chars)
hermes.turn.tool_targetsstring|-joined distinct file paths/URLs
hermes.turn.tool_commandsstring|-joined distinct shell commands
hermes.turn.tool_outcomesstringSorted CSV of distinct outcome statuses
hermes.turn.skill_countintDistinct skills inferred
hermes.turn.skillsstringSorted CSV of distinct skill names
hermes.turn.api_call_countintpre_api_request hooks fired
hermes.turn.final_statusstringcompleted · interrupted · incomplete · timed_out

Empty/zero aggregators are omitted.

llm.*

Span kind: LLM (OpenInference).

AttributeConventionTypeMeaning
llm.model_nameOpenInferencestringModel name
llm.providerOpenInferencestringProvider (anthropic, openai, ...)
gen_ai.request.modelgen_aistringModel name (Langfuse)
gen_ai.systemgen_aistringProvider (Langfuse)
input.valueOpenInferencestringUser message OR full conversation JSON
input.mime_typeOpenInferencestringtext/plain OR application/json
output.valueOpenInferencestringFinal assistant response
output.mime_typeOpenInferencestringtext/plain
gen_ai.content.promptgen_aistringUser message
gen_ai.content.completiongen_aistringAssistant response
hermes.conversation.message_counthermesintWhen conversation capture is on (optional)

api.*

Span kind: LLM (OpenInference).

AttributeConventionTypeMeaning
gen_ai.request.modelgen_aistringModel name
llm.model_nameOpenInferencestringModel name
llm.providerOpenInferencestringProvider
llm.token_count.promptOpenInferenceintPrompt tokens
llm.token_count.completionOpenInferenceintCompletion tokens
llm.token_count.totalOpenInferenceintSum
llm.token_count.cache_readOpenInferenceintCache read (optional)
llm.token_count.cache_writeOpenInferenceintCache write (optional)
llm.token_count.completion_details.reasoningOpenInferenceintReasoning/thinking tokens — a subset of completion (optional)
gen_ai.usage.input_tokensgen_aiintPrompt tokens
gen_ai.usage.output_tokensgen_aiintCompletion tokens
gen_ai.usage.cache_read_input_tokensgen_aiintCache read (optional)
gen_ai.usage.cache_creation_input_tokensgen_aiintCache write (optional)
gen_ai.usage.reasoning.output_tokensgen_aiintReasoning/thinking tokens — a subset of output (optional)
llm.invocation_parametersOpenInferencestring (JSON)Request params
gen_ai.response.finish_reasongen_aistringstop, tool_use, length, etc.
http.duration_mshermesintWall-clock HTTP duration

api.* on failure (api_request_error)

When the request fails, the span ends with StatusCode.ERROR, an exception event (exception.type / exception.message / exception.escaped), and:

AttributeConventionTypeMeaning
error.typeOTelstringError class reported by Hermes (e.g. RateLimitError)
http.response.status_codeOTelintHTTP status (omitted for network errors)
gen_ai.response.status_codegen_aiintSame value, gen_ai spelling
hermes.retry.counthermesintRetries attempted so far for this request
hermes.max_retrieshermesintConfigured retry ceiling
hermes.retryablehermesboolWhether the error is retryable
llm.response.duration_mshermesfloatWall-clock of the failed attempt

The most recent error.type is also stamped on the turn's root agent span at on_session_end.

tool.*

Span kind: TOOL (OpenInference).

AttributeConventionTypeMeaning
tool.nameOpenInferencestringTool name
input.valueOpenInferencestringTool args (JSON)
output.valueOpenInferencestringTool result
hermes.tool.targethermesstringInferred file path / URL (optional)
hermes.tool.commandhermesstringInferred shell command (optional)
hermes.tool.outcomehermesstringcompleted · error · timeout · blocked
hermes.skill.namehermesstringInferred skill name (optional)

subagent.*

Span kind: AGENT (OpenInference). One per delegated child agent; nests under the parent turn, with the child's own root span nested beneath it (or linked, cross-process).

AttributeConventionTypeMeaning
gen_ai.operation.namegen_aistringinvoke_agent
gen_ai.agent.namegen_aistringChild role
hermes.subagent.rolehermesstringChild role
hermes.subagent.goalhermesstringDelegated goal (preview)
hermes.subagent.child_session_idhermesstringChild session ID (join key)
hermes.subagent.parent_session_idhermesstringParent session ID
hermes.subagent.parent_turn_idhermesstringParent turn ID
hermes.subagent.child_idhermesstringChild sub-agent ID (optional)
hermes.subagent.statushermesstringReported child_status (on stop)
hermes.subagent.duration_mshermesfloatChild wall-clock ms (on stop)
hermes.subagent.summaryhermesstringChild result summary (on stop)

The delegated child's own agent root additionally carries hermes.session.is_subagent=true, hermes.subagent.parent_session_id, and hermes.subagent.role.

Metrics (separate from spans)

Emitted via PeriodicExportingMetricReader on backends that support OTLP metrics:

MetricTypeUnitLabels
hermes.tokens.promptCountertokensmodel, provider
hermes.tokens.completionCountertokensmodel, provider
hermes.tokens.totalCountertokensmodel, provider
hermes.tokens.cache_readCountertokensmodel, provider
hermes.tokens.cache_writeCountertokensmodel, provider
hermes.tokens.reasoningCountertokensmodel, provider
hermes.tool.callsCountercounttool_name, outcome
hermes.tool.durationHistogrammstool_name, outcome
hermes.api.durationHistogrammsmodel, provider, finish_reason
hermes.skill.inferredCountercountskill_name, source
hermes.sessionsCountercountkind, final_status
hermes.subagent.countCountercountrole, status
hermes.subagent.durationHistogrammsrole
hermes.api.error.countCountercounterror_type, status_class, retryable, model, provider
hermes.retry.countCountercountmodel, provider
hermes.approval.countCountercountchoice, pattern_key
hermes.approval.durationHistogrammschoice, pattern_key

status_class is bucketed to 2xx/3xx/4xx/5xx/network/other to keep cardinality bounded. hermes.retry.count increments once per retryable failure.

hermes.tokens.reasoning (token_type reasoning) counts the model's thinking tokens. These are a subset of completion/output tokens, not an additive bucket — they are reported separately for visibility but are already included in hermes.tokens.completion and total_tokens, so do not sum reasoning into the total. Only emitted by reasoning-capable models that report a non-zero count.

Label cardinality is bounded by normalised values (outcomes, finish reasons) and small dimension sets (model, tool name). No user IDs or other high-cardinality labels.

OTel GenAI semantic-convention metrics

In addition to the hermes.* instruments above, the plugin emits spec-named metrics so generic OpenTelemetry GenAI dashboards and alert rules work without any per-user wiring. This mirrors the dual-convention span attributes. Set emit_genai_metrics: false (or HERMES_OTEL_EMIT_GENAI_METRICS=false) to emit only the hermes.* metrics.

MetricTypeUnitLabels
gen_ai.client.token.usageHistogram{token}gen_ai.token.type (input/output), gen_ai.operation.name, gen_ai.provider.name, gen_ai.request.model, gen_ai.response.model
gen_ai.client.operation.durationHistogramsgen_ai.operation.name, gen_ai.provider.name, gen_ai.request.model, gen_ai.response.model, error.type (on failures)
gen_ai.agent.token.usageHistogram{token}gen_ai.token.type, gen_ai.operation.name (invoke_agent), gen_ai.provider.name, gen_ai.request.model

Notes:

  • Units follow the spec: durations are in seconds (gen_ai.client.operation.duration), whereas the hermes.* duration histograms stay in ms for backward compatibility.
  • gen_ai.token.type is limited to the spec's input / output enum. Cache and reasoning buckets are subsets already counted in those, so they are not split into the spec metric (the hermes.tokens.* metrics retain that breakdown).
  • Dimensions are deliberately low-cardinality — operation, provider, and model only, never per-call IDs such as session_id — so the metrics stay aggregatable.
  • gen_ai.agent.token.usage is the per-turn/session rollup recorded at session end; gen_ai.client.* are per-API-call.
  • gen_ai.agent.request.duration is intentionally not emitted yet — there is no reliable per-turn duration signal today (a turn can span multiple API calls); it is deferred until true session-lifecycle timing lands.