Skip to content

feat: emit gen_ai.client.token.usage as an OTel Histogram (GenAI semconv)#102

Open
syrac88 wants to merge 1 commit into
DEVtheOPS:mainfrom
syrac88:feat/genai-token-histogram
Open

feat: emit gen_ai.client.token.usage as an OTel Histogram (GenAI semconv)#102
syrac88 wants to merge 1 commit into
DEVtheOPS:mainfrom
syrac88:feat/genai-token-histogram

Conversation

@syrac88

@syrac88 syrac88 commented Jul 14, 2026

Copy link
Copy Markdown

Problem

The plugin exports token usage exclusively via opencode.token.usage, a Counter
instrument. There is no gen_ai.client.token.usage metric, and the OTel GenAI
Semantic Conventions

define that metric as a Histogram. Some downstream platforms (e.g. Splunk
Observability Cloud's AI Agent Monitoring pages) require the histogram shape to
populate their token tiles — a Counter under any name is silently ignored.

Same root cause reported against Claude Code's own telemetry:
anthropics/claude-code#77562

Change

Adds a gen_ai.client.token.usage Histogram instrument, recorded alongside
(not replacing) the existing opencode.token.usage Counter:

  • Name intentionally unprefixed — it is fixed by the spec, and semconv-strict
    backends only recognise it verbatim
  • Dimensions per the GenAI semconv: gen_ai.token.type, gen_ai.request.model,
    gen_ai.operation.name, gen_ai.provider.name (token types mirror the
    Counter's type values: input, output, reasoning, cacheRead, cacheCreation)
  • Semconv-recommended explicit bucket boundaries via advice
  • Zero-valued token types are skipped so the distribution isn't distorted
    (unlike a Counter, a Histogram counts every record())
  • Can be disabled like any other metric: OPENCODE_DISABLE_METRICS=gen_ai.client.token.usage

Fully additive — existing dashboards built on opencode.token.usage are unaffected.

Testing

  • bun run typecheck / bun run lint / bun run build pass
  • bun test: 3 new unit tests (attributes per token type, zero-value skipping,
    disable flag); no regressions in the existing suite
  • Smoke-tested createInstruments() against an InMemoryMetricExporter: the
    metric exports with dataPointType: HISTOGRAM, unit {token}, and the
    semconv bucket boundaries
  • Background/real-world repro of the Splunk tile behaviour is documented in the
    linked Claude Code issue

Happy to adjust naming, gate this behind an opt-in/opt-out flag, or rework based
on maintainer preference.

🤖 Generated with Claude Code

…emconv

Adds a Histogram instrument alongside the existing opencode.token.usage
Counter so GenAI-semconv-compliant backends (e.g. Splunk Observability
Cloud AI Agent Monitoring) can populate their token tiles, which require
histogram-typed metrics and silently ignore counters regardless of name.

The metric name is intentionally unprefixed (fixed by the spec), uses the
semconv-recommended bucket boundaries, and skips zero-valued token types
so the distribution is not distorted. Fully additive: the existing
opencode.token.usage Counter is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dialupdisaster

Copy link
Copy Markdown
Contributor

The overall direction looks good: the unprefixed Histogram name, {token} unit, recommended bucket boundaries, additive behavior, disable support, and focused tests all make sense. CI is passing.

I think two semantic-convention correctness issues should be addressed before merging:

  1. gen_ai.token.type should use the canonical input and output values. The current implementation also emits reasoning, cacheRead, and cacheCreation. The current GenAI conventions say cached tokens should be included in input totals and reasoning tokens should be included in output totals. Given the plugin's existing accounting, this likely means recording:
input = tokens.input + tokens.cache.read + tokens.cache.write
output = tokens.output + tokens.reasoning

The detailed subtype measurements can remain available through opencode.token.usage. Otherwise semconv-aware dashboards may underreport the canonical input/output series and ignore the custom token types.

  1. gen_ai.provider.name currently receives opencode's raw providerID. That is correct for IDs such as anthropic, but not for all providers. For example, opencode's amazon-bedrock should be aws.bedrock; google needs to resolve to gcp.gemini, gcp.vertex_ai, or the general gcp.gen_ai based on what can reliably be inferred. The conventions require a predefined provider value when one applies, and strict dashboards may depend on these values.

Two smaller points:

With the canonical input/output aggregation and provider normalization resolved, this looks like a useful compatibility feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants