From 04113aa2e9c9ab4ace46a284a6735610e7d3d269 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Fri, 21 Aug 2026 13:38:28 -0700 Subject: [PATCH] fix(router): don't log 'Could not identify azure model' when the deployment name resolves from the cost map (#37869) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(router): don't log 'Could not identify azure model' when the deployment name resolves from the cost map get_router_model_info already falls back to resolving the azure deployment's model name against the model cost map when base_model is unset — and for deployments named after real azure models (e.g. azure/gpt-4o) that resolution returns correct max tokens and costs. The unconditional ERROR was therefore spurious for exactly the deployments that need no operator action, and on busy proxies it logs thousands of times per day per multi-deployment group. Log at debug when the fallback entry carries usable limits/costs (membership alone is not enough: Router init auto-registers every deployment name as a zeroed stub), keep the ERROR otherwise. Fixes #33172 Co-Authored-By: Claude Fable 5 * fix(router): use consistent positive checks in azure base_model fallback gate Review follow-up: token-limit fields used 'is not None' while the cost field used '> 0' — a cost-map entry explicitly storing 0 limits could suppress the error log without carrying usable resolution data. All three checks now require a positive value. Co-Authored-By: Claude Fable 5 * refactor(router): trim fallback gate comment and reuse the shared local_model_cost_map fixture --------- Co-authored-by: Mihidum Hettiyahandi <55163074+mihidumh@users.noreply.github.com> Co-authored-by: Claude Fable 5 --- litellm/router.py | 23 +++++++-- tests/test_litellm/test_router.py | 82 +++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 665a90957a8..7dedbe851d7 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -9184,10 +9184,27 @@ class Router: ## 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 diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index 1d6eb1bc590..58a500def8e 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -8639,3 +8639,85 @@ class TestAutoRoutedRequestMarker: 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"]