Merge pull request #41059 from BerriAI/litellm_prometheus_pre_call_rate_limit_api_provider

fix(prometheus): label pre-call rate limit failures with the resolved api_provider
This commit is contained in:
Yassin Kortam 2026-09-14 13:02:07 -07:00 committed by GitHub
commit 34fe9f71d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 84 additions and 1 deletions

View file

@ -41,6 +41,7 @@ from litellm.proxy._types import (
LiteLLM_UserTable,
UserAPIKeyAuth,
)
from litellm.proxy.hooks.rate_limiter_utils import PROXY_LLM_PROVIDER_FALLBACK
from litellm.repositories.base_repository import BaseRepository
from litellm.repositories.organization_repository import OrganizationRepository
from litellm.repositories.team_repository import TeamRepository
@ -2581,6 +2582,15 @@ class PrometheusLogger(CustomLogger):
)
return None
@staticmethod
def _extract_api_provider_from_exception(exception: Exception) -> str | None:
if not isinstance(exception, litellm.exceptions.RateLimitError):
return None
llm_provider: Final = exception.llm_provider
if not llm_provider or llm_provider == PROXY_LLM_PROVIDER_FALLBACK:
return None
return llm_provider
async def async_post_call_failure_hook(
self,
request_data: dict,
@ -2616,7 +2626,9 @@ class PrometheusLogger(CustomLogger):
_metadata: Final = request_data.get("metadata", {}) or {}
model_id: Final = _metadata.get("model_info", {}).get("id") or request_data.get("model_info", {}).get("id")
rate_limit_category, rate_limit_type = self._extract_rate_limit_labels(original_exception)
api_provider: Final = self._extract_api_provider_from_request_data(request_data)
api_provider: Final = self._extract_api_provider_from_request_data(
request_data
) or self._extract_api_provider_from_exception(original_exception)
enum_values: Final = UserAPIKeyLabelValues(
end_user=user_api_key_dict.end_user_id,
user=user_api_key_dict.user_id,

View file

@ -716,6 +716,77 @@ async def test_failure_hook_emits_api_provider_value_on_failed_requests_metric()
_clear_prometheus_registry()
async def _failed_requests_api_provider_labels(
request_data: dict[str, object],
original_exception: Exception,
) -> list[str]:
from litellm.integrations.prometheus import PrometheusLogger
from litellm.proxy._types import UserAPIKeyAuth
_clear_prometheus_registry()
try:
await PrometheusLogger().async_post_call_failure_hook(
request_data=request_data,
original_exception=original_exception,
user_api_key_dict=UserAPIKeyAuth(token="tok"),
)
return [
s.labels.get("api_provider")
for s in _collected_samples("litellm_proxy_failed_requests_metric_total")
]
finally:
_clear_prometheus_registry()
@pytest.mark.asyncio
async def test_failure_hook_emits_api_provider_from_pre_call_rate_limit_error_for_router_alias():
"""
Pre-call limiters reject before a deployment lands on request_data and a
router alias cannot be inferred from its name, so the provider the limiter
resolved onto the exception is the only source for the label.
"""
from litellm.exceptions import RateLimitType
from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError
err = ProxyRateLimitError(
detail={"error": "rpm exceeded"},
rate_limit_type=RateLimitType.REQUESTS,
model="openai/gpt-5.4-mini",
llm_provider="openai",
)
assert await _failed_requests_api_provider_labels(
{"model": "team-chat-model", "metadata": {}}, err
) == ["openai"]
@pytest.mark.asyncio
async def test_failure_hook_leaves_api_provider_unset_when_rate_limiter_could_not_resolve_provider():
from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError
err = ProxyRateLimitError(detail={"error": "rpm exceeded"}, model="unknown-alias")
assert await _failed_requests_api_provider_labels(
{"model": "unknown-alias", "metadata": {}}, err
) == ["None"]
@pytest.mark.asyncio
async def test_failure_hook_prefers_request_data_provider_over_exception_provider():
from litellm.exceptions import RateLimitError
err = RateLimitError(message="upstream 429", llm_provider="openai", model="gpt-4o")
assert await _failed_requests_api_provider_labels(
{
"model": "gpt-4o",
"metadata": {},
"litellm_params": {"custom_llm_provider": "azure"},
},
err,
) == ["azure"]
if __name__ == "__main__":
test_user_email_in_required_metrics()
test_user_email_label_exists()