Problem
CostService selects the cache-aware cost calculator by provider via PROVIDERS_CACHE_COST_CALCULATOR. Most OpenAI-compatible providers (openai, azure, xai, deepseek, fireworks_ai, moonshot) are wired to SpanCostCalculator.textGenerationWithCacheCostOpenAI, which reads cached tokens from exactly one shape:
original_usage.prompt_tokens_details.cached_tokens // (or the bare-OTel prompt_tokens_details.cached_tokens)
The problem is that the calculator is chosen by provider, but the field the calculator must read is determined by the usage shape the span actually carries, and those two are not always aligned. A provider assigned the OpenAI calculator that emits cache tokens in the Anthropic shape (cache_read_input_tokens / cache_creation_input_tokens) or a bare-OTel cache_read.input_tokens shape will have those tokens invisible to the calculator, so cached input is billed at the full input rate instead of the configured cache-read rate. The mismatch is silent: cost is simply too high, with no error.
This coupling is fragile as provider coverage grows, since a provider's SDK/instrumentation can change the usage shape independently of its canonical name.
Proposal
Route the cache calculator by the usage keys actually present rather than (or in addition to) the provider name: if the span carries prompt_tokens_details.cached_tokens use the OpenAI reader; if it carries cache_read_input_tokens/cache_creation_input_tokens use the Anthropic reader; etc. A small shape-detection helper keyed on which cache fields are populated would make cache pricing correct regardless of which provider emitted the span, and would remove the need to hand-maintain a per-provider calculator map for every new cache-priced provider.
Context
Surfaced while registering additional providers; Baz raised the same cache-shape concern on earlier provider PRs (snowflake / deepinfra). Filing as a standalone robustness improvement rather than folding it into a registration PR.
Problem
CostServiceselects the cache-aware cost calculator by provider viaPROVIDERS_CACHE_COST_CALCULATOR. Most OpenAI-compatible providers (openai,azure,xai,deepseek,fireworks_ai,moonshot) are wired toSpanCostCalculator.textGenerationWithCacheCostOpenAI, which reads cached tokens from exactly one shape:The problem is that the calculator is chosen by provider, but the field the calculator must read is determined by the usage shape the span actually carries, and those two are not always aligned. A provider assigned the OpenAI calculator that emits cache tokens in the Anthropic shape (
cache_read_input_tokens/cache_creation_input_tokens) or a bare-OTelcache_read.input_tokensshape will have those tokens invisible to the calculator, so cached input is billed at the full input rate instead of the configured cache-read rate. The mismatch is silent: cost is simply too high, with no error.This coupling is fragile as provider coverage grows, since a provider's SDK/instrumentation can change the usage shape independently of its canonical name.
Proposal
Route the cache calculator by the usage keys actually present rather than (or in addition to) the provider name: if the span carries
prompt_tokens_details.cached_tokensuse the OpenAI reader; if it carriescache_read_input_tokens/cache_creation_input_tokensuse the Anthropic reader; etc. A small shape-detection helper keyed on which cache fields are populated would make cache pricing correct regardless of which provider emitted the span, and would remove the need to hand-maintain a per-provider calculator map for every new cache-priced provider.Context
Surfaced while registering additional providers; Baz raised the same cache-shape concern on earlier provider PRs (snowflake / deepinfra). Filing as a standalone robustness improvement rather than folding it into a registration PR.