Commit graph

4637 commits

Author SHA1 Message Date
mubashir1osmani
4401b85855 Merge remote-tracking branch 'berri/litellm_internal_staging' into litellm_managed_batches_observability 2026-08-17 19:08:12 -04:00
mateo-berri
b7593a99c7 fix(guardrails): keep remaining usage upserts when one write fails
Per-row guards in the daily metrics and usage unit flush so a single DB error no longer drops the rest of the batch, plus removal of narrating comments flagged in review
2026-08-17 16:01:53 -07:00
Mateo Wang
77b7c6c40c
Merge pull request #37198 from BerriAI/litellm_lit5660_batches_limit_400
fix(proxy): reject out-of-range limit on GET /v1/batches with OpenAI-parity 400
2026-08-17 15:53:46 -07:00
mateo-berri
915a1cabcd feat(proxy): add Amazon Comprehend Medical passthrough provider 2026-08-17 15:44:06 -07:00
Mateo Wang
a6de0736e1
Merge pull request #37201 from BerriAI/litellm_fix_batches_404
fix(proxy): return 404 instead of 500 for unresolvable batch and file ids on /v1/batches
2026-08-17 15:43:49 -07:00
mateo-berri
91c12ec810 test(proxy): run managed passthrough limit tests in CI 2026-08-17 15:39:46 -07:00
Itai Modiano
fbd09ca27d
perf(guardrails): stop sending the conversation twice in the noma v2 payload (#36764)
The Noma guardrail sends the conversation to the scanner in `inputs`. It
also forwarded `request_data` whole, which repeats that same conversation
under `messages` (or `input` on the responses API), and attached
`logging_obj.model_call_details`, which repeats it a third time.

For image-heavy calls that duplication is most of the request. A
production scan of a request carrying base64 images measured 100MB total,
of which 94.8MB was `request_data` against 5.1MB of `inputs` - the proxy
was uploading ~95% redundant bytes, and paying to serialize them.

Drop `messages` and `input` from `request_data` and from
`model_call_details`. This is a denylist rather than an allowlist on
purpose: every other key is still forwarded untouched, so a scanner-side
change that starts reading a new `request_data` key needs no matching
release of this hook. The removed keys are ones the scanner never reads -
it takes context only from metadata, litellm_metadata,
provider_specific_header, litellm_session_id/trace_id/call_id, stream,
response/responses ids, and litellm_logging_obj.complete_streaming_response,
all of which still pass through.

The conversation still reaches the scanner in full via `inputs`, so no
detection coverage changes.

Trimming happens before serialization, so the duplicate is never encoded.

Existing payload tests asserted the duplication; they now assert the trim
while keeping what they originally guarded - deep-copy semantics and the
unpicklable-object (uvloop.Loop) regression.
2026-08-17 15:37:17 -07:00
mateo-berri
55e80849d1 feat(guardrails): track bedrock guardrail usage units per invocation 2026-08-17 15:18:53 -07:00
tin-berri
c1aae9d723
feat(shadow-eval): name the shadowed key in job responses and the UI headline (#37221) 2026-08-17 22:12:59 +00:00
yucheng-berri
7984ae4636
fix(mcp): scope authorization server issuer for named MCP servers (#37204)
* fix(mcp): scope authorization server issuer

Generated with AI

Co-Authored-By: Claude Code <noreply@anthropic.com>

* fix(mcp): keep the bare-origin issuer when no server was named

The scoped issuer must key off whether the request actually carried a server
name. _build_oauth_authorization_server_response rebinds mcp_server_name when
root discovery resolves the single configured OAuth2 server, so gating on the
rebound value also scoped /.well-known/openid-configuration, whose document is
served from the bare origin and whose issuer must stay the bare origin

Adds the named-server regression test for the reported mismatch, restores the
bare-origin assertion, and covers the OIDC document

* test(mcp): type the delegate_auth_to_upstream helper parameter

* refactor(mcp): bind the discovery issuer to a local before building the response

---------

Co-authored-by: Irosh <15094153+irosh-colombage-ZocDoc2@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-17 15:06:12 -07:00
Mateo Wang
e8154c3e4c
Revert "fix(mcp): scope authorization server issuer" (#37220) 2026-08-17 15:06:01 -07:00
mateo-berri
97b7eaad5c fix(mcp): keep issuer-anchored slots on reload, skip discovery for stamped M2M challenge
Registry-swap reconciliation used bool(server.url) while registration uses _requires_oauth_discovery, dropping slots for issuer-anchored servers without a url. The preemptive 401 loop awaited discovery before the stamped client_credentials continue, so a deferred discovery failure could 503 requests whose challenge decision never reads metadata
2026-08-17 14:44:01 -07:00
mateo-berri
127b426991 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_azure_di_native_ocr_format 2026-08-17 14:28:17 -07:00
Mateo Wang
c1fc5983ca
Merge pull request #36482 from irosh-colombage-ZocDoc2/fix/mcp-oauth-scoped-issuer
fix(mcp): scope authorization server issuer
2026-08-17 14:27:38 -07:00
yuneng-jiang
77c8a6452f
Merge pull request #36258 from BerriAI/litellm_/elastic-ishizaka-db31e6
feat(proxy): let USE_V2_MIGRATION_RESOLVER select the v2 migration resolver
2026-08-17 14:13:04 -07:00
mateo-berri
5ddff616dc fix(mcp): keep origin issuer on the openid-configuration alias 2026-08-17 13:41:36 -07:00
Mateo Wang
e11fe1d6cc
Merge pull request #36781 from daniel-meismer-zocdoc/feature/request-logs-user-id-filter
feat(ui): add user ID request log filter
2026-08-17 13:32:26 -07:00
mateo-berri
d5a4c14577 docs(proxy): pre-fix passthrough streams omitted content-type, not octet-stream
Some checks failed
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-08-17 13:21:28 -07:00
Mateo Wang
cde5465e6f
Merge pull request #37203 from BerriAI/litellm_batches_metadata_type_400
fix(proxy): return 400 for non-object metadata and litellm_metadata instead of silent drop or 500
2026-08-17 13:19:33 -07:00
Mateo Wang
3b6e56716a
Merge pull request #36978 from Scott-Wilson-ZocDoc/fix/mcp-guardrail-usage-monitor
fix(guardrails): record MCP tool guardrail evaluations and blocks in …
2026-08-17 13:18:03 -07:00
mateo-berri
f86dc8f54e test(proxy): assert non-Bedrock passthrough stream emits no content-type header 2026-08-17 13:09:17 -07:00
mateo-berri
8ced9f56a1 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_4561_bedrock_passthrough_content_type 2026-08-17 13:04:18 -07:00
mateo-berri
11e2341fc9 Merge branch 'litellm_internal_staging' into feature/request-logs-user-id-filter 2026-08-17 13:01:19 -07:00
mateo-berri
c7b17b6615 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_pr36482_head 2026-08-17 12:54:14 -07:00
mateo-berri
09c8d1f1f5 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit5660_batches_limit_400 2026-08-17 12:46:15 -07:00
mateo-berri
a21de07b3f fix(proxy): drop every invalid metadata field before raising so failure hooks never see them 2026-08-17 12:44:46 -07:00
mateo-berri
73ae9c65dd test(proxy): give the x-litellm-model fallback test deterministic openai env creds 2026-08-17 12:42:57 -07:00
mateo-berri
f3b0cdca43 fix(proxy): keep ProxyException status codes on /v1/moderations instead of wrapping into 500 2026-08-17 12:32:30 -07:00
mateo-berri
c894697a2a fix(proxy): keep ProxyException status codes on /v1/messages instead of wrapping into 500 2026-08-17 12:21:12 -07:00
Mateo Wang
badd737526
Merge pull request #37199 from BerriAI/litellm_lit5657_batches_400_missing_fields
fix(proxy): return 400 naming the missing required param on POST /v1/batches
2026-08-17 12:20:25 -07:00
mateo-berri
6e55a21ebb fix(proxy): enforce batch list limit bounds on managed passthrough listings 2026-08-17 12:15:22 -07:00
Thijmen Stavenuiter
4e3e7d8cf6 test: cover soft budget row creation and windows without max_budget 2026-08-17 21:12:36 +02:00
mateo-berri
57e1bf41c6 fix(proxy): return 400 for non-object metadata and litellm_metadata instead of silent drop or 500 2026-08-17 12:08:56 -07:00
Thijmen Stavenuiter
c7abb519a1 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into key-budget-window-usage 2026-08-17 21:04:01 +02:00
mateo-berri
4a7dfd75fc fix(proxy): return 404 instead of 500 for unresolvable batch and file ids on /v1/batches 2026-08-17 12:02:15 -07:00
mateo-berri
a9bb09905d test(batches): drop redundant section banner 2026-08-17 12:00:40 -07:00
mateo-berri
e4ce526900 fix(proxy): return 400 naming the missing required param on POST /v1/batches 2026-08-17 11:55:06 -07:00
mateo-berri
f55a193628 fix(proxy): reject out-of-range limit on GET /v1/batches with OpenAI-parity 400 2026-08-17 11:54:34 -07:00
ryan-crabbe-berri
ad6a3a7b9e
fix(proxy): registry caches stop per-request tag and end-user Postgres reads in auth (#36801)
* fix(proxy): cache tag-name registry so unregistered request tags skip Postgres

Request tags are free-form attribution labels, so most have no LiteLLM_TagTable
row. get_tag_objects_batch never cached that absence: every tagged request ran
a find_many that came back empty, and under Prisma pool contention those
per-request queries queued for minutes inside user_api_key_auth.

Cache the bounded set of registered tag names under one aggregate key with the
management-object TTL. Uncached request tags are filtered against it before any
per-tag DB fetch, so unregistered tags cost zero DB reads on a warm path. An
empty registry is cached as a valid answer; DB errors are not cached and fall
back to the per-tag lookup; tables past TAG_REGISTRY_MAX_SIZE cache an overflow
sentinel that disables filtering. Tag create/update/delete endpoints now evict
the registry and per-tag keys and publish cross-worker invalidation (they
previously evicted nothing). The per-tag write-back also gains the management
TTL it was missing, and the hand-built tag:{name} key strings are replaced with
a shared builder.

* fix(proxy): skip per-request end-user DB reads via restricted-id registry

Every request carrying a user id ran get_end_user_object, and with high-cardinality
auto-created end-user rows (hundreds of thousands of ids, all restriction fields
NULL) the per-pod cache missed on nearly every request, so each one paid a Postgres
find_unique that queued behind the Prisma pool during background-job bursts. True
misses were never cached, and unknown ids paid the read twice per request.

Cache the bounded set of end-user ids that carry any restriction (blocked, budget,
region, default model, or object permission) under one aggregate key with the
management-object TTL. When an id misses the per-id cache and is absent from a
usable registry, get_end_user_object returns None with zero DB reads; restricted
ids keep today's fetch-and-cache path. The skip is bypassed whenever
litellm.max_end_user_budget_id is set (default budgets make unrestricted rows
behaviorally distinct from missing rows), validate_end_user_id_in_db is on
(existence checks need the row), or the token carries end_user_max_budget from
custom auth (the row's recorded spend seeds the budget counter). Empty registries
cache as a valid answer, DB errors are never cached, and oversized tables cache an
overflow sentinel that disables filtering. Customer create/update/block/delete now
evict the registry and per-id keys and publish cross-worker invalidation (they
previously evicted nothing), and the per-id write-back gains the management TTL it
was missing so Redis entries no longer live forever.

* refactor(proxy): single generic registry loader with error sentinel and single-flight

Code review follow-ups on the two registry caches. Registry DB errors now cache
the overflow sentinel for a short REGISTRY_ERROR_NEGATIVE_CACHE_TTL window and
log at warning, so a degraded Postgres stops paying the failing registry scan on
every request on top of the per-id fallback. Cold registry loads are single-flight
per worker behind per-registry locks with a recheck after acquire, so a TTL expiry
no longer fans out one full-table scan per in-flight request. The tag and end-user
loaders collapse into one _load_bounded_registry with per-entity fetch closures,
and the triplicated evict-then-broadcast protocol becomes one evict_and_broadcast
helper beside publish_auth_cache_invalidation, shared by the tag, customer, and
project eviction paths.

* chore(lint): suppress fail-safe registry excepts and ratchet BLE001 budget

* docs(proxy): trim registry cache commentary to single-line why docstrings

* fix(lint): move tag fetch return to else block to satisfy TRY300 budget
2026-08-17 18:52:13 +00:00
Thijmen Stavenuiter
9b30e73d07 fix: cap /v2/key/info batch size to bound spend-log query fan-out 2026-08-17 20:48:12 +02:00
mateo
1e1a2b63a4 fix(ocr): validate body req_format in the proxy endpoint and run its tests in CI
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-17 18:29:35 +00:00
yucheng-berri
1139012b45
fix(guardrails): scan text on /guardrails/apply_guardrail for Azure Content Safety (#36894)
* fix(guardrails): scan text on /guardrails/apply_guardrail for Azure Content Safety

The two Azure Content Safety guardrails never implemented apply_guardrail, so the
endpoint fell through to the base no-op and answered 200 with the caller's text
echoed back, having scanned nothing.

Implementing that method also flips the proxy's unified-vs-native dispatch, which
would move request traffic off these guardrails' own hooks. Add an opt-out that
keeps every lifecycle event on the native hooks, so only the endpoint changes.

* test(guardrails): cover the remaining native-hook opt-out dispatch sites

Adds regression tests for the parallel post-call path, the MCP post-call hook, and
the policy engine step, so every read of the opt-out flag fails when removed.
2026-08-17 11:18:40 -07:00
mateo
bfc52b94db fix(ocr): return 400 for an unknown x-req-format header value
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-17 18:11:57 +00:00
mateo
57d739b433 feat(ocr): add req_format=native to return Azure Document Intelligence's own analyzeResult payload
Callers can opt into the provider's raw operation response on /v1/ocr with the x-req-format: native header (or req_format in the body) while page-based cost tracking keeps reading usage_info off the normalized response.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-17 18:03:53 +00:00
mubashir1osmani
141ada1118 feat(batches): aggregate reasoning tokens and per-line pass/fail counts
Batch retrieval already computed cost/usage on completion, but silently
dropped reasoning tokens and never counted per-line success/failure.
Adds BatchCostUsageResult (replacing bare cost/usage/models tuples) with
successful_requests/failed_requests, and threads reasoning_tokens through
the aggregated Usage. Both surface on SpendLogs the same way batch_models
already does.
2026-08-17 11:25:55 -04:00
Thijmen Stavenuiter
31130036c0 refactor: build budget window usage without in-place mutation
Replace _attach_budget_limits_usage, which rewrote the caller's key_info dict, with _budget_limits_with_usage returning a new list. Callers assign the result once. Keeps the response shape and spend-counter read path identical while following the repo's no-mutation rule.
2026-08-17 16:21:31 +02:00
mateo-berri
5965648547 fix(proxy): close websocket cleanly when OpenAI credentials are missing 2026-08-16 14:40:37 -07:00
mateo-berri
4ba9d6b136 fix(proxy): expose url join helper at module level for websocket route 2026-08-16 14:29:21 -07:00
mateo-berri
81aefe4b3c Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_pr36151_ws_passthrough
# Conflicts:
#	litellm/proxy/pass_through_endpoints/pass_through_endpoints.py
#	tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py
2026-08-16 14:21:57 -07:00
mateo-berri
862f33bbaa fix(proxy): negotiate client subprotocol on OpenAI websocket passthrough 2026-08-16 14:11:12 -07:00