feat(proxy)!: gate all mock testing request params behind a single config flag - #35423
Conversation
…nfig flag Handling of the client-supplied mock testing params was split across three places with different behavior for each. Three were dropped from every proxy request, two reached the router untouched, and a request that asked for a synthetic failure came back as an ordinary success with nothing to indicate that no failure had been injected Put all six behind one opt-in, general_settings. dangerously_allow_mock_testing_request_params, and reject rather than drop when it is unset, so a fallback drill cannot report a pass for a test that never ran. The rejection names the params it saw and the config key to set, which is also the answer for anyone following the older docs The flag is config-file only. It is deliberately absent from ConfigGeneralSettings, and that absence is what makes /config/update drop it on parse and /config/field/update reject it; the tests pin both so the field cannot be added back for tidiness without the reason surfacing. Enabling it logs a startup warning naming every param it unlocks BREAKING CHANGE: mock_timeout and mock_testing_rate_limit_error now require general_settings.dangerously_allow_mock_testing_request_params to be set in config.yaml. Previously they were accepted unconditionally
…itellm_/mock-testing-feature-flag-6e30ad
Greptile SummaryThe PR introduces a config-file-only gate for six mock-testing request parameters.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/proxy/proxy_server.py | Adds the startup warning and tests that prevent the opt-in from being enabled through configuration APIs. |
| litellm/proxy/route_llm_request.py | Replaces unconditional mock-parameter stripping with a centralized config-controlled rejection or pass-through gate. |
| tests/test_litellm/proxy/test_proxy_server.py | Covers startup warning contents and verifies that management endpoints cannot persist the config-file-only flag. |
| tests/test_litellm/proxy/test_route_llm_request.py | Covers all six gated parameters, default rejection, enabled forwarding, multi-parameter errors, and ordinary requests. |
Reviews (2): Last reviewed commit: "refactor(proxy): drop an inaccurate comm..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The comment said the Responses WebSocket route never runs add_litellm_data_to_request. It does, via common_processing_pre_call_logic, so the note recorded a request-flow constraint that does not hold The gate stays in route_request, which is the dispatch chokepoint and where the previous handling lived
|
@greptile review again and give an updated score |
QA verdict: PASSThe single config flag Everything below hit a live proxy backed by real Anthropic calls (primary Config usedTwo proxies, identical except for the one flag. Disabled proxy on :4020, enabled proxy on :4021 model_list:
- model_name: primary-model
litellm_params:
model: anthropic/claude-haiku-4-5
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: fallback-model
litellm_params:
model: anthropic/claude-sonnet-4-5
api_key: os.environ/ANTHROPIC_API_KEY
general_settings:
master_key: sk-1234
# dangerously_allow_mock_testing_request_params: true # only in the enabled proxyThe config/API paths were exercised against a third proxy on :4030 with Sad path: flag absent, every gated param rejected (:4020)Each of the six is rejected on its own with a 400 that names it and points at the config key. Only the first is shown; the other five are identical in shape All present params are named in one error when several are sent together Edge case: the gate keys on presence, not truthiness, so a param sent with Ordinary traffic is untouched, and the non-gated Happy path: flag enabled (:4021)
Ordinary traffic still returns 200 with the flag on Cannot be set from the Admin UI or the config API
The Admin UI matches: the General tab under Router Settings lists every editable Startup warningAbsent when the flag is off. When on, it names the setting and all six params Unit testsThe PR's own tests pass: Non-blockers (pre-existing, not introduced by this PR)
|
…ends Gating the mock testing request params behind general_settings.dangerously_allow_mock_testing_request_params (#35423) turned every fallback, retry and timeout drill in tests/test_fallbacks.py into a 400: the build_and_test job mounts proxy_server_config.yaml, which never opted in. Opt that config in. It is the config the CI proxy runs with, and the suite it serves exists to drive synthetic failures. Add a unit test that ties the two together: it scans the top-level tests/test_*.py files build_and_test globs for gated param names and fails if the config they run against has not opted in, so the next change to either side is caught in a fast lint-tier job rather than a Docker E2E.

TLDR
Problem this solves:
How it solves it:
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@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
All runs below are against a live proxy at commit
2a9aa966ce, hitting the real Anthropic APIConfig used, two variants differing only by the
general_settingsline:1. Flag not set (the default): gated params are refused
Several at once are all named, so fixing one does not surprise you with the next:
2. Flag not set: ordinary traffic is untouched
No startup warning is emitted in this run
3. Flag set: startup warning
The warning lists every param the flag unlocks, because the key name says
mock_testingwhile the gate also coversmock_timeoutandmock_delay4. Flag set: a forced fallback actually runs
The request named
primary-model(haiku) and was served byclaude-sonnet-4-5, so the fallback chain was exercised for real rather than reported as exercisedType
🆕 New Feature
Changes
The six params are
mock_testing_fallbacks,mock_testing_context_fallbacks,mock_testing_content_policy_fallbacks,mock_testing_rate_limit_error,mock_timeoutandmock_delay. They now share one gate inroute_request, which is where the dispatch chokepoint is; the Responses WebSocket route calls it directly and never runsadd_litellm_data_to_request, so putting the gate with its siblings inlitellm_pre_call_utilswould have missed that routeRejecting rather than dropping is the behavioral change that matters. A dropped param used to produce an ordinary 200 from the real model, which reads as a fallback test that passed when nothing was mocked at all. The 400 names the params it saw and the config key to set, which also answers anyone following the older docs
The flag is config-file only. It is deliberately absent from
ConfigGeneralSettings, and that absence is what makes/config/updatedrop it on parse and/config/field/updatereject it. Two tests pin that behavior so the field cannot be added back as a tidy-up without the reason surfacingmock_responseandmock_tool_callsare unchanged and keep their existing per key or teamallow_client_mock_responseopt-inBreaking change
mock_timeoutandmock_testing_rate_limit_errorwere accepted unconditionally before this and now require the flag. Anyone sending them to a proxy today gets a 400 until an admin sets it in config.yamlFinal Attestation