Repository navigation
fix(batches): account for Anthropic cache TTL and search fees - #45834
mavericksea-ai wants to merge 2 commits into
Conversation
|
| shared_info: Final = litellm.get_model_info(model=model, custom_llm_provider=custom_llm_provider) | ||
| except Exception: # noqa: BLE001 # unmapped custom deployments can still supply their own rates | ||
| return get_cost_for_anthropic_web_search(model_info=model_info, usage=usage) | ||
| resolved_info: Final[ModelInfo] = {**shared_info, **model_info} if model_info is not None else shared_info |
There was a problem hiding this comment.
Custom prices erase search fees
When an Anthropic deployment sets custom token prices but no search price, deployment_pricing_model_info returns search_context_cost_per_query=None. This merge overwrites the published search price with that None, so paid searches still cost zero through retrieval logging and the background poller.
Ignore unset deployment values when merging, while preserving explicit zero prices.
| has_hour_price: Final = ( | ||
| any( | ||
| key.startswith("cache_creation_input_token_cost_above_1hr") and value is not None | ||
| for key, value in model_info.items() | ||
| ) | ||
| or _select_priced_tier(model_info, usage) is not None | ||
| ) | ||
| one_hour_rate: Final = standard_rates[3] / 2 if has_hour_price else cache_creation_rate |
There was a problem hiding this comment.
Missing hour prices change charges
has_hour_price treats any selected tiered_pricing entry as an explicit one-hour price, even when it has none. It also counts hour-price keys for thresholds the request has not crossed. In either case, standard_rates[3] can be an ordinary cache-write fallback that incorrectly replaces the supplied batch rate.
For example, a selected tier with cache writes priced at 6 and an explicit batch write rate of 4, but no hour price, charges one-hour tokens at 3 instead of retaining 4. Check whether the selected rate actually declares an hour price before replacing cache_creation_rate.
Merging this PR will not alter performance
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
@greptileai please review commit e880377. Both prior findings are addressed with regression tests for missing pricing fallbacks and explicit zero overrides |
TLDR
Problem this solves:
How it solves it:
I maintain DriftproofHQ and found this during usage-to-cost reconciliation work. This fixes the reproduced first-party Anthropic batch cases without claiming a production incident or Sev1
User Flow
Before, a developer reconciling batch usage gets an understated total
repro.pyAfter, the same usage produces the full cost
repro.pyRelevant issues
Fixes #45830
Related prior work includes #39626, which acknowledges dropped special batch costs, #43602 and #43585 on Vertex batch cache prices, and #36762 on Bedrock cache TTL translation
Changes
Batch cache-write pricing reads the reported one-hour token count and uses the standard one-hour rate with the batch discount. The existing rate resolver handles standard token thresholds and explicit zero prices. Missing one-hour prices retain the configured batch write rate, including when the selected tier omits that price or a higher threshold does not apply. Remaining cache-write tokens retain their existing batch rate
Anthropic batch rows use the existing provider search-price calculator. Aggregation retains the search count and a separate
tool_cost, adds it to the total, and forwards it through both retrieval-driven logging and the background poller. Unset deployment search prices inherit the published rate, while explicit zero and custom search prices remain overrides. Models without search usage keep their existing usage shapePre-Submission checklist
Screenshots / Proof of Fix
The requester required offline testing with no model calls. These are synthetic provider-result fixtures, not a live paid API or proxy test. Prices come from the bundled map. Save the Python reproduction in #45830 as
repro.py, use the same installed dependencies on both sides, and runLITELLM_LOCAL_MODEL_COST_MAP=True PYTHONPATH=. python repro.pyBefore (38d0333)
After (e880377)
The actual standard logging payload was also captured through both batch entry paths. The combined case now records
response_cost=0.53andtool_usage_cost=0.10. Seven fixtures per entry path check token-only, five-minute, one-hour, mixed-TTL, one-search, ten-search and combined usageThe offline SQLite-adapter budget test from the issue now stores $0.43 against a $0.40 limit and the real budget function blocks, whereas the old $0.28 record allowed it. This remains a component test with synthetic usage, not evidence of real customer losses
Validation
make checkpassed, including formatting, Ruff, import checks and the type/test-quality delta gates. The complete batch and calculator files passed 481 tests, batch-specific logging coverage passed 18 tests, and shared pricing helper suites passed 245 tests. The change adds 22 regression cases, including missing tier prices, uncrossed thresholds and deployment search overrides. Fourteen fixture-to-logging runs and twelve SQLite-adapter budget cases also passed offlineThe broader three-file run encounters
test_logging_result_for_bridge_calls. It also fails in the same combined-suite order on the unchangedde6a071baseline, while the isolated test passes on both baseline and patch. This limitation is recorded rather than treating the broad run as greenType
Bug Fix
Caveats
Low
Final Attestation