mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
fix(cost_calculator): respect custom pricing for known providers like anthropic
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), cost_per_token() dispatched directly to the provider-specific function (anthropic_cost_per_token) before checking custom pricing entries. This caused the zero-cost override to be ignored and real provider pricing to be applied instead. - Add custom_pricing parameter to cost_per_token() and pass it from completion_cost() - When custom_pricing=True and model is in model_cost, bypass the provider dispatch chain and use generic_cost_per_token() directly - Fix zero-cost check in else branch: use `is not None` instead of `or 0.0 > 0` which collapsed 0.0 to False Fixes #25204 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d251238bd7
commit
bc8be27ab2
2 changed files with 82 additions and 2 deletions
|
|
@ -283,6 +283,7 @@ def cost_per_token( # noqa: PLR0915
|
|||
audio_transcription_file_duration: float = 0.0, # for audio transcription calls - the file time in seconds
|
||||
### SERVICE TIER ###
|
||||
service_tier: Optional[str] = None, # for OpenAI service tier pricing
|
||||
custom_pricing: Optional[bool] = None, # whether model uses custom pricing from router/config
|
||||
response: Optional[Any] = None,
|
||||
### REQUEST MODEL ###
|
||||
request_model: Optional[str] = None, # original request model for router detection
|
||||
|
|
@ -475,6 +476,13 @@ def cost_per_token( # noqa: PLR0915
|
|||
else None
|
||||
),
|
||||
)
|
||||
elif custom_pricing is True and model in model_cost_ref:
|
||||
return generic_cost_per_token(
|
||||
model=model,
|
||||
usage=usage_block,
|
||||
custom_llm_provider=custom_llm_provider or "",
|
||||
service_tier=service_tier,
|
||||
)
|
||||
elif custom_llm_provider == "vertex_ai":
|
||||
cost_router = google_cost_router(
|
||||
model=model_without_prefix,
|
||||
|
|
@ -545,8 +553,8 @@ def cost_per_token( # noqa: PLR0915
|
|||
)
|
||||
|
||||
if (
|
||||
(model_info.get("input_cost_per_token") or 0.0) > 0
|
||||
or (model_info.get("output_cost_per_token") or 0.0) > 0
|
||||
model_info.get("input_cost_per_token") is not None
|
||||
or model_info.get("output_cost_per_token") is not None
|
||||
):
|
||||
return generic_cost_per_token(
|
||||
model=model,
|
||||
|
|
@ -1509,6 +1517,7 @@ def completion_cost( # noqa: PLR0915
|
|||
audio_transcription_file_duration=audio_transcription_file_duration,
|
||||
rerank_billed_units=rerank_billed_units,
|
||||
service_tier=service_tier,
|
||||
custom_pricing=custom_pricing,
|
||||
response=completion_response,
|
||||
request_model=request_model_for_cost,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -447,6 +447,77 @@ def test_per_request_custom_pricing_with_router():
|
|||
assert "gpt-3.5-turbo" in selected
|
||||
|
||||
|
||||
def test_zero_cost_custom_pricing_with_anthropic_provider():
|
||||
"""
|
||||
When a user sets input_cost_per_token=0.0 and output_cost_per_token=0.0
|
||||
with custom_llm_provider="anthropic" via the router, the zero-cost
|
||||
override must be respected. The provider-specific dispatch should
|
||||
be bypassed so that real Anthropic pricing is NOT used.
|
||||
|
||||
Regression test for https://github.com/BerriAI/litellm/issues/25204
|
||||
"""
|
||||
from litellm import Router
|
||||
|
||||
router = Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "free-claude",
|
||||
"litellm_params": {
|
||||
"model": "anthropic/claude-sonnet-4-20250514",
|
||||
"api_key": "test_api_key",
|
||||
},
|
||||
"model_info": {
|
||||
"id": "zero-cost-model-id",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
result = router.completion(
|
||||
model="free-claude",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
mock_response="Hi there!",
|
||||
)
|
||||
|
||||
assert result._hidden_params["response_cost"] == 0.0
|
||||
|
||||
|
||||
def test_cost_per_token_custom_pricing_bypasses_provider_dispatch():
|
||||
"""
|
||||
cost_per_token with custom_pricing=True should use generic_cost_per_token
|
||||
instead of the provider-specific dispatch, ensuring custom pricing entries
|
||||
(including zero costs) are respected.
|
||||
|
||||
Regression test for https://github.com/BerriAI/litellm/issues/25204
|
||||
"""
|
||||
from litellm.cost_calculator import cost_per_token
|
||||
|
||||
custom_model_id = "custom-zero-cost-anthropic"
|
||||
litellm.register_model(
|
||||
model_cost={
|
||||
custom_model_id: {
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"litellm_provider": "anthropic",
|
||||
"max_tokens": 8192,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
prompt_cost, completion_cost_val = cost_per_token(
|
||||
model=custom_model_id,
|
||||
prompt_tokens=1000,
|
||||
completion_tokens=500,
|
||||
custom_llm_provider="anthropic",
|
||||
custom_pricing=True,
|
||||
)
|
||||
|
||||
assert prompt_cost == 0.0
|
||||
assert completion_cost_val == 0.0
|
||||
|
||||
|
||||
def test_azure_realtime_cost_calculator():
|
||||
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
|
||||
litellm.model_cost = litellm.get_model_cost_map(url="")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue