fix(dashscope): honor the model reasoning rate when a tier omits output rates

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-08-15 03:42:23 +00:00
parent 34918d34f9
commit e46f2ca62f
2 changed files with 70 additions and 10 deletions

View file

@ -88,19 +88,20 @@ def _calculate_completion_cost(
model_info: ModelInfo,
tier: dict | None,
) -> float:
# A tier declaring no output rate falls back to the model's own, since a table spelling out
# only input rates would otherwise serve every completion for free
# A tier that declares output rates keeps the request on them, all-or-nothing. A tier table
# spelling out only input rates would serve every completion for free, so there the model's
# own output rates stand in
tier_declares_output: Final = tier is not None and "output_cost_per_token" in tier
output_cost: Final = (
tier_rate(tier, "output_cost_per_token")
if tier is not None and "output_cost_per_token" in tier
if tier_declares_output
else float(model_info.get("output_cost_per_token") or 0.0)
)
if tier is not None:
return (breakdown.completion_tokens * output_cost) + (
breakdown.reasoning_tokens * (tier_rate(tier, "output_cost_per_reasoning_token") or output_cost)
)
reasoning_cost: Final = _flat_rate(model_info, "output_cost_per_reasoning_token", "output_cost_per_token")
tier_reasoning_cost: Final = tier_rate(tier, "output_cost_per_reasoning_token") if tier is not None else 0.0
model_reasoning_cost: Final = (
0.0 if tier_declares_output else float(model_info.get("output_cost_per_reasoning_token") or 0.0)
)
reasoning_cost: Final = tier_reasoning_cost or model_reasoning_cost or output_cost
return (breakdown.completion_tokens * output_cost) + (breakdown.reasoning_tokens * reasoning_cost)

View file

@ -21,7 +21,11 @@ import litellm
from litellm.llms.dashscope.cost_calculator import (
cost_per_token as dashscope_cost_per_token,
)
from litellm.types.utils import Usage, PromptTokensDetailsWrapper
from litellm.types.utils import (
CompletionTokensDetailsWrapper,
PromptTokensDetailsWrapper,
Usage,
)
class TestDashscopeCostCalculator:
@ -344,6 +348,61 @@ class TestDashscopeCostCalculator:
assert math.isclose(prompt_cost, 500 * 4e-07, rel_tol=1e-10)
assert math.isclose(completion_cost, 200 * 1.6e-06, rel_tol=1e-10)
def test_dashscope_tier_without_an_output_rate_bills_the_model_reasoning_rate(self):
"""
Regression: a tier declaring only an input rate billed reasoning tokens at the model's
plain output rate, ignoring the model's dedicated reasoning rate.
"""
litellm.model_cost["dashscope/qwen-input-only-reasoning-test"] = {
"litellm_provider": "dashscope",
"mode": "chat",
"output_cost_per_token": 1.6e-06,
"output_cost_per_reasoning_token": 4e-06,
"tiered_pricing": [{"range": [0, 1000], "input_cost_per_token": 4e-07}],
}
usage = Usage(
prompt_tokens=500,
completion_tokens=200,
completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=150),
)
_, completion_cost = dashscope_cost_per_token(
model="qwen-input-only-reasoning-test", usage=usage
)
assert math.isclose(
completion_cost, (50 * 1.6e-06) + (150 * 4e-06), rel_tol=1e-10
)
def test_dashscope_tier_output_rate_wins_over_the_model_reasoning_rate(self):
"""
A tier declaring its own output rate keeps reasoning tokens on that tier rather than
mixing in a model-level reasoning rate.
"""
litellm.model_cost["dashscope/qwen-tier-output-reasoning-test"] = {
"litellm_provider": "dashscope",
"mode": "chat",
"output_cost_per_reasoning_token": 4e-06,
"tiered_pricing": [
{
"range": [0, 1000],
"input_cost_per_token": 4e-07,
"output_cost_per_token": 1.6e-06,
}
],
}
usage = Usage(
prompt_tokens=500,
completion_tokens=200,
completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=150),
)
_, completion_cost = dashscope_cost_per_token(
model="qwen-tier-output-reasoning-test", usage=usage
)
assert math.isclose(completion_cost, 200 * 1.6e-06, rel_tol=1e-10)
def test_dashscope_tiered_pricing_zero_input_falls_back_to_flat_rates(self):
"""
No tier can be selected without input tokens, so an empty-prompt request must