fix(proxy): reserve tiered output at the higher reasoning rate

Some tiered Dashscope models price reasoning output above standard output
(output_cost_per_reasoning_token > output_cost_per_token). The reservation charged
all output at the standard rate, so a reasoning-heavy request reserved too little
and concurrent calls could exceed the budget before reconciliation. The reasoning
share is unknown before the request runs, so reserve every output token at the
higher of the two configured rates, for both tiered and flat pricing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Shivam Rawat 2026-07-11 14:55:32 -07:00
parent c132a03ca7
commit 0bd831dc5e
2 changed files with 93 additions and 3 deletions

View file

@ -1014,20 +1014,29 @@ def _max_cost_for_cost_info(
if isinstance(tiered_pricing, list) and tiered_pricing:
tier = select_tier_for_input(tiered_pricing=tiered_pricing, input_tokens=input_tokens)
if tier is not None:
output_rate = max(
tier_rate(tier, "output_cost_per_token"),
tier_rate(tier, "output_cost_per_reasoning_token"),
)
return (input_tokens * tier_rate(tier, "input_cost_per_token")) + (
output_tokens * output_multiplier * tier_rate(tier, "output_cost_per_token")
output_tokens * output_multiplier * output_rate
)
input_cost_per_token = _to_float(model_info.get("input_cost_per_token"))
output_cost_per_token = _to_float(model_info.get("output_cost_per_token"))
output_cost_per_reasoning_token = _to_float(model_info.get("output_cost_per_reasoning_token"))
cost = 0.0
if input_cost_per_token is not None:
cost += input_tokens * input_cost_per_token
elif input_tokens > 0:
return None
if output_cost_per_token is not None:
cost += output_tokens * output_multiplier * output_cost_per_token
# The reasoning-token share is unknown before the request runs, so reserve every
# output token at the higher of the standard and reasoning rates to avoid
# under-reserving reasoning-heavy requests.
output_rate = max(output_cost_per_token or 0.0, output_cost_per_reasoning_token or 0.0)
if output_cost_per_token is not None or output_cost_per_reasoning_token is not None:
cost += output_tokens * output_multiplier * output_rate
elif output_tokens > 0:
return None

View file

@ -868,6 +868,87 @@ def test_tiered_reservation_is_all_or_nothing_with_output_tier_from_input_length
assert estimated > graduated_under_reserve
def test_tiered_reservation_uses_higher_reasoning_output_rate():
"""Some tiered models price reasoning output above standard output. The
reasoning-token share is unknown before the request runs, so reservation must
charge every output token at the higher of the two rates to avoid under-reserving
reasoning-heavy requests."""
tiered_pricing = [
{
"range": [0, 32000],
"input_cost_per_token": 1e-06,
"output_cost_per_token": 1.2e-06,
"output_cost_per_reasoning_token": 4e-06,
}
]
input_tokens = 1000
output_tokens = 500
with (
patch(
"litellm.proxy.spend_tracking.budget_reservation._get_model_cost_info",
return_value={"tiered_pricing": tiered_pricing, "max_output_tokens": 200000},
),
patch(
"litellm.proxy.spend_tracking.budget_reservation._estimate_input_tokens",
return_value=input_tokens,
),
patch(
"litellm.proxy.spend_tracking.budget_reservation._estimate_output_tokens",
return_value=output_tokens,
),
):
estimated = estimate_request_max_cost(
request_body=_request_body(),
route="/chat/completions",
llm_router=None,
)
expected = (input_tokens * 1e-06) + (output_tokens * 4e-06)
assert estimated == pytest.approx(expected)
# Reserving output at the plain rate would under-reserve reasoning-heavy calls.
under_reserve = (input_tokens * 1e-06) + (output_tokens * 1.2e-06)
assert estimated > under_reserve
def test_flat_reservation_uses_higher_reasoning_output_rate():
"""The same reasoning under-reservation gap exists for flat-rate models that
declare output_cost_per_reasoning_token above output_cost_per_token."""
input_tokens = 1000
output_tokens = 500
with (
patch(
"litellm.proxy.spend_tracking.budget_reservation._get_model_cost_info",
return_value={
"input_cost_per_token": 1e-06,
"output_cost_per_token": 1.2e-06,
"output_cost_per_reasoning_token": 4e-06,
"max_output_tokens": 200000,
},
),
patch(
"litellm.proxy.spend_tracking.budget_reservation._estimate_input_tokens",
return_value=input_tokens,
),
patch(
"litellm.proxy.spend_tracking.budget_reservation._estimate_output_tokens",
return_value=output_tokens,
),
):
estimated = estimate_request_max_cost(
request_body=_request_body(),
route="/chat/completions",
llm_router=None,
)
expected = (input_tokens * 1e-06) + (output_tokens * 4e-06)
assert estimated == pytest.approx(expected)
under_reserve = (input_tokens * 1e-06) + (output_tokens * 1.2e-06)
assert estimated > under_reserve
def test_reservation_uses_most_expensive_deployment_in_group():
"""When a model group mixes deployments with different tiered rates, reservation
must estimate against the most expensive one. Reserving the cheaper sibling would