Repository navigation
feat(guardrails): bound every guardrail check with litellm_params.timeout - #43648
devin-ai-integration[bot] wants to merge 7 commits into
Conversation
…eout Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
|
… timeout docs Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…rd fail-open skips Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ount timeouts in guardrail errors Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…st-MCP fail-open regression Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…d of the proxy hook timeout Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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 e9dbe27. Configure here.
|
Admin UI Playground on gpt-5.4-mini with a moderation sink stalled for 30s and Base db9c307 fail-closed answers after 31.01s. Head e9dbe27 fail-open answers in 2.80s, and fail-closed shows the 408, delayed to 11.55s by the SDK's default retries
|
TLDR
Problem this solves:
timeoutin guardraillitellm_paramsis ignored by most built-in guardrailsHow it solves it:
timeoutunreachable_fallback: fail_openstatus="timeout"in guardrail metrics and counts toward the guardrail errors counter, whether the request was blocked or let throughTimeoutErrora guardrail raises itself, before the limit, is still treated as a normal guardrail error, sofail_openonly applies when the proxy-side limit runs outcustom_codekeeps its own execution timeout, so it still returns the 500exceeded its Ns execution timeoutit returns on main. A guardrail class opts out of the proxy limit withenforces_own_timeout = TrueThis replaces the provider-by-provider approach in #43134, which threads an HTTP timeout through about 74 files. The value lives in a new
hook_timeoutattribute that the registry sets fromlitellm_params.timeout, so it never touches theself.timeoutthat several providers already use for their own HTTP clientUser Flow
Before: when any guardrail's vendor hangs during an outage, every chat request waits with it. The steps use
openai_moderation, but it's the same for every built-in or custom guardrailtimeout: 2on a guardrail, hereopenai_moderation, and restarts the proxyAfter: the same request is cut off at the configured limit, whichever guardrail is hanging
timeout: 2on a guardrail, hereopenai_moderation, and restarts the proxyGuardrail '<name>' did not finish within 2.0s, or withunreachable_fallback: fail_openit returns the model's normal 200 answerLinear ticket
Resolves LIT-8333
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Setup: a local sink on 127.0.0.1:9911 answers
/v1/moderationsafter sleeping 30s to simulate a hung vendor. The chat model is the realopenai/gpt-5.4-mini. Both proxies use this configRequest, with
<guardrail>set per caseBefore (db9c307)
fail_closed
moderation-fail-closedtimeout: 2is ignored and the request waits for the sink:HTTP 200 in 30.876394sfail_open
moderation-fail-openHTTP 200 in 30.733296sAfter (dbc1583)
fail_closed
moderation-fail-closedfail_open
moderation-fail-openIntegration test
tests/integration/observability/test_guardrail_timeout.pyruns a real proxy with owned upstreams for a stalled, a failing and a healthy moderation vendor, plussingulrand a spinningcustom_codeguardrailsingulrreturns 408 on both sides, andcustom_codereturns the same 500 on both sidesUnit mutations, 15 of 15 killed and green on restore: no wall-clock bound, fail_open ignored, vendor
TimeoutErrortreated as the deadline, Prometheus timeout not counted as an error, registry drops the timeout or the fallback, registry ignoresenforces_own_timeout, pipeline pre_call unbounded, during_call and post_call unbounded, metrics wrapper and pre_call status nottimeout, error branch overwrites the timeout status, and post_mcp fail-open returnsNoneType
🆕 New Feature
Caveats (if any)
Medium
timeoutnow also cap the whole checkunreachable_fallback: fail_opennow also skips a guardrail that times outcustom_codeand any subclass settingenforces_own_timeoutignoreunreachable_fallbackon timeouttimeoutbefore seeing the errorapply_guardrailcalls and vector-store message scans are not bounded yetpost_mcp_call, and policy pipeline steps areLow
Final Attestation
REVIEWER MUST KNOW BEFORE APPROVING
timeoutset is now cut off at that limit, including policy pipeline stepstimeoutand waited for the vendor,HTTP 200 in 30.88sagainst a 30s stallHTTP 408 in 2.07swithGuardrail '<name>' did not finish within 2.0s. A config that already carries a stale lowtimeoutnow cuts off checks that used to finish, including ones that retry or make several callsunreachable_fallback: fail_opennow lets a timed-out request through for every guardrail, and a timed-outpost_mcp_callreturns the unscanned tool resultopenai_moderationand most guardrails ignored it,HTTP 200 in 30.73safter the full stallHTTP 200 in 3.11sstatus="timeout"and count towardlitellm_guardrail_errors_totaltimeoutstatus, and a guardrail exception was recorded asstatus="error"status="timeout"for both fail_closed and fail_open, so blocked and let-through timeouts share one labelsingulr, and any guardrail whose own HTTP call uses the sametimeout, now returns the proxy's timeout messageHTTP 408 in 2.05swithlitellm.Timeout: Connection timed out. Timeout passed=2.0, time taken=2.002 secondsHTTP 408 in 2.04swithlitellm.Timeout: Guardrail 'singulr-open-on-error' did not finish within 2.0s, and the metric status moves fromerrortotimeoutfail_opencan hand a partly edited request to the modelcustom_codeis exempt and does not change:HTTP 500withexceeded its 2s execution timeouton both legs, and it still ignoresfail_openon timeoutLink to Devin session: https://app.devin.ai/sessions/ddf36d5cf5434de19370ceb10b5249b2
Open in Devin Desktop: https://app.devin.ai/desktop/session/ddf36d5cf5434de19370ceb10b5249b2?variant=devin
Requested by: @kerry-berri