Skip to content

LLM monetary costs are recorded with no currency — ingest OTel GenAI gen_ai.usage.cost.currency #8564

Description

@dhyabi2

Costs without a unit of account

Opik already collects LLM monetary costs, in two shapes:

  1. A pre-computed total from gen_ai.usage.cost, routed to the span's
    totalEstimatedCost:

    // apps/opik-backend/.../domain/mapping/otel/GenAIMappingRules.java
    .rule("gen_ai.usage.cost").source(SOURCE)
        .outcome(OpenTelemetryMappingRule.Outcome.COST).spanType(SpanType.llm).build(),

    guarded by genAiUsageCostExtractedIntoTotalEstimatedCost in
    OpenTelemetryMapperTest.java (the fix for the earlier bug where a double cost
    was "routed there and silently dropped").

  2. gen_ai.cost.* attributes (total_cost / input_cost / output_cost) captured
    into span metadata (OpenTelemetryMapperTest.java).

In both paths the value is a bare number. Nothing records what currency it is in. For
anyone who self-hosts Opik, ingests traces billed in a non-USD currency, or compares
cost across deployments, the number is uninterpretable and cross-deployment cost
reconciliation is impossible.

The standard direction

The OpenTelemetry GenAI semantic-conventions working group is standardizing this
exactly (there is no OTel convention for GenAI cost today; each vendor rolls its own).
The open proposal
add per-operation cost conventions (gen_ai.usage.cost.*) #443
defines:

  • gen_ai.usage.cost.amount (double) — the monetary cost at recording time
  • gen_ai.usage.cost.currency (string) — the ISO 4217 code, conditionally required when amount is set
  • gen_ai.usage.cost.source (enum provider | local) — provenance

So the attribute a trace should carry beside gen_ai.usage.cost is
gen_ai.usage.cost.currency, and the OTel GenAI mapper is the natural place to map it.

Suggested scope

Consider ingesting gen_ai.usage.cost.currency alongside the existing
gen_ai.usage.cost handling in GenAIMappingRules.java (map it to COST/METADATA so
the unit rides with the amount), and keeping the value additive — no change to the
totalEstimatedCost number or to gen_ai.cost.* behavior. A test alongside
genAiUsageCostExtractedIntoTotalEstimatedCost asserting that a span with
gen_ai.usage.cost + gen_ai.usage.cost.currency retains both the amount and its unit
would lock it in.

Why it helps

  • Cost becomes interpretable: the number names its unit of account (USD today, any ISO
    4217 code later) without changing the amount logic.
  • Brings the backend's handling in line with the emerging OTel GenAI standard, where
    currency is required whenever an amount is set.
  • Matters to any per-trace cost-accounting or billing workflow that consumes Opik
    spans.

Happy to open a PR on the backend mapper with tests if this scope looks right.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions