Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions litellm/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -9184,10 +9184,27 @@ def get_router_model_info(

## SET MODEL TO 'model=' - if base_model is None + not azure
if custom_llm_provider == "azure" and base_model is None:
verbose_router_logger.error(
"Could not identify azure model '%s'. Set azure 'base_model' for accurate max tokens, cost tracking, etc.- https://docs.litellm.ai/docs/proxy/cost_tracking#spend-tracking-for-azure-openai-models",
_model,
)
# Router init auto-registers every deployment name into
# litellm.model_cost as a zeroed stub, so membership alone can't
# tell a resolvable name apart; require usable limits/costs.
_azure_fallback_key = _model if _model.startswith("azure/") else f"azure/{_model}"
_fallback_entry = litellm.model_cost.get(_azure_fallback_key)
_fallback_resolves = _fallback_entry is not None and (
(_fallback_entry.get("max_input_tokens") or 0) > 0
or (_fallback_entry.get("max_tokens") or 0) > 0
or (_fallback_entry.get("input_cost_per_token") or 0) > 0
)
if _fallback_resolves:
verbose_router_logger.debug(
"Azure deployment '%s' has no base_model set; using '%s' from the model cost map for max tokens, cost tracking, etc.",
_model,
_azure_fallback_key,
)
else:
verbose_router_logger.error(
"Could not identify azure model '%s'. Set azure 'base_model' for accurate max tokens, cost tracking, etc.- https://docs.litellm.ai/docs/proxy/cost_tracking#spend-tracking-for-azure-openai-models",
_model,
)
elif custom_llm_provider != "azure":
model = _model

Expand Down
82 changes: 82 additions & 0 deletions tests/test_litellm/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -8639,3 +8639,85 @@ async def test_fallback_reentry_with_a_plain_group_clears_the_stale_marker(self)
await router.async_pre_routing_hook(model="gemini-flash", request_kwargs=request_kwargs)

assert AUTO_ROUTED_REQUEST_METADATA_KEY not in request_kwargs["metadata"]


@pytest.mark.usefixtures("local_model_cost_map")
class TestAzureBaseModelFallbackLogging:
"""When an azure deployment has no base_model but its model name is a known
azure key in the cost map, get_router_model_info resolves it via the
fallback, so it must not log the per-request 'Could not identify azure
model' ERROR. The ERROR must remain for genuinely unmappable deployment
names. Issue #33172."""

def _router_with_azure_deployment(self, deployment_model: str):
return litellm.Router(
model_list=[
{
"model_name": "my-group",
"litellm_params": {
"model": deployment_model,
"api_key": "fake-key",
"api_base": "https://fake.openai.azure.com",
},
"model_info": {"id": "azure-base-model-test-id"},
}
]
)

def test_map_known_deployment_name_resolves_without_error_log(self):
router = self._router_with_azure_deployment("azure/gpt-4o")

with patch(
"litellm.router.verbose_router_logger.error"
) as mock_error:
model_info = router.get_router_model_info(
deployment=None, received_model_name="my-group", id="azure-base-model-test-id"
)

assert not any(
"Could not identify azure model" in str(call)
for call in mock_error.call_args_list
), f"unexpected error log: {mock_error.call_args_list}"
# the fallback resolution must actually surface the map values
assert model_info["max_input_tokens"] == litellm.model_cost["azure/gpt-4o"]["max_input_tokens"]
assert model_info["input_cost_per_token"] == litellm.model_cost["azure/gpt-4o"]["input_cost_per_token"]

def test_unmappable_deployment_name_still_logs_error(self):
router = self._router_with_azure_deployment("azure/my-custom-deployment-name")

with patch(
"litellm.router.verbose_router_logger.error"
) as mock_error:
model_info = router.get_router_model_info(
deployment=None, received_model_name="my-group", id="azure-base-model-test-id"
)

assert any(
"Could not identify azure model" in str(call)
for call in mock_error.call_args_list
), "expected the error log for an unmappable azure deployment name"
# unmappable names resolve to a zeroed stub — unchanged behavior
assert model_info.get("max_input_tokens") is None

def test_explicit_base_model_still_wins(self):
router = litellm.Router(
model_list=[
{
"model_name": "my-group",
"litellm_params": {
"model": "azure/some-deployment",
"api_key": "fake-key",
"api_base": "https://fake.openai.azure.com",
},
"model_info": {
"id": "azure-base-model-test-id",
"base_model": "azure/gpt-4o-mini",
},
}
]
)

model_info = router.get_router_model_info(
deployment=None, received_model_name="my-group", id="azure-base-model-test-id"
)
assert model_info["max_input_tokens"] == litellm.model_cost["azure/gpt-4o-mini"]["max_input_tokens"]
Loading