Issue #3615553: Stop double-charging cached tokens on OpenAI and Gemini
Closes #3615553 https://www.drupal.org/project/ai_metering/issues/3615553
OpenAI and Gemini report input tokens inclusive of cache reads, so TokenEstimator::calculateCost() was charging the cached portion twice: once at the full input rate (folded into input_tokens), again at the cache rate. Anthropic reports input exclusive of cache reads and was correctly unaffected.
Cost calculation now subtracts cached tokens from input for providers listed in the new quota.cache_inclusive_providers setting (default: openai, gemini), applied only to the cost calculation itself. Stored input_tokens and quota consumption stay the raw, provider-reported value, since quota enforcement is token-count-based and out of scope for this fix.
An update hook recomputes estimated_cost_usd for every historical row double-charged this way, from the raw input/cached counts already on record. Unlike #3615557, no pairing or heuristic is needed: the data for a correct recomputation was never lost.
Confirmed against a live OpenAI account with real prompt caching triggered: a paired cold/cache-hit call on an identical prompt showed input unchanged (4095) while cached jumped from 0 to 3968, proving input already counts the cache reads. Corrected cost for that call: $0.00032025, versus $0.00091545 the previous code would have billed.
A related upstream ambiguity in drupal/ai TokenUsageDto (no documented convention for whether input includes cached) is filed separately as ai#3586663.
AI assistance
Yes.