litellm - ✅(Solved) Fix [Bug]: custom_llm_provider "anthropic" bypasses input_cost_per_token: 0 — cost_per_token() dispatches to anthropic_cost_per_token() before checking custom pricing [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#25204Fetched 2026-04-08 02:53:09
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1referenced ×1

Root Cause

  • custom_llm_provider: "anthropic" is required in our setup because the intermediate proxy expects Anthropic-format requests (with metadata.user_id for per-user routing)
  • Setting base_model to a fake name via the /model/update API does not work — the API auto-populates base_model from the model name and ignores the override
  • The same issue likely affects all provider-specific dispatch paths (openai, anthropic, bedrock, etc.) where cost_per_token() dispatches to the provider function before checking custom pricing
  • Related (but opposite symptom): #23309 — custom pricing not applied for anthropic_messages call type

Fix Action

Fix / Workaround

In litellm/cost_calculator.py, cost_per_token() has a provider dispatch chain:

This runs before the else branch that would check litellm.model_cost for custom pricing entries. So when custom_llm_provider == "anthropic", the function dispatches directly to anthropic_cost_per_token(), which calls get_model_info() against the internal price DB (model_prices_and_context_window.json), finds the real Anthropic pricing (e.g. $15/$75 per MTok for Opus 4.6), and returns that — completely bypassing the configured 0 costs.

Additionally, use_custom_pricing_for_model() correctly detects input_cost_per_token: 0.0 as custom pricing (is not None check), but this signal is never consulted in the Anthropic dispatch path.

PR fix notes

PR #25206: fix(cost_calculator): respect custom pricing for known providers

Description (problem / solution / changelog)

Summary

  • Fixes #25204: When custom_llm_provider is set to a known provider (e.g., "anthropic") and the user configures explicit zero costs (input_cost_per_token: 0.0), the zero-cost override was ignored because cost_per_token() dispatched to the provider-specific function before checking custom pricing entries
  • Adds custom_pricing parameter to cost_per_token() so it can bypass the provider dispatch chain when custom pricing is configured
  • Fixes secondary bug in the else branch where (... or 0.0) > 0 collapsed zero costs to False

Root Cause

In cost_per_token(), the provider dispatch chain (elif custom_llm_provider == "anthropic": return anthropic_cost_per_token(...)) runs before any check for custom pricing entries in litellm.model_cost. The completion_cost() function already resolves the correct model name via _select_model_name_for_cost_calc() using the custom_pricing flag, but this flag was never passed to cost_per_token().

Changes

  1. cost_per_token() signature: Added custom_pricing: Optional[bool] = None parameter
  2. Custom pricing bypass: When custom_pricing=True and the model is found in model_cost_ref, call generic_cost_per_token() directly — bypassing all provider-specific dispatch
  3. completion_cost() call: Pass custom_pricing=custom_pricing through to cost_per_token()
  4. Zero-cost fix: Changed (model_info.get("input_cost_per_token") or 0.0) > 0 to is not None in the else branch

Test plan

  • New test: test_zero_cost_custom_pricing_with_anthropic_provider — Router-level test with zero costs for anthropic model
  • New test: test_cost_per_token_custom_pricing_bypasses_provider_dispatch — Unit test calling cost_per_token() directly
  • All 5 existing custom pricing tests pass
  • Full cost calculator test suite passes (37 tests)
  • Completion cost custom pricing tests pass

🤖 Generated with Claude Code

Changed files

  • litellm/cost_calculator.py (modified, +11/-2)
  • tests/test_litellm/test_cost_calculator.py (modified, +71/-0)

Code Example

elif custom_llm_provider == "anthropic":
    return anthropic_cost_per_token(model=model, usage=usage_block)

---

model_list:
  - model_name: my-proxy/claude-opus-4-6
    litellm_params:
      model: my-proxy/claude-opus-4-6
      api_base: https://my-internal-proxy.example.com
      api_key: os.environ/PROXY_API_KEY
      custom_llm_provider: anthropic
      input_cost_per_token: 0.0
      output_cost_per_token: 0.0
      cache_creation_input_token_cost: 0.0
      cache_read_input_token_cost: 0.0
    model_info:
      input_cost_per_token: 0.0
      output_cost_per_token: 0.0
      cache_creation_input_token_cost: 0.0
      cache_read_input_token_cost: 0.0

---

# Check custom pricing FIRST, before provider dispatch
if custom_pricing and _has_custom_cost_overrides(model_info):
    return _apply_custom_pricing(model_info, usage)

# Then fall through to provider-specific paths
elif custom_llm_provider == "anthropic":
    return anthropic_cost_per_token(...)
RAW_BUFFERClick to expand / collapse

What happened?

When a model is configured with custom_llm_provider: "anthropic" and explicit zero costs (input_cost_per_token: 0, output_cost_per_token: 0), LiteLLM ignores the zero-cost override and calculates spend using the internal Anthropic pricing database.

This affects any deployment that proxies Anthropic models through an intermediate service (subscription-based, internal proxy, etc.) where the actual per-token cost is zero.

Relevant code

In litellm/cost_calculator.py, cost_per_token() has a provider dispatch chain:

elif custom_llm_provider == "anthropic":
    return anthropic_cost_per_token(model=model, usage=usage_block)

This runs before the else branch that would check litellm.model_cost for custom pricing entries. So when custom_llm_provider == "anthropic", the function dispatches directly to anthropic_cost_per_token(), which calls get_model_info() against the internal price DB (model_prices_and_context_window.json), finds the real Anthropic pricing (e.g. $15/$75 per MTok for Opus 4.6), and returns that — completely bypassing the configured 0 costs.

Additionally, use_custom_pricing_for_model() correctly detects input_cost_per_token: 0.0 as custom pricing (is not None check), but this signal is never consulted in the Anthropic dispatch path.

LiteLLM Version

v1.82.3-stable (Docker image: ghcr.io/berriai/litellm:v1.82.3-stable)

Configuration

model_list:
  - model_name: my-proxy/claude-opus-4-6
    litellm_params:
      model: my-proxy/claude-opus-4-6
      api_base: https://my-internal-proxy.example.com
      api_key: os.environ/PROXY_API_KEY
      custom_llm_provider: anthropic
      input_cost_per_token: 0.0
      output_cost_per_token: 0.0
      cache_creation_input_token_cost: 0.0
      cache_read_input_token_cost: 0.0
    model_info:
      input_cost_per_token: 0.0
      output_cost_per_token: 0.0
      cache_creation_input_token_cost: 0.0
      cache_read_input_token_cost: 0.0

Evidence

Expected: $0.00 spend (zero-cost subscription model)

Actual: real Anthropic pricing applied

Recent request logs:

ModelTokensExpected CostActual Cost
my-proxy/claude-opus-4-689,262 (88,800 prompt + 462 completion)$0.00$0.058085
my-proxy/claude-opus-4-646,434 (46,362 prompt + 72 completion)$0.00$0.051309
my-proxy/claude-opus-4-646,347 (41,784 prompt + 4,563 completion)$0.00$0.183162
my-proxy/claude-haiku-4-5-2025100115,054 (14,987 prompt + 67 completion)$0.00$0.001947

The calculated costs match standard Anthropic API pricing, confirming LiteLLM's cost calculator is using the internal price DB rather than the configured 0.0 overrides.

Aggregated daily spend shows ~$700/day for claude-opus-4-6 across our team — all phantom costs from this bug.

Additional context

  • custom_llm_provider: "anthropic" is required in our setup because the intermediate proxy expects Anthropic-format requests (with metadata.user_id for per-user routing)
  • Setting base_model to a fake name via the /model/update API does not work — the API auto-populates base_model from the model name and ignores the override
  • The same issue likely affects all provider-specific dispatch paths (openai, anthropic, bedrock, etc.) where cost_per_token() dispatches to the provider function before checking custom pricing
  • Related (but opposite symptom): #23309 — custom pricing not applied for anthropic_messages call type

Suggested fix

In cost_per_token(), check for custom pricing (via use_custom_pricing_for_model() or custom_cost_per_token) before the provider-specific dispatch chain. If custom pricing is configured, use it directly and skip the provider lookup. Something like:

# Check custom pricing FIRST, before provider dispatch
if custom_pricing and _has_custom_cost_overrides(model_info):
    return _apply_custom_pricing(model_info, usage)

# Then fall through to provider-specific paths
elif custom_llm_provider == "anthropic":
    return anthropic_cost_per_token(...)

extent analysis

TL;DR

Modify the cost_per_token() function to check for custom pricing before dispatching to provider-specific paths.

Guidance

  1. Check custom pricing first: In cost_per_token(), add a check for custom pricing using use_custom_pricing_for_model() or custom_cost_per_token before the provider-specific dispatch chain.
  2. Apply custom pricing: If custom pricing is configured, use it directly and skip the provider lookup by returning the custom pricing value.
  3. Fall through to provider paths: If no custom pricing is configured, fall through to the provider-specific paths as currently implemented.
  4. Verify the fix: Test the modified cost_per_token() function with different scenarios, including custom pricing and provider-specific paths, to ensure the correct costs are being calculated.

Example

if custom_pricing and _has_custom_cost_overrides(model_info):
    return _apply_custom_pricing(model_info, usage)
elif custom_llm_provider == "anthropic":
    return anthropic_cost_per_token(model=model, usage=usage_block)

Notes

This fix assumes that the use_custom_pricing_for_model() function correctly detects custom pricing and that the _has_custom_cost_overrides() function checks for custom cost overrides. Additionally, this fix may need to be applied to other provider-specific dispatch paths.

Recommendation

Apply the suggested fix to the cost_per_token() function to correctly handle custom pricing for Anthropic models. This fix will ensure that custom pricing is used when configured, rather than falling back to the internal Anthropic pricing database.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - ✅(Solved) Fix [Bug]: custom_llm_provider "anthropic" bypasses input_cost_per_token: 0 — cost_per_token() dispatches to anthropic_cost_per_token() before checking custom pricing [1 pull requests, 1 participants]