fix(cost): pass service_tier through azure and azure_ai cost calculation (#24926)

service_tier (priority/flex) was not forwarded to generic_cost_per_token
for azure and azure_ai providers, so tier-specific pricing was ignored
and standard pricing was always returned. Other providers (openai,
bedrock, gemini, vertex_ai) already pass it correctly.
This commit is contained in:
michelligabriele 2026-04-02 05:08:57 +02:00 committed by Sameer Kankute
parent 49ec6aba80
commit 1b6914d44c
No known key found for this signature in database
5 changed files with 131 additions and 2 deletions

View file

@ -513,7 +513,8 @@ def cost_per_token( # noqa: PLR0915
return fireworks_ai_cost_per_token(model=model, usage=usage_block)
elif custom_llm_provider == "azure":
return azure_openai_cost_per_token(
model=model, usage=usage_block, response_time_ms=response_time_ms
model=model, usage=usage_block, response_time_ms=response_time_ms,
service_tier=service_tier,
)
elif custom_llm_provider == "gemini":
return gemini_cost_per_token(
@ -539,6 +540,7 @@ def cost_per_token( # noqa: PLR0915
usage=usage_block,
response_time_ms=response_time_ms,
request_model=request_model,
service_tier=service_tier,
)
else:
model_info = _cached_get_model_info_helper(

View file

@ -12,7 +12,8 @@ from litellm.utils import get_model_info
def cost_per_token(
model: str, usage: Usage, response_time_ms: Optional[float] = 0.0
model: str, usage: Usage, response_time_ms: Optional[float] = 0.0,
service_tier: Optional[str] = None,
) -> Tuple[float, float]:
"""
Calculates the cost per token for a given model, prompt tokens, and completion tokens.
@ -47,4 +48,5 @@ def cost_per_token(
model=model,
usage=usage,
custom_llm_provider="azure",
service_tier=service_tier,
)

View file

@ -65,6 +65,7 @@ def cost_per_token(
usage: Usage,
response_time_ms: Optional[float] = 0.0,
request_model: Optional[str] = None,
service_tier: Optional[str] = None,
) -> Tuple[float, float]:
"""
Calculate the cost per token for Azure AI models.
@ -102,6 +103,7 @@ def cost_per_token(
model=model,
usage=usage,
custom_llm_provider="azure_ai",
service_tier=service_tier,
)
except Exception as e:
# For Model Router, the model name (e.g., "azure-model-router") may not be in the cost map

View file

@ -0,0 +1,75 @@
"""
Test Azure OpenAI cost calculator service_tier pricing.
"""
import pytest
import litellm
from litellm.llms.azure.cost_calculation import cost_per_token
from litellm.types.utils import Usage
# Register a test model with tier-specific pricing
TEST_MODEL = "test-azure-gpt-4.1"
TEST_MODEL_COST = {
TEST_MODEL: {
"input_cost_per_token": 0.001,
"output_cost_per_token": 0.002,
"input_cost_per_token_priority": 0.01,
"output_cost_per_token_priority": 0.02,
"input_cost_per_token_flex": 0.0005,
"output_cost_per_token_flex": 0.001,
"litellm_provider": "azure",
"max_tokens": 8192,
}
}
class TestAzureServiceTierCostCalculation:
"""Test that service_tier is passed through Azure cost calculation."""
@pytest.fixture(autouse=True)
def register_test_model(self):
litellm.register_model(model_cost=TEST_MODEL_COST)
def test_service_tier_priority_higher_cost(self):
"""Priority tier should cost more than standard."""
usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500)
standard_prompt, standard_completion = cost_per_token(
model=TEST_MODEL, usage=usage
)
priority_prompt, priority_completion = cost_per_token(
model=TEST_MODEL, usage=usage, service_tier="priority"
)
assert priority_prompt > standard_prompt
assert priority_completion > standard_completion
def test_service_tier_flex_lower_cost(self):
"""Flex tier should cost less than standard."""
usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500)
standard_prompt, standard_completion = cost_per_token(
model=TEST_MODEL, usage=usage
)
flex_prompt, flex_completion = cost_per_token(
model=TEST_MODEL, usage=usage, service_tier="flex"
)
assert flex_prompt < standard_prompt
assert flex_completion < standard_completion
def test_service_tier_none_returns_standard(self):
"""service_tier=None should return standard pricing."""
usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500)
none_prompt, none_completion = cost_per_token(
model=TEST_MODEL, usage=usage, service_tier=None
)
standard_prompt, standard_completion = cost_per_token(
model=TEST_MODEL, usage=usage, service_tier="standard"
)
assert abs(none_prompt - standard_prompt) < 1e-10
assert abs(none_completion - standard_completion) < 1e-10

View file

@ -451,3 +451,51 @@ class TestAzureModelRouterCostBreakdown:
assert logging_obj.cost_breakdown["additional_costs"][
"Azure Model Router Flat Cost"
] == pytest.approx(expected_flat_cost, rel=1e-9)
class TestAzureAIServiceTierCostCalculation:
"""Test that service_tier is passed through Azure AI cost calculation."""
@pytest.fixture(autouse=True)
def register_test_model(self):
import litellm
litellm.register_model(model_cost={
"test-azure-ai-model": {
"input_cost_per_token": 0.001,
"output_cost_per_token": 0.002,
"input_cost_per_token_priority": 0.01,
"output_cost_per_token_priority": 0.02,
"input_cost_per_token_flex": 0.0005,
"output_cost_per_token_flex": 0.001,
"litellm_provider": "azure_ai",
"max_tokens": 8192,
}
})
def test_service_tier_priority_higher_cost(self):
"""Priority tier should cost more than standard for azure_ai."""
usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500)
standard_prompt, standard_completion = cost_per_token(
model="test-azure-ai-model", usage=usage
)
priority_prompt, priority_completion = cost_per_token(
model="test-azure-ai-model", usage=usage, service_tier="priority"
)
assert priority_prompt > standard_prompt
assert priority_completion > standard_completion
def test_service_tier_flex_lower_cost(self):
"""Flex tier should cost less than standard for azure_ai."""
usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500)
standard_prompt, standard_completion = cost_per_token(
model="test-azure-ai-model", usage=usage
)
flex_prompt, flex_completion = cost_per_token(
model="test-azure-ai-model", usage=usage, service_tier="flex"
)
assert flex_prompt < standard_prompt
assert flex_completion < standard_completion