Trace Attributes
When trace export is enabled, AIControls emits one span per governed request. This page lists every attribute a span can carry. Empty and zero-valued fields are always omitted.
Span names and kinds
| Request | Span name | Langfuse observation type |
|---|---|---|
| LLM call | chat <model> | generation |
| MCP tool call | execute_tool <tool> | tool |
Outbound HTTP (opt-in via export.requestTypes: [egress]) | HTTP <method> | — |
Span timestamps are reconstructed from the audit record: start = completion time − measured latency. Each span is its own trace; cross-request grouping uses the session.id attribute (Langfuse Sessions) rather than a shared trace.
A span's status is Error only when the upstream call failed (error.type carries the upstream error). A policy denial is a successful governance outcome: the span stays un-errored, with aicontrols.decision: deny and langfuse.observation.level: WARNING.
GenAI semantic conventions
| Attribute | Type | Meaning |
|---|---|---|
gen_ai.operation.name | string | chat (LLM) or execute_tool (MCP) |
gen_ai.request.model | string | Model requested by the caller — differs from response.model when a policy mutated the model |
gen_ai.response.model | string | Model actually served |
gen_ai.system | string | Provider: anthropic, openai, github-copilot, … (heuristic from the model name when recorded before upstream resolution) |
gen_ai.usage.input_tokens | int | Total prompt tokens including cache reads/writes, normalized across providers (Anthropic reports input exclusive of cache and is converted; OpenAI-family counts already include cache) — consumers (e.g. Langfuse) derive non-cached input by subtracting the cache details below |
gen_ai.usage.output_tokens | int | Completion tokens |
gen_ai.usage.details.cache_read_input_tokens | int | Prompt-cache read tokens |
gen_ai.usage.details.cache_creation_input_tokens | int | Prompt-cache write tokens |
gen_ai.usage.details.reasoning_tokens | int | Extended-thinking tokens |
gen_ai.usage.cost | double | Estimated cost in USD from the AIControls cost table |
gen_ai.response.finish_reasons | string[] | Stop reason (end_turn, max_tokens, …) |
gen_ai.response.id | string | Provider request ID — joins to provider billing exports and client-side telemetry |
gen_ai.tool.name | string | MCP tool name (tool spans) |
gen_ai.tool.call.id | string | Anthropic tool_use block ID — links a tool span to the LLM turn that requested it |
gen_ai.prompt | string | Prompt snippet — only when observability.otel.content.capturePrompts: true |
gen_ai.tool.call.arguments | string | Tool argument JSON — only when observability.otel.content.captureToolArguments: true |
Session and user dimensions
| Attribute | Meaning |
|---|---|
session.id / langfuse.session.id | Developer session (falls back to the Claude Code conversation ID) |
user.id / langfuse.user.id | Developer email (falls back to the OIDC subject) |
langfuse.observation.type | generation or tool — makes Langfuse render LLM spans with model/token/cost UI |
langfuse.observation.level | WARNING on deny and require_approval decisions |
AIControls governance attributes
| Attribute | Meaning |
|---|---|
aicontrols.decision | The decision: allow, deny, mutated, audit, … |
aicontrols.policy / aicontrols.policy.rule | Policy and rule that produced the decision |
aicontrols.policy.message | Policy message — set on non-allow decisions |
aicontrols.agent.id / aicontrols.namespace | Calling agent identity |
aicontrols.client.type | AI client: claude-code, codex, copilot, … |
aicontrols.ticket.id | Work-item reference from the developer session |
aicontrols.skill / aicontrols.subagent.type | Claude Code skill / sub-agent variant, when detected |
aicontrols.purpose | Set on AIControls' own internal LLM calls (session-summary, llm-judge, …) |
aicontrols.latency_ms | End-to-end latency (also the span duration) |
aicontrols.latency.identity_ms / .policy_ms / .forward_ms | Per-stage latency breakdown |
aicontrols.cache.class | Prompt-cache classification: write / hit / miss / na |
aicontrols.context_pressure_pct | Input tokens as % of the model's context window |
aicontrols.mutated_fields | Request fields rewritten by mutating policies |
aicontrols.pii.tokenized_entities | PII entity types tokenized in the request (never values) |
error.type | Low-cardinality error class on failed upstream calls: timeout, connection, canceled, or upstream_error — the raw error message is in the span status message |
Resource attributes
Set once per batch, not per span: service.name (from serviceName, default aicontrols).