fix(guardrails): stop reporting a no-op guardrail as applied on passthrough - #34411
Conversation
Greptile SummaryThis PR corrects Headroom guardrail reporting for passthrough and fail-open requests
Confidence Score: 5/5The PR appears safe to merge No blocking failure remains
|
| Filename | Overview |
|---|---|
| litellm/integrations/custom_guardrail.py | Adds the self-logging capability and suppresses synthetic success records when enabled |
| litellm/proxy/guardrails/guardrail_hooks/headroom/headroom.py | Marks Headroom as self-logging and explicitly records successful and fail-open compression attempts |
| litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py | Defers applied-guardrail bookkeeping to guardrails that declare ownership of their execution records |
| tests/test_litellm/integrations/test_custom_guardrail.py | Verifies that self-logging no-op guardrails do not receive synthetic success entries |
| tests/test_litellm/proxy/guardrails/guardrail_hooks/test_headroom.py | Covers Headroom no-op, successful compression, and fail-open reporting behavior |
| tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py | Verifies unified hooks defer applied-guardrail tracking only for self-logging guardrails |
Reviews (3): Last reviewed commit: "fix(guardrails): stop reporting a no-op ..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
cb9b86d to
c43b0b7
Compare
|
Good catch on the fail_open P1, it was a real gap in the design and is fixed in the latest commit. Root cause framing: the Fix: headroom now records a Pinned by |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c43b0b7. Configure here.
…hrough On passthrough requests the shared guardrail plumbing still dispatches headroom's pre_call apply_guardrail, but the passthrough translation hands it only `texts` and no `structured_messages`, so it early-returns a no-op. The @log_guardrail_information decorator then synthesized an "allow"/"success" StandardLoggingGuardrailInformation entry, and the unified hook added the guardrail to applied_guardrails, so spend logs reported the compression guardrail as succeeded even though nothing ran. Add a records_own_guardrail_information flag for guardrails that log their own execution (headroom). The decorator skips the synthetic success entry for them, and the unified hook lists such a guardrail in applied_guardrails only when it actually recorded a run. A guardrail that owns its logging must record every outcome it runs, so headroom now records a guardrail_failed_to_respond entry on the fail_open path (compression attempted, service unreachable, request forwarded uncompressed) instead of leaving it unlogged; fail_closed is still recorded by the decorator's error path, and a genuine no-op stays not_run.
c43b0b7 to
9777e95
Compare
|
Rebased onto current That PR replaced the count-of-recorded-entries self-record check with a concurrency-safe The decorator now reads The Behavior is unchanged from the previous revision and re-verified live: passthrough no-op reports |
TLDR
Problem this solves:
guardrail_status,guardrail_information, andapplied_guardrailsall list a no-op guardrailHow it solves it:
Relevant issues
apply_guardrail, which no-ops because the passthrough translation supplies onlytextsand nostructured_messages@log_guardrail_informationdecorator then synthesized an "allow"/"success" guardrail entry, and the unified hook added the guardrail toapplied_guardrails, so spend logs claimed compression succeedednot_runwith no entryLinear ticket
Resolves LIT-4650
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
Screenshots / Proof of Fix
Config: a
headroomguardrail withdefault_on: trueplus a generic passthrough endpoint. A downstream logger captures theStandardLoggingPayloadthat a Pub/Sub consumer would receive. The compression service is never reached on passthrough because headroom no-ops before calling it.Passthrough request (real Anthropic upstream), identical before and after:
Before, on
litellm_internal_staging(0a4333580f), the spend log falsely reports success:{ "call_type": "pass_through_endpoint", "status_fields.guardrail_status": "success", "metadata.applied_guardrails": ["headroom-compression"], "guardrail_information": [ { "guardrail_name": "headroom-compression", "guardrail_provider": null, "guardrail_mode": "pre_call", "guardrail_response": "allow", "guardrail_status": "success", "duration": 1e-05 } ] }After, on this branch (
cb9b86dff7), the same request reports the guardrail as not run:{ "call_type": "pass_through_endpoint", "status_fields.guardrail_status": "not_run", "metadata.applied_guardrails": null, "guardrail_information": null }The working path is unchanged. A
/v1/messagesrequest where headroom actually compresses (real 1.76ms call to the compression service) still reports the guardrail correctly and still emits thex-litellm-applied-guardrailsheader:{ "call_type": "anthropic_messages", "status_fields.guardrail_status": "success", "metadata.applied_guardrails": ["headroom-compression"], "guardrail_information": [ { "guardrail_name": "headroom-compression", "guardrail_provider": "headroom", "guardrail_mode": "pre_call", "guardrail_response": {"tokens_before": 1000, "tokens_after": 200, "compression_ratio": 0.2}, "guardrail_status": "success", "duration": 0.00176 } ] }A
fail_openattempt (compression service unreachable, request forwarded uncompressed) is reported as a failure, notnot_runand notsuccess, so a consumer can tell compression was attempted and did not happen:{ "call_type": "anthropic_messages", "status_fields.guardrail_status": "guardrail_failed_to_respond", "metadata.applied_guardrails": ["headroom-compression"], "guardrail_information": [ { "guardrail_name": "headroom-compression", "guardrail_provider": "headroom", "guardrail_mode": "pre_call", "guardrail_response": {"error": "headroom compression unavailable; request forwarded uncompressed"}, "guardrail_status": "guardrail_failed_to_respond", "duration": 0.00152 } ] }Type
🐛 Bug Fix
Changes
records_own_guardrail_informationonCustomGuardrail;HeadroomGuardrailsets it since it records its own entry only when it compresses@log_guardrail_informationdecorator skips its synthetic "allow"/"success" entry for such a guardrail, so a no-op early return is not logged as a run; genuine failures are still recordedapplied_guardrails; such a guardrail marks itself only when it actually runs, soHeadroomGuardrailadds itself where it records (on a successful compression and on afail_openfailure) and stays absent on a no-opHeadroomGuardrailrecords aguardrail_failed_to_respondentry on thefail_openpath so an attempted-but-failed compression is logged as a failure rather than droppedFinal Attestation