fix(alerts): stop detecting traffic drops (#51338)

This commit is contained in:
moe-berri 2026-11-12 12:22:24 +08:00 • committed by GitHub
parent 61944def17
commit 6a98d09261
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 26 additions and 44 deletions

View file

@ -17,8 +17,6 @@ SPEND_MULTIPLIER: Final = 3
SPEND_MIN_INCREASE_USD: Final = 50
TRAFFIC_SPIKE_MULTIPLIER: Final = 10
TRAFFIC_SPIKE_MIN_INCREASE: Final = 1000
TRAFFIC_DROP_RATIO: Final = 0.2
TRAFFIC_DROP_MIN_BASELINE: Final = 500
LATENCY_MULTIPLIER: Final = 3
LATENCY_MIN_INCREASE_MS: Final = 500
LATENCY_MIN_TIMED_REQUESTS: Final = 100
@ -45,7 +43,6 @@ class ResourceWindow(BaseModel):
base_timed: int = Field(ge=0, le=MAX_ALERT_ID)
active_days: int = Field(ge=0, le=BASELINE_DAYS)
timed_days: int = Field(ge=0, le=BASELINE_DAYS)
exists: bool
class SweepState(BaseModel):
@ -187,20 +184,14 @@ def _spend_spike(window: ResourceWindow) -> tuple[float, float] | None:
return (window.spend, baseline) if triggered else None
def _traffic_change(window: ResourceWindow) -> tuple[float, float] | None:
def _traffic_spike(window: ResourceWindow) -> tuple[float, float] | None:
baseline: Final = window.base_requests / BASELINE_DAYS
spike: Final = (
window.active_days >= MIN_ACTIVE_DAYS
and window.requests >= TRAFFIC_SPIKE_MULTIPLIER * baseline
and window.requests - baseline >= TRAFFIC_SPIKE_MIN_INCREASE
)
drop: Final = (
window.exists
and window.active_days == BASELINE_DAYS
and baseline >= TRAFFIC_DROP_MIN_BASELINE
and window.requests < TRAFFIC_DROP_RATIO * baseline
)
return (window.requests, baseline) if spike or drop else None
return (window.requests, baseline) if spike else None
def _slower_responses(window: ResourceWindow) -> tuple[float, float] | None:
@ -231,7 +222,7 @@ def _more_failures(window: ResourceWindow) -> tuple[float, float] | None:
def detect(scope: PerformanceScope, window: ResourceWindow, day: date) -> tuple[AlertDetection, ...]:
checks: Final[tuple[tuple[AlertType, tuple[float, float] | None, int], ...]] = (
("spend", _spend_spike(window), window.requests),
("traffic", _traffic_change(window), window.requests),
("traffic", _traffic_spike(window), window.requests),
("latency", _slower_responses(window), window.timed),
("reliability", _more_failures(window), window.requests),
)

View file

@ -80,33 +80,18 @@ class _Source:
resource_id: LiteralString
requests: LiteralString
filter: LiteralString
exists: LiteralString
_SOURCES: Final[Mapping[PerformanceScope, _Source]] = {
"gateway": _Source('"LiteLLM_DailyModelUsage"', "'gateway'", "request_count", "", "true"),
"model": _Source('"LiteLLM_DailyModelUsage"', "model_group", "request_count", "", "true"),
"team": _Source(
'"LiteLLM_DailyTeamSpend"',
"team_id",
"api_requests",
"",
'EXISTS (SELECT 1 FROM "LiteLLM_TeamTable" t WHERE t.team_id = page.resource_id AND NOT t.blocked)',
),
"user": _Source(
'"LiteLLM_DailyUserSpend"',
"user_id",
"api_requests",
"",
'EXISTS (SELECT 1 FROM "LiteLLM_UserTable" u WHERE u.user_id = page.resource_id)',
),
"gateway": _Source('"LiteLLM_DailyModelUsage"', "'gateway'", "request_count", ""),
"model": _Source('"LiteLLM_DailyModelUsage"', "model_group", "request_count", ""),
"team": _Source('"LiteLLM_DailyTeamSpend"', "team_id", "api_requests", ""),
"user": _Source('"LiteLLM_DailyUserSpend"', "user_id", "api_requests", ""),
"key": _Source(
'"LiteLLM_DailyUserSpend"',
"api_key",
"api_requests",
" AND api_key ~ '^[0-9a-f]{64}$'",
'EXISTS (SELECT 1 FROM "LiteLLM_VerificationToken" k WHERE k.token = page.resource_id'
" AND k.blocked IS NOT TRUE AND (k.expires IS NULL OR k.expires > now()))",
),
}
@ -127,8 +112,7 @@ def _page_sql(scope: PerformanceScope, wrapped: bool) -> LiteralString:
if scope == "gateway"
else f" GROUP BY {source.resource_id} ORDER BY {source.resource_id}"
)
return f"""WITH page AS (
SELECT {source.resource_id} AS resource_id,
return f"""SELECT {source.resource_id} AS resource_id,
COALESCE(SUM(spend) FILTER (WHERE date = $1), 0)::float8 AS spend,
COALESCE(SUM(spend) FILTER (WHERE date < $1), 0)::float8 AS base_spend,
COALESCE(SUM({source.requests}) FILTER (WHERE date = $1), 0)::bigint AS requests,
@ -143,9 +127,7 @@ def _page_sql(scope: PerformanceScope, wrapped: bool) -> LiteralString:
COUNT(DISTINCT date) FILTER (WHERE date < $1 AND timed_requests > 0) AS timed_days
FROM {source.table}
WHERE date BETWEEN $2 AND $1{where}{group}
LIMIT $3::int
)
SELECT page.*, {source.exists} AS exists FROM page ORDER BY resource_id"""
LIMIT $3::int"""
def _next_id_sql(scope: PerformanceScope, wrapped: bool) -> LiteralString:

View file

@ -157,6 +157,7 @@ async def test_a_full_sweep_saves_one_alert_per_real_change_and_nothing_for_norm
write_rows('INSERT INTO "LiteLLM_UserTable" (user_id) VALUES (%s)', (f"{p}-user-stopped",))
_seed("user", f"{p}-user-stopped", STEADY, None)
_seed("user", f"{p}-user-deleted", STEADY, None)
_seed("user", f"{p}-user-busy", STEADY, Day(requests=20_000, timed=20_000, response_ms=2_000_000))
_seed("key", fixture.key("slow"), STEADY, Day(response_ms=1_000_000))
_seed("model", f"{p}-model", STEADY, Day(failed=100))
@ -170,7 +171,7 @@ async def test_a_full_sweep_saves_one_alert_per_real_change_and_nothing_for_norm
assert int(str(age["seconds"])) < 300
assert fixture.saved() == {
(f"{p}-team-spike", "spend"): ("team", "open"),
(f"{p}-user-stopped", "traffic"): ("user", "open"),
(f"{p}-user-busy", "traffic"): ("user", "open"),
(fixture.key("slow"), "latency"): ("key", "open"),
(f"{p}-model", "reliability"): ("model", "open"),
}

View file

@ -5,6 +5,8 @@ import pytest
from litellm.models.alert import AlertDetection, MetricEvidence
from litellm.models.performance_alert import (
SCOPES,
PerformanceScope,
ResourceWindow,
SweepState,
advance,
@ -29,7 +31,6 @@ STEADY: Final = {
"base_timed": 7000,
"active_days": 7,
"timed_days": 7,
"exists": True,
}
@ -45,6 +46,19 @@ def test_steady_usage_raises_nothing() -> None:
assert _types() == ()
@pytest.mark.parametrize("scope", SCOPES)
@pytest.mark.parametrize("requests", (0, 100))
def test_quiet_or_idle_resources_do_not_raise_traffic_alerts(scope: PerformanceScope, requests: int) -> None:
window: Final = _window(
resource_id="a" * 64 if scope == "key" else "resource-a",
requests=requests,
failed=0,
response_ms=requests * 100,
timed=requests,
)
assert detect(scope, window, DAY) == ()
@pytest.mark.parametrize(
("changes", "expected"),
(
@ -57,11 +71,6 @@ def test_steady_usage_raises_nothing() -> None:
({"requests": 1100, "failed": 0, "response_ms": 110_000, "timed": 1100, "base_requests": 700}, ("traffic",)),
({"requests": 1099, "failed": 0, "response_ms": 109_900, "timed": 1099, "base_requests": 700}, ()),
({"requests": 50_000, "failed": 0, "active_days": 3}, ()),
({"requests": 99, "failed": 0, "timed": 99, "response_ms": 9900, "base_requests": 3500}, ("traffic",)),
({"requests": 100, "failed": 0, "timed": 100, "response_ms": 10_000, "base_requests": 3500}, ()),
({"requests": 0, "failed": 0, "timed": 0, "response_ms": 0, "base_requests": 3500, "active_days": 6}, ()),
({"requests": 0, "failed": 0, "timed": 0, "response_ms": 0, "base_requests": 3500, "exists": False}, ()),
({"requests": 0, "failed": 0, "timed": 0, "response_ms": 0, "base_requests": 3493}, ()),
({"response_ms": 600_000}, ("latency",)),
({"response_ms": 599_000}, ()),
({"response_ms": 60_000_000, "timed": 99}, ()),

View file

@ -112,7 +112,6 @@ def spend_window() -> Mapping[str, object]:
"base_timed": 0,
"active_days": 7,
"timed_days": 0,
"exists": True,
}