[FEATURE] Add triggering_llm_request_id attribute to claude_code.tool spans for exact cost attribution

Resolved 💬 1 comment Opened Apr 23, 2026 by ThomasR101 Closed May 27, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Claude Code's OpenTelemetry layer emits claude_code.tool spans as siblings of their triggering claude_code.llm_request spans under a shared claude_code.interaction parent. The tool span carries tool_name, duration_ms, result_tokens, but no correlation key back to the specific LLM request that decided to call it.

This makes exact per-tool cost attribution impossible for observability pipelines. Cost is billed at the llm_request level; without a link from tool back to its triggering request, downstream systems can only approximate attribution by chronological sibling-ordering (exact for single-tool turns, proportional-by-duration for multi-tool fan-outs).

What I've verified

Span schema today (from live Tempo traces on @anthropic-ai/claude-code v2.1.118 with CLAUDE_CODE_ENABLE_TELEMETRY=1):

claude_code.interaction
├── claude_code.llm_request   (attrs: model, input_tokens, output_tokens,
│                                     cache_read_tokens, cache_creation_tokens,
│                                     request_id, duration_ms)
├── claude_code.llm_request
├── claude_code.tool          ← NO triggering_llm_request_id
│   └── claude_code.tool.execution
├── claude_code.tool          ← still no link — order-dependent only
└── claude_code.llm_request   (response generation — no tools)

Anthropic Messages API does not expose per-tool-use-block cost either — verified against anthropic-sdk-python v0.96.0 Usage type at https://github.com/anthropics/anthropic-sdk-python/blob/v0.96.0/src/anthropic/types/usage.py. Available fields: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens, cache_creation (TTL breakdown), server_tool_use (hosted-only), service_tier, inference_geo. No per-tool-use-block breakdown in any beta header, streaming event, or Advisor API surface.

The only upstream correlation key Claude Code CLI could expose is client-side: the request_id of the llm_request that produced each tool_use block. The CLI already has this internally (the response that contained the tool_use block is the same response that carries the request_id).

Related but distinct open issues:

  • #35953 requests CLAUDE_TOOL_USE_ID as env var in Bash subprocesses (outbound trace propagation)
  • #14859 requests agent hierarchy in hook events
  • #16184 requested parent_tool_use_id in hook payloads (closed, not_planned)

None add the llm_request → tool correlation I'm describing here.

Proposed Solution

Add a single string attribute to every claude_code.tool span:

triggering_llm_request_id: <string>  // the request_id of the llm_request
                                        whose tool_use block produced this tool span

This is the exact correlation key the CLI already has internally. Adding it as an OTel attribute is a minimal instrumentation change (assuming the tool-use-block handler has access to the enclosing response's request_id).

Why this unblocks accurate cost attribution

With triggering_llm_request_id present, an observability pipeline can:

  1. For each interaction trace, group tool spans by their triggering LLM request
  2. Single-tool turn: tool gets 100% of that llm_request's cost (exact)
  3. Multi-tool fan-out turn: prorate cost by output_token share of each tool_use block (Anthropic doesn't expose per-block usage, but response token share is a defensible proxy)
  4. No-tool llm_request (pure reasoning/response): cost stays unattributed to any tool

Without triggering_llm_request_id, step 1 requires heuristic sibling-ordering which cannot distinguish parallel tool_use blocks from the same llm_request from sequential calls. In my observed sessions, ~10% of LLM turns have multi-tool fan-outs (max observed: 10 tools in one turn).

Current workaround I've implemented

I run a Python post-processor that walks Tempo traces, sorts children of each claude_code.interaction by start_time_unix_nano, and groups tool spans with their nearest preceding llm_request sibling. This is chronologically correct for sequential tool calls but cannot distinguish parallel tool_use blocks from the same llm_request without the correlation attribute. Attribution drops from "exact" to "approximate" for the ~10% fan-out case.

With triggering_llm_request_id on each tool span, the heuristic disappears and attribution becomes exact regardless of fan-out.

Priority

Medium — enables accurate cost attribution for organizations building observability on Claude Code telemetry. Approximations work for aggregate rankings; exact attribution matters for per-team chargeback and tool ROI analysis.

Feature Category

OpenTelemetry instrumentation

Additional context

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗