fix(router): don't log 'Could not identify azure model' when the deployment name resolves from the cost map (#37869)

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>
This commit is contained in:
tin-berri 2026-08-21 13:38:28 -07:00 • committed by GitHub
parent 52e181d12d
commit 04113aa2e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 102 additions and 3 deletions

View file

@ -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

View file

@ -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"]