fix(guardrails): keep not_run entries out of daily evaluation counts

This commit is contained in:
Yucheng Zhu 2026-09-01 00:20:14 -07:00
parent 0e6c4279b6
commit 527c36343f
2 changed files with 43 additions and 9 deletions

View file

@ -316,15 +316,18 @@ async def process_spend_logs_guardrail_usage(
guardrail_id = entry.get("guardrail_id") or entry.get("guardrail_name") or ""
if not guardrail_id:
continue
key = _MetricsKey(guardrail_id, date_key)
daily_guardrail[key]["requests_evaluated"] += 1
action = _guardrail_status_to_action(entry.get("guardrail_status"))
if action == "passed":
daily_guardrail[key]["passed_count"] += 1
elif action == "blocked":
daily_guardrail[key]["blocked_count"] += 1
else:
daily_guardrail[key]["flagged_count"] += 1
status = entry.get("guardrail_status")
# not_run means the guardrail never evaluated the request: index it for drill-down, keep it out of counts
if status != "not_run":
key = _MetricsKey(guardrail_id, date_key)
daily_guardrail[key]["requests_evaluated"] += 1
action = _guardrail_status_to_action(status)
if action == "passed":
daily_guardrail[key]["passed_count"] += 1
elif action == "blocked":
daily_guardrail[key]["blocked_count"] += 1
else:
daily_guardrail[key]["flagged_count"] += 1
policy_id = entry.get("policy_id")
index_rows.append(
{

View file

@ -306,6 +306,37 @@ async def test_zero_and_non_int_usage_counters_are_skipped():
}
@pytest.mark.asyncio
async def test_not_run_entries_are_indexed_but_not_counted_as_evaluations():
"""
LIT-6314 records a not_run entry when message scoping leaves a guardrail
nothing to scan. The guardrail never evaluated the request, so counting it
as a passed evaluation would inflate daily pass rates; it still gets an
index row so per-request drill-down finds the spend log.
"""
prisma = _prisma()
logs = [_payload("r1", guardrail_status="not_run"), _payload("r2")]
await process_spend_logs_guardrail_usage(prisma, logs)
metrics_create = prisma.db.litellm_dailyguardrailmetrics.upsert.call_args.kwargs["data"]["create"]
assert metrics_create["requests_evaluated"] == 1
assert metrics_create["passed_count"] == 1
index_rows = prisma.db.litellm_spendlogguardrailindex.create_many.call_args.kwargs["data"]
assert sorted(row["request_id"] for row in index_rows) == ["r1", "r2"]
@pytest.mark.asyncio
async def test_batch_of_only_not_run_entries_writes_no_metrics_row():
prisma = _prisma()
await process_spend_logs_guardrail_usage(prisma, [_payload("r1", guardrail_status="not_run")])
assert prisma.db.litellm_dailyguardrailmetrics.upsert.call_count == 0
index_rows = prisma.db.litellm_spendlogguardrailindex.create_many.call_args.kwargs["data"]
assert [row["request_id"] for row in index_rows] == ["r1"]
@pytest.mark.asyncio
async def test_payload_without_request_id_is_skipped_like_the_metrics_path():
prisma = _prisma()