mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
test(cost-calc): pin the rate fallbacks inside a tiered-pricing tier
_get_tiered_base_costs documents that tiered pricing is all-or-nothing: a
tier is picked from the request's input tokens, and any rate that tier does
not declare falls back to the tier's own input rate so one request is never
priced from two tiers.
Nothing checked that. Every existing tiered test supplies a fully populated
tier, so the fallbacks were never reached: deleting them from the source
left the whole suite green. The fallbacks are not hypothetical either. Of
the 66 tiered rows shipped in model_prices_and_context_window.json, 54
declare no cache-creation rate and 44 declare no cache-read rate, so the
fallback is what prices their cached tokens today.
Adds three tests on the generic path:
- a tier with no cache rates bills cached and cache-creation tokens at
that tier's input rate, ignoring the model's top-level cache rates
- a tier with no above-1hr rate bills 1h cache writes at the tier's
cache-creation rate rather than zero
- a tier with no input rate is not a priced tier at all, so the model's
flat rates still apply instead of billing input at zero
Test-only change, no source touched.
This commit is contained in:
parent
137311ffd6
commit
54cbc44705
1 changed files with 130 additions and 0 deletions
|
|
@ -716,6 +716,136 @@ def test_generic_cost_per_token_tier_without_an_output_rate_bills_the_model_rate
|
|||
litellm.model_cost.pop(model, None)
|
||||
|
||||
|
||||
def test_generic_cost_per_token_tier_without_cache_rates_bills_cache_at_the_tier_input_rate():
|
||||
model = "litellm-test-tiered-no-cache-rates"
|
||||
custom_llm_provider = "openrouter"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": custom_llm_provider,
|
||||
"mode": "chat",
|
||||
"cache_read_input_token_cost": 9e-09,
|
||||
"cache_creation_input_token_cost": 9e-06,
|
||||
"tiered_pricing": [
|
||||
{
|
||||
"range": [0, 32000],
|
||||
"input_cost_per_token": 4.6e-07,
|
||||
"output_cost_per_token": 2.3e-06,
|
||||
},
|
||||
{
|
||||
"range": [32000, 128000],
|
||||
"input_cost_per_token": 7e-07,
|
||||
"output_cost_per_token": 3.5e-06,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
uncached = Usage(prompt_tokens=40000, completion_tokens=100, total_tokens=40100)
|
||||
cached = Usage(
|
||||
prompt_tokens=40000,
|
||||
completion_tokens=100,
|
||||
total_tokens=40100,
|
||||
prompt_tokens_details=PromptTokensDetailsWrapper(
|
||||
cached_tokens=5000, cache_creation_tokens=15000
|
||||
),
|
||||
)
|
||||
uncached_prompt_cost, _ = generic_cost_per_token(
|
||||
model=model,
|
||||
usage=uncached,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
cached_prompt_cost, cached_completion_cost = generic_cost_per_token(
|
||||
model=model,
|
||||
usage=cached,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
|
||||
tier_input_rate = 7e-07
|
||||
assert round(cached_prompt_cost, 12) == round(40000 * tier_input_rate, 12)
|
||||
assert round(cached_prompt_cost, 12) == round(uncached_prompt_cost, 12)
|
||||
assert round(cached_completion_cost, 12) == round(100 * 3.5e-06, 12)
|
||||
finally:
|
||||
litellm.model_cost.pop(model, None)
|
||||
|
||||
|
||||
def test_generic_cost_per_token_tier_without_a_1hr_cache_rate_bills_the_tier_cache_creation_rate():
|
||||
model = "litellm-test-tiered-no-1hr-cache-rate"
|
||||
custom_llm_provider = "openrouter"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": custom_llm_provider,
|
||||
"mode": "chat",
|
||||
"cache_creation_input_token_cost_above_1hr": 9e-05,
|
||||
"tiered_pricing": [
|
||||
{
|
||||
"range": [0, 128000],
|
||||
"input_cost_per_token": 7e-07,
|
||||
"output_cost_per_token": 3.5e-06,
|
||||
"cache_creation_input_token_cost": 8.75e-07,
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
usage = Usage(
|
||||
prompt_tokens=1000,
|
||||
completion_tokens=10,
|
||||
total_tokens=1010,
|
||||
prompt_tokens_details=PromptTokensDetailsWrapper(
|
||||
cache_creation_tokens=800,
|
||||
cache_creation_token_details=CacheCreationTokenDetails(
|
||||
ephemeral_5m_input_tokens=300, ephemeral_1h_input_tokens=500
|
||||
),
|
||||
),
|
||||
)
|
||||
prompt_cost, completion_cost = generic_cost_per_token(
|
||||
model=model,
|
||||
usage=usage,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
|
||||
tier_cache_creation_rate = 8.75e-07
|
||||
expected_prompt = (200 * 7e-07) + (800 * tier_cache_creation_rate)
|
||||
assert round(prompt_cost, 12) == round(expected_prompt, 12)
|
||||
assert round(completion_cost, 12) == round(10 * 3.5e-06, 12)
|
||||
finally:
|
||||
litellm.model_cost.pop(model, None)
|
||||
|
||||
|
||||
def test_generic_cost_per_token_tier_without_an_input_rate_is_not_a_priced_tier():
|
||||
model = "litellm-test-tiered-no-input-rate"
|
||||
custom_llm_provider = "openrouter"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": custom_llm_provider,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 1e-06,
|
||||
"output_cost_per_token": 2e-06,
|
||||
"tiered_pricing": [{"range": [0, 128000], "output_cost_per_token": 3.5e-06}],
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
usage = Usage(prompt_tokens=1000, completion_tokens=100, total_tokens=1100)
|
||||
prompt_cost, completion_cost = generic_cost_per_token(
|
||||
model=model,
|
||||
usage=usage,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
assert round(prompt_cost, 12) == round(1000 * 1e-06, 12)
|
||||
assert round(completion_cost, 12) == round(100 * 2e-06, 12)
|
||||
finally:
|
||||
litellm.model_cost.pop(model, None)
|
||||
|
||||
|
||||
def test_router_deployment_with_input_only_tiers_bills_completions_at_the_backend_rate():
|
||||
"""Regression: the router registers a deployment's custom pricing as a standalone
|
||||
model_cost entry holding only the supplied fields, so an input-only tier table left
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue