fix: preserve the original evidence for an alert episode

This commit is contained in:
moe-berri 2026-11-05 12:03:14 +08:00 • committed by ishaan-j-berri
parent 491f45d4c5
commit f8e681bf61
2 changed files with 53 additions and 5 deletions

View file

@ -414,11 +414,9 @@ class UsageAlertsRepository:
), updated AS (
UPDATE "LiteLLM_UsageAlertIncident" i SET
evaluation_day=p.day, breached=p.breached,
day=CASE WHEN p.breached THEN p.day ELSE i.day END,
record=(CASE WHEN p.breached THEN p.record || jsonb_build_object(
'id',i.id,'first_seen',i.record->'first_seen',
'activity_path',replace(p.record->>'activity_path',p.record->>'id',i.id)) ELSE i.record END)
|| jsonb_build_object('status',CASE WHEN i.status='open' AND NOT p.breached THEN 'resolved' ELSE i.status END),
record=i.record || jsonb_build_object(
'last_seen',CASE WHEN p.breached THEN p.record->'last_seen' ELSE i.record->'last_seen' END,
'status',CASE WHEN i.status='open' AND NOT p.breached THEN 'resolved' ELSE i.status END),
status=CASE WHEN i.status='open' AND NOT p.breached THEN 'resolved' ELSE i.status END,
updated_at=CURRENT_TIMESTAMP
FROM prior p WHERE i.id=p.prior_id AND p.evaluation_day<p.day AND (NOT p.breached OR p.prior_breached)

View file

@ -152,6 +152,56 @@ async def test_incident_http_read_dismiss_and_scope_authorization(gateway: Gatew
assert gateway.request("DELETE", f"/usage/alert-rules/{rule.id}", key=owner_key).status_code == 204
@pytest.mark.asyncio(loop_scope="session")
async def test_ongoing_episode_preserves_its_original_evidence_and_activity_link(db: ManagementDatabase) -> None:
repository: Final = UsageAlertsRepository.from_prisma(db.prisma)
definition: Final = UsageAlertRuleInput(
name="Original episode", scope_type="user", scope_id="episode-example", metric="requests", direction="increase"
)
rule: Final = await repository.save_rule(definition, "qa-admin")
assert rule is not None
initial: Final = UsageAlertObservation(
entity_id=rule.scope_id,
day=date(2026, 11, 2),
observed=420,
baseline=120,
baseline_sample_days=7,
threshold=360,
breached=True,
)
try:
original: Final = (await repository.record_observations(rule, (initial,)))[0]
edited: Final = await repository.save_rule(
definition.model_copy(update={"name": "Edited rule", "multiplier": 4}), "qa-admin", rule
)
assert edited is not None
later: Final = initial.model_copy(
update={
"day": date(2026, 11, 3),
"observed": 600,
"baseline": 100,
"threshold": 400,
"baseline_sample_days": 4,
}
)
ongoing: Final = (await repository.record_observations(edited, (later,)))[0]
assert ongoing.last_seen >= original.last_seen
assert ongoing.model_copy(update={"last_seen": original.last_seen}) == original
stored: Final = await repository.get_alert(original.id)
assert stored == ongoing
healthy: Final = later.model_copy(update={"day": date(2026, 11, 4), "breached": False})
resolved: Final = (await repository.record_observations(edited, (healthy,)))[0]
assert resolved.model_copy(update={"status": "open", "last_seen": original.last_seen}) == original
repeated: Final = later.model_copy(update={"day": date(2026, 11, 5)})
new_episode: Final = (await repository.record_observations(edited, (repeated,)))[0]
assert new_episode.id != original.id
assert new_episode.day == repeated.day and new_episode.observed_value == 600
assert new_episode.threshold == 400 and new_episode.rule_name == "Edited rule"
assert new_episode.activity_path != original.activity_path
finally:
await repository.delete_rule(rule.id)
@pytest.mark.asyncio(loop_scope="session")
@pytest.mark.parametrize(
"changed_field,new_value",