This commit is contained in:
Shivam Rawat 2026-09-01 02:54:29 -07:00 committed by GitHub
commit d46a1ffeb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 257 additions and 3 deletions

View file

@ -8,7 +8,8 @@ See https://help.aliyun.com/zh/model-studio/billing-for-model-studio
"""
from dataclasses import dataclass
from typing import Final
from types import MappingProxyType
from typing import Final, cast
from litellm.litellm_core_utils.llm_cost_calc.tiered_pricing import select_tier_for_input, tier_rate
from litellm.litellm_core_utils.llm_cost_calc.utils import (
@ -50,6 +51,30 @@ def _extract_token_breakdown(usage: Usage) -> TokenBreakdown:
)
# Models like deepseek-v4-pro-0813 price thinking-mode requests at their own input,
# cache-hit, and output rates; reasoning tokens in the usage mark the request as thinking-mode
_THINKING_RATE_KEYS: Final = MappingProxyType(
{
"input_cost_per_token": "input_cost_per_token_thinking",
"cache_read_input_token_cost": "cache_read_input_token_cost_thinking",
"output_cost_per_token": "output_cost_per_token_thinking",
}
)
def _with_thinking_rates(model_info: ModelInfo, breakdown: TokenBreakdown) -> ModelInfo:
if breakdown.reasoning_tokens == 0:
return model_info
overrides: Final = {
base_key: model_info.get(thinking_key)
for base_key, thinking_key in _THINKING_RATE_KEYS.items()
if model_info.get(thinking_key) is not None
}
if not overrides:
return model_info
return cast("ModelInfo", {**model_info, **overrides})
def _flat_rate(model_info: ModelInfo, cost_key: str, fallback_cost_key: str) -> float:
value: Final = model_info.get(cost_key)
if value is None:
@ -123,8 +148,9 @@ def cost_per_token(model: str, usage: Usage) -> tuple[float, float]:
Returns:
Tuple[float, float] - (prompt_cost_in_usd, completion_cost_in_usd)
"""
model_info: Final = get_model_info(model=model, custom_llm_provider="dashscope")
base_model_info: Final = get_model_info(model=model, custom_llm_provider="dashscope")
breakdown: Final = _extract_token_breakdown(usage)
model_info: Final = _with_thinking_rates(model_info=base_model_info, breakdown=breakdown)
raw_tiers: Final = model_info.get("tiered_pricing")
tiered_pricing: Final = raw_tiers if isinstance(raw_tiers, list) else None
tier: Final = (

View file

@ -13766,6 +13766,23 @@
"supports_system_messages": true,
"supports_tool_choice": false
},
"dashscope/MiniMax/MiniMax-M3": {
"cache_read_input_token_cost": 1.19e-07,
"input_cost_per_token": 5.94e-07,
"litellm_provider": "dashscope",
"max_input_tokens": 1048576,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 2.374e-06,
"source": "https://help.aliyun.com/en/model-studio/minimax-m3",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true,
"supports_video_input": true,
"supports_vision": true
},
"dashscope/deepseek-v4-flash": {
"cache_read_input_token_cost": 4e-08,
"input_cost_per_token": 2e-07,
@ -13814,6 +13831,26 @@
"supports_response_schema": true,
"supports_tool_choice": true
},
"dashscope/deepseek-v4-pro-0813": {
"cache_read_input_token_cost": 6.4e-08,
"cache_read_input_token_cost_thinking": 1.27e-07,
"input_cost_per_token": 6.36e-07,
"input_cost_per_token_thinking": 1.272e-06,
"litellm_provider": "dashscope",
"max_input_tokens": 1000000,
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"output_cost_per_reasoning_token": 3.816e-06,
"output_cost_per_token": 1.908e-06,
"output_cost_per_token_thinking": 3.816e-06,
"source": "https://help.aliyun.com/en/model-studio/deepseek-v4-pro",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"dashscope/glm-5.1": {
"cache_read_input_token_cost": 2.6e-07,
"input_cost_per_token": 1.4e-06,
@ -13863,6 +13900,40 @@
"supports_tool_choice": true,
"supports_vision": true
},
"dashscope/kimi-k3": {
"cache_read_input_token_cost": 2.83e-07,
"input_cost_per_token": 2.827e-06,
"litellm_provider": "dashscope",
"max_input_tokens": 1048576,
"max_output_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 1.4133e-05,
"source": "https://www.alibabacloud.com/help/en/model-studio/kimi-k3",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"dashscope/kimi/kimi-k3": {
"cache_read_input_token_cost": 2.83e-07,
"input_cost_per_token": 2.827e-06,
"litellm_provider": "dashscope",
"max_input_tokens": 1048576,
"max_output_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 1.4133e-05,
"source": "https://www.alibabacloud.com/help/en/model-studio/kimi-k3",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"dashscope/qwen-coder": {
"input_cost_per_token": 3e-07,
"litellm_provider": "dashscope",

View file

@ -222,6 +222,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
cache_read_input_token_cost_above_272k_tokens_priority: float | None
cache_read_input_token_cost_above_272k_tokens_flex: float | None
cache_read_input_token_cost_above_512k_tokens: float | None
cache_read_input_token_cost_thinking: ReadOnly[float | None] # DashScope dual-mode models: cache-hit rate for thinking-mode requests
# Smallest prefix this model will actually cache, whatever caching mechanism its provider uses.
# Absent means the provider-agnostic default applies; see MINIMUM_PROMPT_CACHE_TOKEN_COUNT.
prompt_cache_min_tokens: int | None
@ -234,6 +235,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
input_cost_per_token_above_272k_tokens_priority: float | None
input_cost_per_token_above_272k_tokens_flex: float | None
input_cost_per_token_above_512k_tokens: float | None # MiniMax-M3: prompts >512K priced at 2x input
input_cost_per_token_thinking: ReadOnly[float | None] # DashScope dual-mode models: input rate for thinking-mode requests
input_cost_per_character_above_128k_tokens: float | None # only for vertex ai models
input_cost_per_query: float | None # only for rerank models
input_cost_per_image: float | None # only for vertex ai models
@ -274,6 +276,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
output_cost_per_reasoning_token: float | None
output_cost_per_reasoning_token_flex: float | None
output_cost_per_reasoning_token_priority: float | None
output_cost_per_token_thinking: ReadOnly[float | None] # DashScope dual-mode models: output rate for thinking-mode requests
output_cost_per_video_per_second: float | None # only for vertex ai models
output_cost_per_audio_per_second: float | None # only for vertex ai models
output_cost_per_second: float | None # for OpenAI Speech models
@ -3453,6 +3456,9 @@ class CustomPricingLiteLLMParams(MirroredPricingParams):
output_cost_per_reasoning_token: float | None = None
output_cost_per_reasoning_token_flex: float | None = None
output_cost_per_reasoning_token_priority: float | None = None
output_cost_per_token_thinking: float | None = None
input_cost_per_token_thinking: float | None = None
cache_read_input_token_cost_thinking: float | None = None
output_cost_per_video_per_second: float | None = None
output_cost_per_audio_per_second: float | None = None
search_context_cost_per_query: dict[str, Any] | None = None

View file

@ -5854,6 +5854,7 @@ def _get_model_info_helper(
cache_read_input_token_cost_above_512k_tokens=_model_info.get(
"cache_read_input_token_cost_above_512k_tokens", None
),
cache_read_input_token_cost_thinking=_model_info.get("cache_read_input_token_cost_thinking", None),
cache_read_input_token_cost_flex=_model_info.get("cache_read_input_token_cost_flex", None),
cache_read_input_token_cost_priority=_model_info.get("cache_read_input_token_cost_priority", None),
cache_read_input_token_cost_ultrafast=_model_info.get("cache_read_input_token_cost_ultrafast", None),
@ -5874,6 +5875,7 @@ def _get_model_info_helper(
"input_cost_per_token_above_272k_tokens_flex", None
),
input_cost_per_token_above_512k_tokens=_model_info.get("input_cost_per_token_above_512k_tokens", None),
input_cost_per_token_thinking=_model_info.get("input_cost_per_token_thinking", None),
input_cost_per_query=_model_info.get("input_cost_per_query", None),
input_cost_per_second=_model_info.get("input_cost_per_second", None),
input_cost_per_audio_token=_model_info.get("input_cost_per_audio_token", None),
@ -5899,6 +5901,7 @@ def _get_model_info_helper(
output_cost_per_character=_model_info.get("output_cost_per_character", None),
output_cost_per_reasoning_token=_model_info.get("output_cost_per_reasoning_token", None),
output_cost_per_reasoning_token_flex=_model_info.get("output_cost_per_reasoning_token_flex", None),
output_cost_per_token_thinking=_model_info.get("output_cost_per_token_thinking", None),
output_cost_per_reasoning_token_priority=_model_info.get(
"output_cost_per_reasoning_token_priority", None
),

View file

@ -13766,6 +13766,23 @@
"supports_system_messages": true,
"supports_tool_choice": false
},
"dashscope/MiniMax/MiniMax-M3": {
"cache_read_input_token_cost": 1.19e-07,
"input_cost_per_token": 5.94e-07,
"litellm_provider": "dashscope",
"max_input_tokens": 1048576,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 2.374e-06,
"source": "https://help.aliyun.com/en/model-studio/minimax-m3",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true,
"supports_video_input": true,
"supports_vision": true
},
"dashscope/deepseek-v4-flash": {
"cache_read_input_token_cost": 4e-08,
"input_cost_per_token": 2e-07,
@ -13814,6 +13831,26 @@
"supports_response_schema": true,
"supports_tool_choice": true
},
"dashscope/deepseek-v4-pro-0813": {
"cache_read_input_token_cost": 6.4e-08,
"cache_read_input_token_cost_thinking": 1.27e-07,
"input_cost_per_token": 6.36e-07,
"input_cost_per_token_thinking": 1.272e-06,
"litellm_provider": "dashscope",
"max_input_tokens": 1000000,
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"output_cost_per_reasoning_token": 3.816e-06,
"output_cost_per_token": 1.908e-06,
"output_cost_per_token_thinking": 3.816e-06,
"source": "https://help.aliyun.com/en/model-studio/deepseek-v4-pro",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"dashscope/glm-5.1": {
"cache_read_input_token_cost": 2.6e-07,
"input_cost_per_token": 1.4e-06,
@ -13863,6 +13900,40 @@
"supports_tool_choice": true,
"supports_vision": true
},
"dashscope/kimi-k3": {
"cache_read_input_token_cost": 2.83e-07,
"input_cost_per_token": 2.827e-06,
"litellm_provider": "dashscope",
"max_input_tokens": 1048576,
"max_output_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 1.4133e-05,
"source": "https://www.alibabacloud.com/help/en/model-studio/kimi-k3",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"dashscope/kimi/kimi-k3": {
"cache_read_input_token_cost": 2.83e-07,
"input_cost_per_token": 2.827e-06,
"litellm_provider": "dashscope",
"max_input_tokens": 1048576,
"max_output_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 1.4133e-05,
"source": "https://www.alibabacloud.com/help/en/model-studio/kimi-k3",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"dashscope/qwen-coder": {
"input_cost_per_token": 3e-07,
"litellm_provider": "dashscope",

View file

@ -14,7 +14,6 @@ import os
import pytest
# Add the project root to Python path
import litellm
from litellm.llms.dashscope.cost_calculator import (
cost_per_token as dashscope_cost_per_token,
@ -526,3 +525,81 @@ class TestDashscopeCostCalculator:
assert prompt_cost == 0.0
assert math.isclose(completion_cost, 500 * 1.6e-06, rel_tol=1e-10)
def test_dashscope_thinking_mode_bills_thinking_rates(self):
"""
deepseek-v4-pro-0813 prices thinking-mode requests at their own input, cache-hit,
and output rates. A request whose usage carries reasoning tokens must bill every
token at those rates, not the cheaper non-thinking ones.
"""
usage = Usage(
prompt_tokens=1000,
completion_tokens=500,
prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=200),
completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=300),
)
prompt_cost, completion_cost = dashscope_cost_per_token(
model="deepseek-v4-pro-0813", usage=usage
)
model_info = litellm.get_model_info("dashscope/deepseek-v4-pro-0813")
expected_prompt_cost = (
800 * model_info["input_cost_per_token_thinking"]
+ 200 * model_info["cache_read_input_token_cost_thinking"]
)
expected_completion_cost = 500 * model_info["output_cost_per_token_thinking"]
assert model_info["input_cost_per_token_thinking"] > model_info["input_cost_per_token"]
assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-10)
assert math.isclose(completion_cost, expected_completion_cost, rel_tol=1e-10)
def test_dashscope_non_thinking_request_bills_base_rates(self):
"""
The same dual-mode model without reasoning tokens in the usage stays on the
non-thinking rates.
"""
usage = Usage(
prompt_tokens=1000,
completion_tokens=500,
prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=200),
)
prompt_cost, completion_cost = dashscope_cost_per_token(
model="deepseek-v4-pro-0813", usage=usage
)
model_info = litellm.get_model_info("dashscope/deepseek-v4-pro-0813")
expected_prompt_cost = (
800 * model_info["input_cost_per_token"]
+ 200 * model_info["cache_read_input_token_cost"]
)
expected_completion_cost = 500 * model_info["output_cost_per_token"]
assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-10)
assert math.isclose(completion_cost, expected_completion_cost, rel_tol=1e-10)
def test_dashscope_zero_thinking_rate_bills_thinking_input_free(self):
"""
An explicit zero thinking rate is a price, not a missing value: it must override
the base rate rather than fall through to it.
"""
litellm.model_cost["dashscope/qwen-zero-thinking-test"] = {
"litellm_provider": "dashscope",
"mode": "chat",
"input_cost_per_token": 4e-07,
"input_cost_per_token_thinking": 0,
"output_cost_per_token": 1.6e-06,
}
usage = Usage(
prompt_tokens=1000,
completion_tokens=500,
completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=150),
)
prompt_cost, completion_cost = dashscope_cost_per_token(
model="qwen-zero-thinking-test", usage=usage
)
assert prompt_cost == 0.0
assert math.isclose(completion_cost, 500 * 1.6e-06, rel_tol=1e-10)