litellm/tests/test_litellm/proxy
yucheng-berri 3a31331435
fix(proxy): run pre-call guardrails on batch input file uploads (#37519)
* fix(proxy): run pre-call guardrails on batch input file uploads

POST /v1/files with purpose=batch was the only route in files_endpoints that
never reached pre_call_hook, so guardrails did not see batch content at all and
records reached the provider unscanned.

Stream the uploaded JSONL a record at a time and run each record's body through
the existing pre_call_hook dispatch under the call type its url maps to, so
guardrail resolution, key and team config, and the per-endpoint translations are
reused rather than reimplemented. The hook gains a guardrails_only mode for this,
since the same callback loop also drives rate limiters, budget hooks, prompt
templates and hanging-request alerting, none of which should fire once per record.

A guardrail that blocks raises its own exception, which propagates untouched so
its status code survives. A record a guardrail would rewrite, a record that
cannot be parsed, and a record whose url cannot be scanned all reject the upload,
since silently skipping any of them is the bypass this is meant to close.
Per-record redaction lands separately.

The scan only runs when a guardrail that actually runs pre_call, or a guardrail
pipeline, is configured, so deployments without one are byte for byte unchanged.

* fix(proxy): compare the dict a batch guardrail returns, not the one it was given

async_pre_call_hook may return a replacement dict instead of mutating its input, and
process_pre_call_hook_response then makes that replacement the request. The scan only
inspected the dict it passed in, so a guardrail that redacts by returning a copy was
treated as a no-op and its record uploaded unchanged.

* fix(proxy): treat a missing batch body key as different from a null one

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* docs(proxy): document the guardrails_only mode on pre_call_hook

* fix(proxy): resolve a batch record's scan type from its body when the url is unfamiliar

The scanner only accepted five exact urls, but callers write that field by hand and the
provider transformers are far more permissive: bedrock treats any non-empty url as chat
and vertex strips query strings and trailing slashes. Uploads that work today would have
started failing the moment a pre-call guardrail was configured.

Normalize the url before lookup and fall back to the body shape when it is unfamiliar, so
a record we can still read is a record we still scan. Only a body with no messages, prompt
or input is now refused, and the error says so instead of listing urls that were never the
whole set.

Also pins the default side of the guardrails_only gate: the hanging-request alert and
prompt templating are asserted to still fire when the flag is absent.

* refactor(proxy): drop batch guardrail checks the upload validation already makes

check_batch_file_upload now runs first and rejects a line that does not parse, a line that
is not an object, and a line missing custom_id, method, url or body, so the guardrail scan
can rely on all four. Its own parse handling was unreachable through the endpoint and is
gone, along with the tests for it. What is left is the case that validation does not cover,
a body whose value is not an object, since it only checks that the key is present.

* fix(proxy): resolve a batch record's call type from the url path, not the whole url

A record naming its route in full, which is how callers actually write batch files, matched
no known route, so it fell through to the body shape. A Responses record carries `input`,
and that reads as an embedding, so the record was scanned as the wrong call type and any
guardrail scoped to chat or Responses skipped it while the upload was accepted. Chat records
survived only because their body shape happens to map back to the same call type. The url is
now reduced to its path before matching.

Guardrails that pick their policy from a request header, such as noma choosing an application
id, saw no headers at all during the scan and fell back to a default, so a batch record could
be evaluated under a different policy than the same content sent online. The sanitized headers
the proxy already stores in request metadata now travel with the scan.

Also drops the bare `dict` annotation, the unreachable non-dict branch on the guardrail chain's
own return, and the type alias that was missing its `TypeAlias`, which together were failing
the lint gate.

* fix(proxy): give each batch record its own copy of the scan metadata

The narrowed metadata was handed to every record as a shallow copy, so `headers` and `tags`
stayed shared with the upload request and with the other records in the same window. A guardrail
that writes into one of those in place, which several do to record their own bookkeeping, would
have its write show up in every record scanned after it and in the request itself. The narrowing
already removed the values that cannot be copied, so each record now gets a deep copy.

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-20 12:51:15 -07:00
..
_experimental/mcp_server fix(mcp): answer 503 when a refresh-token burn cannot be recorded 2026-08-20 07:56:38 -07:00
a2a fix(a2a): reject malformed protocolVersion suffixes while keeping semver prereleases 2026-07-21 13:24:58 -07:00
agent_endpoints test: run the 30 test files stranded in the second mirror (#37595) 2026-08-20 10:59:43 -07:00
analytics_endpoints feat(ui): split failed requests into their own series on the cache dashboard (#34862) 2026-07-29 09:48:17 -07:00
anthropic_endpoints fix(proxy): estimate failed-request input tokens on /v1/messages and count system prompts 2026-08-18 14:21:47 -07:00
auth feat(proxy): add POST /auto_router/validate_complexity_router_config to dry-run the complexity-router write gate (#37409) 2026-08-20 19:48:32 +00:00
batches_endpoints Merge pull request #37198 from BerriAI/litellm_lit5660_batches_limit_400 2026-08-17 15:53:46 -07:00
client Merge origin/litellm_internal_staging into litellm_cli_refresh_tokens 2026-08-20 10:54:48 -07:00
common_utils test: run the 30 test files stranded in the second mirror (#37595) 2026-08-20 10:59:43 -07:00
config_resolvers feat(proxy): add SAML 2.0 SSO for the admin UI (#31429) 2026-07-24 12:51:28 -07:00
credential_endpoints fix(proxy): return the real status code when a credential update is rejected (#36166) 2026-08-07 08:02:52 +00:00
db Merge pull request #34887 from RayJueWang/litellm_fix_spend_deadlock_retry 2026-08-19 15:53:45 -07:00
discovery_endpoints test: remove tests that never execute 2026-08-12 10:45:38 -07:00
enterprise_billing feat(proxy): push-based OTLP billable-request metering for enterprise deployments (#31592) 2026-07-15 12:12:52 -07:00
experimental/mcp_server
fine_tuning_endpoints fix(proxy): authenticate managed ids before routing 2026-08-08 02:42:34 -07:00
google_endpoints chore: litellm oss staging160626 (#30527) 2026-06-16 18:23:13 -07:00
guardrails Merge pull request #37387 from BerriAI/litellm_guardrail_usage_requeue 2026-08-18 16:42:06 -07:00
health_endpoints feat(proxy): auto-suppress the no-Redis banner for confirmed single-worker deployments 2026-08-14 17:11:05 -07:00
hooks Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_batch_enqueued_token_limit 2026-08-19 19:31:46 -07:00
image_endpoints fix(proxy)!: share one destination check between body and path-supplied model 2026-08-05 15:17:43 -07:00
logging_endpoints feat(proxy): add POST /v1/callbacks/logs to replay logging payloads through callbacks (#31134) 2026-06-24 15:25:10 -07:00
management_endpoints feat(proxy): add POST /auto_router/validate_complexity_router_config to dry-run the complexity-router write gate (#37409) 2026-08-20 19:48:32 +00:00
management_helpers feat(proxy)!: default audit logs on for enterprise licenses (#37518) 2026-08-19 18:49:21 -07:00
memory fix(proxy): give proxy_admin_viewer read parity with proxy_admin (#35851) 2026-08-05 18:33:55 +00:00
middleware feat(proxy): add Amazon Comprehend Medical passthrough provider 2026-08-17 15:44:06 -07:00
ocr_endpoints fix(ocr): validate body req_format in the proxy endpoint and run its tests in CI 2026-08-17 18:29:35 +00:00
openai_files_endpoint fix(proxy): run pre-call guardrails on batch input file uploads (#37519) 2026-08-20 12:51:15 -07:00
pass_through_endpoints fix: harden vertex live passthrough against client model forms and dict credentials 2026-08-20 03:05:48 -07:00
policy_engine fix(guardrails): scan text on /guardrails/apply_guardrail for Azure Content Safety (#36894) 2026-08-17 11:18:40 -07:00
prompts fix(proxy): give proxy_admin_viewer read parity with proxy_admin (#35851) 2026-08-05 18:33:55 +00:00
proxy_server feat(ui): let admins supply a dark-mode variant of their custom logo (#37662) 2026-08-20 12:48:55 -07:00
public_endpoints fix(ui): distinguish hosted and local vLLM in the provider dropdown 2026-08-14 13:39:37 -07:00
rag_endpoints fix(rag): track LLM completion usage and spend for /v1/rag/query (#32438) 2026-07-17 17:45:27 +00:00
realtime_endpoints fix(realtime): bound Vertex credential resolution and make realtime failures loud 2026-08-20 02:02:00 -07:00
response_api_endpoints fix(responses_api): map bridged chat usage on guardrail-blocked replies 2026-08-14 17:04:27 -07:00
shutdown feat(proxy): native /health/drain preStop hook for graceful shutdown (#29439) 2026-06-02 16:30:44 -07:00
spend_tracking fix(ptu): hand the prune a plain delete filter the query builder can serialise (#37571) 2026-08-19 22:18:58 -07:00
test_configs
types_utils fix(proxy): resolve router_settings.plugins dotted paths and load plugins from installed packages (#33644) 2026-07-17 11:23:18 -07:00
ui_crud_endpoints feat(ui): let admins supply a dark-mode variant of their custom logo (#37662) 2026-08-20 12:48:55 -07:00
utils fix(proxy): run pre-call guardrails on batch input file uploads (#37519) 2026-08-20 12:51:15 -07:00
vector_store_endpoints test: run the 30 test files stranded in the second mirror (#37595) 2026-08-20 10:59:43 -07:00
vector_store_files_endpoints fix(proxy): authenticate managed ids before routing 2026-08-08 02:42:34 -07:00
video_endpoints test(videos): add 1:1 test file scaffold for videos component paths (#30631) 2026-06-29 09:12:51 +05:30
__init__.py
conftest.py test(proxy): stop monkeypatch.undo re-planting fixture-mocked prisma_client 2026-08-13 20:11:06 -07:00
test_aiohttp_cleanup_closed.py
test_aiohttp_session_recovery.py
test_api_key_masking_in_errors.py
test_audio_speech_prometheus_hooks.py fix(proxy): extend response headers hook to streaming, TTS, image gen, and pass-through (#24232) 2026-06-09 22:10:23 +02:00
test_batch_expiry.py fix(proxy): strip LiteLLM policy tracking from OpenAI batch metadata (#28425) 2026-05-26 11:35:42 -07:00
test_batch_metadata_none_fix.py
test_batch_retrieve_bedrock.py
test_batch_x_litellm_model_encoding.py test(proxy): give the x-litellm-model fallback test deterministic openai env creds 2026-08-17 12:42:57 -07:00
test_blocked_response_usage.py fix(responses_api): map bridged chat usage on guardrail-blocked replies 2026-08-14 17:04:27 -07:00
test_budget_reservation.py feat(proxy): add apply_user_budget_to_team_keys opt-in (#36102) 2026-08-07 15:40:13 +00:00
test_caching_routes.py Litellm oss staging 040626 (#29671) 2026-06-04 11:07:20 -07:00
test_chat_completion_metadata.py
test_claude_code_marketplace.py test: run the 30 test files stranded in the second mirror (#37595) 2026-08-20 10:59:43 -07:00
test_common_request_processing.py fix(proxy): send SSE keepalives while a slow upstream is still silent (#37322) 2026-08-18 14:43:01 -07:00
test_component_allowlists.py fix(gateway): keep the Prometheus /metrics Mount in the gateway route trim (#32317) 2026-07-07 18:36:38 +03:00
test_conftest.py test(proxy): stop monkeypatch.undo re-planting fixture-mocked prisma_client 2026-08-13 20:11:06 -07:00
test_cors_config.py
test_custom_proxy.py
test_dynamic_mcp_route.py fix(proxy): serve aggregate MCP endpoint on bare /mcp instead of 307-redirecting (#34845) 2026-08-14 17:04:32 -07:00
test_empty_model_list.py
test_enforce_user_param.py
test_fallback_management_endpoints.py
test_fastapi_offline_routes.py
test_filter_models_by_team_access_group.py fix: resolve access group names in _filter_models_by_team_id (#25224) 2026-05-01 11:55:36 +05:30
test_health_check_functions.py feat(proxy): skip disable_background_health_check models on GET /health when flag set (#27716) 2026-05-13 09:49:05 -07:00
test_health_check_max_tokens.py fix: skip health check for semantic auto_router deployments (#31668) 2026-06-29 19:47:20 -07:00
test_init_litellm_callbacks.py test: run the 30 test files stranded in the second mirror (#37595) 2026-08-20 10:59:43 -07:00
test_langfuse_passthrough_security.py chore(callbacks): guard dynamic integration hosts 2026-04-30 14:27:19 -07:00
test_lazy_openapi_snapshot.py feat(proxy): add GET /v1/indexes to list vector store indexes (#36289) 2026-08-10 15:09:59 -07:00
test_litellm_pre_call_utils.py fix(proxy): stop leaking the client_side_timeout marker to providers 2026-08-18 13:37:07 -07:00
test_max_budget_env_var.py
test_mcp_asgi_response.py fix(mcp): surface upstream 401 for token-forwarding MCP servers (#27847) 2026-05-13 12:03:36 -07:00
test_model_based_routing_files_batches.py test(batches): move orphan tests into tests/test_litellm for CI coverage (#30510) 2026-06-16 10:20:59 -07:00
test_model_deprecations_endpoint.py refactor(proxy): tighten model deprecation typing and cover the endpoint 2026-08-10 22:58:35 +00:00
test_model_dump_with_preserved_fields.py
test_model_id_header_propagation.py feat(proxy): add x-litellm-model-name response header with deployment model string (#33698) 2026-07-17 20:29:42 -07:00
test_model_info_default_limits.py fix(proxy): align /v1/model/info with router deployments (#30025) 2026-06-10 19:38:21 -07:00
test_model_level_guardrails.py fix(proxy): apply key/team router_settings.model_group_alias (#35486) 2026-08-03 22:09:47 +00:00
test_model_list_healthy_only.py feat: litellm oss 110626 (#30202) 2026-06-11 22:30:26 -07:00
test_modify_response_streaming_passthrough.py fix(proxy): use e.request_data for logging_obj in ModifyResponseException streaming passthrough (#30800) 2026-06-18 23:29:08 -07:00
test_openai_ws_passthrough_routes.py fix(proxy): close websocket cleanly when OpenAI credentials are missing 2026-08-16 14:40:37 -07:00
test_openapi_schema_validation.py fix(proxy): preserve HTTP operations when injecting WebSocket stubs into OpenAPI schema 2026-05-06 00:28:42 +02:00
test_plugin_routes.py feat: litellm plugin architecture v2 (#30688) 2026-06-20 20:37:22 -07:00
test_pricing_field_strip.py fix(guardrails): bill all chunks on mid-chunking block, strip client guardrail cost metadata, add cost map schema keys 2026-08-18 14:52:23 -07:00
test_prisma_engine_watchdog.py test: run the 30 test files stranded in the second mirror (#37595) 2026-08-20 10:59:43 -07:00
test_prometheus_cleanup.py
test_provider_url_destination_guard.py fix(proxy)!: share one destination check between body and path-supplied model 2026-08-05 15:17:43 -07:00
test_proxy_cli.py feat(proxy): let USE_V2_MIGRATION_RESOLVER select the v2 migration resolver 2026-08-07 22:18:59 -07:00
test_proxy_logging_hook_detection.py fix(guardrails): scan text on /guardrails/apply_guardrail for Azure Content Safety (#36894) 2026-08-17 11:18:40 -07:00
test_proxy_server.py fix: gate guardrail read-through to active rows and serialize it with the reload reconcile 2026-08-19 01:46:33 -07:00
test_proxy_types.py feat(proxy): add project-level ITPM and OTPM quotas 2026-08-05 12:53:22 +05:30
test_proxy_utils.py fix(proxy): strip client standard_logging_object before the failure logging handler 2026-08-19 16:53:02 -07:00
test_pyroscope.py Implement normalize_nonempty_secret_str function to trim whitespace from secrets and treat empty values as unset. Update proxy_server to use this function for Grafana credentials. Enhance tests to validate the new normalization behavior. 2026-05-04 18:17:31 +00:00
test_read_model_list.py feat: add minimal rust router + axum ai-gateway calling router.realtime (2/2) (#31135) 2026-06-23 19:16:34 -07:00
test_redis_auth_cache_flag.py fix(proxy): share CLI SSO login sessions across workers without enable_redis_auth_cache (#33261) 2026-07-22 10:29:34 -07:00
test_response_model_sanitization.py perf: eliminate per-request callback scanning on proxy hot path (#27858) 2026-05-14 09:28:31 -07:00
test_route_a2a_models.py fix(proxy): single-row read-through resyncs and reload-race hardening 2026-08-18 21:02:12 -07:00
test_route_llm_request.py fix(proxy): serialize read-through with reloads, gate db object types 2026-08-18 22:43:44 -07:00
test_sensitive_route_auth.py chore(proxy): guard sensitive public endpoints 2026-04-30 11:52:47 -07:00
test_shared_health_check.py feat(proxy): skip disable_background_health_check models on GET /health when flag set (#27716) 2026-05-13 09:49:05 -07:00
test_spend_log_cleanup.py perf(spend-logs): bound retention cleanup so one run cannot saturate the database (#36594) 2026-08-13 18:56:37 -07:00
test_swagger_chat_completions.py fix(proxy): avoid misleading multi-method operation ids 2026-04-30 20:44:14 -07:00
test_team_member_update.py feat(ui): add budget duration to edit team member form (#29717) 2026-06-06 17:24:55 -07:00
test_team_org_move.py
test_tools_allowlist_enforcement.py fix(responses-bridge): custom tool round-trip and allowlist preservation for Codex CLI (#32258) 2026-07-06 17:34:27 -07:00
test_update_llm_router_resilience.py feat(complexity_router): custom classifier plugins via classifier_type 'custom' (#37249) 2026-08-18 14:09:19 -07:00