mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-07 08:24:52 +00:00
fix: address Greptile review — empty recipients guard, type annotation, task pre-filter
- Guard empty recipients in _handle_multi_threshold_max_budget_alert: log warning and skip instead of falling through to old path error loop - Widen max_budget_alert_emails type to Dict[str, Union[str, List[str]]] to match _parse_email_list runtime behavior (accepts comma-separated strings) - Pre-filter asyncio.create_task with min threshold check to avoid unnecessary task allocation on every request when spend is below all configured thresholds
This commit is contained in:
@@ -615,7 +615,14 @@ class BaseEmailLogger(CustomLogger):
|
||||
emails = _parse_email_list(raw_emails)
|
||||
if user_info.user_email:
|
||||
emails.append(user_info.user_email)
|
||||
recipient_emails = list(set(emails)) if emails else None
|
||||
if not emails:
|
||||
verbose_proxy_logger.warning(
|
||||
"No recipients for %d%% threshold on key %s, skipping alert",
|
||||
threshold_pct,
|
||||
_id,
|
||||
)
|
||||
continue
|
||||
recipient_emails = list(set(emails))
|
||||
|
||||
event_message = f"Max Budget Alert - {threshold_pct}% of Maximum Budget Reached"
|
||||
webhook_event = WebhookEvent(
|
||||
|
||||
@@ -3095,7 +3095,7 @@ class CallInfo(LiteLLMPydanticObjectBase):
|
||||
default=None,
|
||||
description="Additional email addresses to send alerts to (e.g., from team metadata)",
|
||||
)
|
||||
max_budget_alert_emails: Optional[Dict[str, List[str]]] = Field(
|
||||
max_budget_alert_emails: Optional[Dict[str, Union[str, List[str]]]] = Field(
|
||||
default=None,
|
||||
description="Map of threshold percentage to email recipients (e.g., {'50': ['a@co.com'], '75': ['a@co.com', 'b@co.com']})",
|
||||
)
|
||||
|
||||
@@ -2969,7 +2969,14 @@ async def _virtual_key_max_budget_alert_check(
|
||||
) or litellm.default_key_max_budget_alert_emails
|
||||
|
||||
if isinstance(alert_email_config, dict) and alert_email_config:
|
||||
# New path: pass the map through, let the email handler decide what to fire
|
||||
# New path: only create task if spend has crossed the lowest threshold
|
||||
min_pct = min(
|
||||
(int(k) for k in alert_email_config if k.isdigit()),
|
||||
default=None,
|
||||
)
|
||||
if min_pct is None or valid_token.spend < valid_token.max_budget * (min_pct / 100.0):
|
||||
return
|
||||
|
||||
call_info = CallInfo(
|
||||
token=valid_token.token,
|
||||
spend=valid_token.spend,
|
||||
|
||||
@@ -1562,7 +1562,7 @@ async def test_virtual_key_max_budget_alert_check_with_multi_threshold_map():
|
||||
}
|
||||
valid_token = UserAPIKeyAuth(
|
||||
token="test-token",
|
||||
spend=30.0,
|
||||
spend=60.0,
|
||||
max_budget=100.0,
|
||||
user_id="test-user",
|
||||
key_alias="test-key",
|
||||
@@ -1670,7 +1670,7 @@ async def test_virtual_key_max_budget_alert_check_global_fallback():
|
||||
}
|
||||
valid_token = UserAPIKeyAuth(
|
||||
token="test-token",
|
||||
spend=30.0,
|
||||
spend=60.0,
|
||||
max_budget=100.0,
|
||||
user_id="test-user",
|
||||
key_alias="test-key",
|
||||
@@ -1709,7 +1709,7 @@ async def test_virtual_key_max_budget_alert_check_per_key_overrides_global():
|
||||
|
||||
valid_token = UserAPIKeyAuth(
|
||||
token="test-token",
|
||||
spend=30.0,
|
||||
spend=60.0,
|
||||
max_budget=100.0,
|
||||
user_id="test-user",
|
||||
key_alias="test-key",
|
||||
|
||||
Reference in New Issue
Block a user