Commit graph

9968 commits

Author SHA1 Message Date
user
b1b00e4bdc
chore(guardrails): cover multimodal + Responses-API content shapes
Several guardrail hooks short-circuit when ``message.content`` is a list
or when the request uses the Responses-API ``input`` field instead of
``messages``. Centralise the content-walking logic in a shared helper and
update the affected hooks so list-format and Responses-API payloads no
longer skip inspection.

Also: Aim's ``async_post_call_success_hook`` now inspects every choice
(via ``asyncio.gather``) instead of only ``choices[0]`` — the prior
behaviour let ``n>1`` callers hide content in subsequent completions.

Hooks updated to use the new helper:
- aim, lakera_ai_v2, lasso (post a synthesised messages list to a remote
  guardrail service)
- azure_content_safety, ibm_detector, banned_keywords, openai_moderation,
  google_text_moderation (iterate text fragments locally)
- secret_detection (walk-and-rewrite to redact in place)

Drive-by fix: the legacy ``data["prompt"]`` list-handling path in
secret_detection rebound the loop variable instead of mutating the list,
leaving secrets unredacted on text-completion calls; corrected to index
back into the list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 03:50:15 +00:00
user
e9fb89b90c fix(proxy): avoid misleading multi-method operation ids 2026-04-30 20:44:14 -07:00
user
f30bfcf36a add budget reservation disable flag 2026-04-30 20:36:14 -07:00
user
f18ee0319d fix(proxy): isolate ownership persistence paths 2026-04-30 20:25:40 -07:00
user
4f8769943b skip invalid budget window counter increments 2026-04-30 20:18:07 -07:00
user
2ecc79b9e9 test(proxy): cover skill ownership propagation 2026-04-30 20:06:44 -07:00
user
dcfde1b899 fallback to plain org cache for spend counters 2026-04-30 20:04:16 -07:00
user
3a566c3938 fix(proxy): stabilize ownership fallback and openapi ids 2026-04-30 19:55:00 -07:00
Sameer Kankute
efa33bfe50
Merge pull request #26222 from BerriAI/litellm_anthropic-json-mode-nonstreaming-mixed-tools
Some checks are pending
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
fix(anthropic): json response_format + user tools non-streaming
2026-05-01 08:24:38 +05:30
Sameer Kankute
72ddbce50e
Merge pull request #25499 from BerriAI/litellm_vertex_request_metadata_labels
feat(vertex_ai): propagate metadata labels to embedding, Imagen, rerank
2026-05-01 08:20:55 +05:30
user
c28e093f41 finalize budget reservations after counter updates 2026-04-30 19:50:36 -07:00
user
abd51fc30e
fix(audit): label vault POST as updated when DB row exists
Greptile P2 follow-up: when a litellm_configoverrides row exists with a
NULL config_value (e.g. an earlier failed write left a stub), the audit
action was mislabeled "created" because we keyed off existing_decrypted
(which is only set when config_value is non-null). Key off existing_record
instead — a row is a row regardless of its value.

Also hoist asyncio + patch imports to module top in the test file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:44:47 +00:00
user
20eb9c96ca fix(proxy): avoid mutating container responses 2026-04-30 19:43:20 -07:00
user
7b1e3f278b test(proxy): cover container endpoint post processing 2026-04-30 19:34:20 -07:00
user
15d845c321 avoid stale local spend counters after redis misses 2026-04-30 19:33:55 -07:00
user
f3cff9338e fix(proxy): harden resource ownership fallbacks 2026-04-30 19:29:08 -07:00
user
405de46329 cap budget reservations to remaining headroom 2026-04-30 19:24:48 -07:00
user
6aac4552f9 fix(proxy): forward decoded container ids 2026-04-30 19:13:20 -07:00
user
9376b30bca fix(proxy): reset filtered container pagination 2026-04-30 19:01:37 -07:00
Krrish Dholakia
dd57ae6691 feat(rate-limit): atomic check-and-increment-by-N for multi-process safety
The previous fix for the TOCTOU bypass relied on a per-instance asyncio.Lock,
which closed the window only within a single proxy worker. Multi-replica
deployments still raced across processes — A and B both read counter=99,
both passed validation, both incremented to 100/100 → effective limit doubled.

Add `CHECK_AND_INCREMENT_BY_N_SCRIPT` Lua script that processes any number of
(window_key, counter_key, limit, increment, ttl) descriptors atomically with
all-or-nothing semantics: if any descriptor would exceed its limit, no counter
is modified and the script returns OVER_LIMIT with the offending descriptor's
state. When Redis isn't configured, the in-memory fallback uses the existing
asyncio.Lock for single-process atomicity.

Expose this as `_PROXY_MaxParallelRequestsHandler_v3.atomic_check_and_increment_by_n`
and rewire both call sites:

- batch_rate_limiter._check_and_increment_batch_counters: replace the
  read_only=True check + separate async_increment_tokens_with_ttl_preservation
  with a single atomic call passing the batch's (request_count, total_tokens)
  as the increment.
- dynamic_rate_limiter_v3._check_rate_limits: bundle model_saturation_check
  (always enforced) and priority_model (enforced only when saturated) into
  one atomic call. When priority is unenforced, increment its counter via
  the existing should_rate_limit(read_only=False) path for tracking only.

Update structural regression tests to assert the new atomic path is used
rather than the legacy two-phase pattern.

Tests: 4/4 TOCTOU tests pass, 59 existing rate-limiter tests pass, no
regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:54:36 -07:00
user
e4741fbb2b fix(proxy): add legacy skill ownership opt-out 2026-04-30 18:53:44 -07:00
harish-berri
7c86e6073b Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_auth_bypass_tag_based_routing
Some checks failed
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Caching (Redis) / caching-redis (push) Has been cancelled
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
2026-05-01 01:49:39 +00:00
user
aa1312ef75 fix(audit): close NameError + mypy + asyncio-import nits
Three Greptile/CI findings on the prior commit:

1. **P1 (real bug):** ``before_config = existing_decrypted if existing_record
   is not None else env_values`` would NameError when ``existing_record``
   exists but its ``config_value`` is null (a valid nullable DB state) —
   the upper branch only defines ``existing_decrypted`` when *both*
   conditions are met, but the ternary only checked the first.  Pre-bind
   ``existing_decrypted: Optional[Dict] = None`` and ``env_values = {}``
   above the if/else so both names are always in scope, and key the
   audit-log decision off ``existing_decrypted is not None`` instead.

2. **mypy lint:** ``action: str`` rejected — the field is typed
   ``AUDIT_ACTIONS = Literal[...]``.  Annotate both helper signatures
   with ``AUDIT_ACTIONS`` and pre-bind the call-site ternary so mypy
   infers the literal correctly.

3. **P2:** ``import asyncio`` was at the bottom of the test file.
   Moved to the stdlib import block at top.
2026-05-01 01:47:11 +00:00
user
0745b1872e test(proxy): cover container endpoint forwarding 2026-04-30 18:46:14 -07:00
Krrish Dholakia
dbe5c3b0b2 fix: close TOCTOU window in batch + dynamic rate limiters
The batch rate limiter (`_check_and_increment_batch_counters`) and the
dynamic rate limiter (`_check_rate_limits`) implemented rate limiting in
two disjoint awaits: a `should_rate_limit(read_only=True)` check followed
by a separate increment. Concurrent requests could all observe the same
pre-increment state, all pass enforcement, and all then increment —
multiplying the effective quota by the concurrency level.

Demonstrated bypass (see new test):
- Batch: 5 concurrent batches of 40 tokens each against TPM=100 consumed
  200 tokens (100% over).
- Dynamic: 5 concurrent priority="high" requests against RPM=2 all
  passed Phase 1 + Phase 3.

Wrap both critical sections in a per-instance asyncio.Lock so the read
and increment execute atomically within a process. Multi-replica
deployments still rely on Redis Lua atomicity for cross-process safety;
that is a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:46:09 -07:00
shivam
f4211ff7c4
Merge branch 'litellm_internal_staging' into litellm_metrics_auth
Some checks failed
Unit Tests: Caching (Redis) / caching-redis (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
2026-04-30 18:41:09 -07:00
user
d2a322074f fix(proxy): preserve container ownership compatibility 2026-04-30 18:40:26 -07:00
shivam
b4df0e2c03
Merge branch 'litellm_internal_staging' into litellm_batch_model_id_mapping 2026-04-30 18:40:17 -07:00
user
ddc50e026e chore(audit): audit-log /cache/settings + /config_overrides/hashicorp_vault mutations
Follow-up to merged #26859 (team-callback audit log).  The variant
scan from that PR flagged two more high-risk admin endpoints whose
mutations weren't audit-logged:

- ``POST /cache/settings`` (cache_settings_endpoints.py) — writes the
  team / global Redis cache configuration, including credentials.  An
  admin (or compromised admin) flipping the cache backend is a
  data-routing pivot — every subsequent LLM response cache write goes
  to the new destination — so the action needs to be traceable.

- ``POST /config_overrides/hashicorp_vault`` and
  ``DELETE /config_overrides/hashicorp_vault``
  (config_override_endpoints.py) — control the proxy's KMS config.
  Mutating these affects every secret retrieval going forward.

Each endpoint now emits an ``LiteLLM_AuditLogs`` row gated on
``litellm.store_audit_logs`` (Enterprise feature), mirroring the
shape merged in #26859.  Both helpers redact every field value
before serialization (replacing them with ``***REDACTED***`` while
keeping field names) so the audit table cannot itself become a
credential-harvest sink — Redis passwords / vault tokens /
``approle_secret_id`` / ``client_key`` would otherwise be persisted
in plaintext JSON.

Both helpers also attach a ``done_callback`` that surfaces a
``verbose_proxy_logger.warning`` when the fire-and-forget audit-log
task fails, so a transient DB error doesn't silently lose the row.

Adds ``CACHE_CONFIG_TABLE_NAME`` / ``CONFIG_OVERRIDES_TABLE_NAME`` to
``LitellmTableNames`` so the audit rows co-locate with the table they
mutate.

Tests:
- /cache/settings: emits when ``store_audit_logs=True``, no emission
  when off, plaintext credentials don't appear in the serialized row.
- /config_overrides/hashicorp_vault POST: same, plus checks the
  redaction of ``vault_token`` and ``vault_addr``.
- /config_overrides/hashicorp_vault DELETE: emits with ``action="deleted"``
  only when an actual row was removed; idempotent delete of a non-existent
  row produces no audit log.
2026-05-01 01:32:41 +00:00
user
46068be6f6 skip invalid budget window reservations 2026-04-30 18:29:14 -07:00
shivam
b75820b984
Merge branch 'litellm_internal_staging' into litellm_metrics_auth 2026-04-30 18:28:37 -07:00
user
64fadc3b8e Merge remote-tracking branch 'origin/litellm_internal_staging' into codex/budget-race-enforcement-greptile-fix
# Conflicts:
#	litellm/proxy/db/spend_counter_reseed.py
#	litellm/proxy/proxy_server.py
2026-04-30 18:25:48 -07:00
user
ad9aa43e86 chore(proxy): scope skills and container resources 2026-04-30 18:23:58 -07:00
Michael-RZ-Berri
05e6402bdb
Merge pull request #26829 from BerriAI/litellm_budgetEnforcementMultiPod
[Fix] Refresh Redis TTL on counter writes, skip stale in-memory in Redis
2026-04-30 18:14:41 -07:00
user
38ebd4de3d harden partial budget reservation cleanup 2026-04-30 18:07:54 -07:00
shivam
e72eac9176
Fix add_model_file_id_mappings when router returns single deployment dict
When model_info.id equals model_name (common for batch models), the router
resolves via has_model_id and returns one deployment dict instead of a list.
The dict branch incorrectly iterated deployment keys (model_name,
litellm_params, model_info), producing non-string values that broke
LiteLLM_ManagedFileTable validation on managed file upload.

Normalize list vs dict by wrapping single deployments and extracting
model_info.id for each response pair.

Add regression tests including the batch model id == model_name case.

Made-with: Cursor
2026-04-30 18:04:47 -07:00
Michael-RZ-Berri
e4fb325a3a
Merge pull request #26914 from BerriAI/litellm_googleGenContentHooks
Run pre_call_hook on Google generateContent endpoints
2026-04-30 17:53:38 -07:00
Michael Riad Zaky
4e26835098 Reorder counter invalidation to run after DB write 2026-04-30 17:50:58 -07:00
Michael Riad Zaky
fed5f36a3d Invalidate spend counters on budget reset 2026-04-30 17:50:58 -07:00
Michael Riad Zaky
9f08db91f9 Refresh Redis TTL on counter writes and skip stale in-memory on Redis miss 2026-04-30 17:50:58 -07:00
Yuneng Jiang
bd638245e8
[Fix] Responses API: Omit Empty Body On DELETE
The async/sync delete_response_api_handler always passed json=data into
httpx.delete, where data is {} from the transformer. httpx serializes that
to a 2-byte body. The Azure Responses DELETE endpoint now rejects any
request body with code: unexpected_body, breaking
test_basic_openai_responses_delete_endpoint on the llm_responses_api_testing
job. Build the kwargs dict and only set json= when data is truthy.

Add unit tests that patch httpx.delete and assert json/data are not in the
captured kwargs for the Azure DELETE path (sync and async).
2026-04-30 17:39:55 -07:00
user
694fadd175 fix budget reservation review findings 2026-04-30 17:38:18 -07:00
user
2c852ba2b1 fix(sso): tighten oauth_state cookie — Secure flag + PKCE-only set
Two Greptile review findings addressed:

1. (P1, security) The ``litellm_oauth_state`` cookie is the sole
   guard against Login-CSRF in the PKCE flow but was set without the
   ``Secure`` attribute, so a network observer on plain HTTP could
   read and replay it — bypassing the protection this PR adds.

   Thread the originating ``Request`` down through
   ``get_sso_login_redirect`` and ``get_generic_sso_redirect_response``
   and set ``Secure`` based on ``request.url.scheme == "https"``.
   When no request is supplied (programmatic callers / tests) default
   to ``Secure=True`` — production-safe.  Local HTTP dev still works
   because the request scheme is observed at runtime.

2. (P2) The cookie was set unconditionally, but the callback only
   validates it inside the PKCE branch.  Two concurrent SSO sessions
   (one PKCE, one plain) could overwrite each other's state cookie
   and produce spurious 400s for the plain-flow user.

   Move the ``set_cookie`` call inside the existing
   ``if code_verifier and "state" in redirect_params`` block so the
   cookie is only written when PKCE is active and the validation
   will actually fire.

Tests cover both paths: PKCE-on (cookie set with Secure default),
PKCE-off (cookie not set), and HTTP dev request (Secure dropped so
the browser will actually attach the cookie on the callback hop).
2026-05-01 00:36:51 +00:00
user
fc580ae1ec fix(videos): encode the variant query param
``variant`` is user-controlled (passed through from
``litellm.video_content(variant=...)``) and was interpolated raw into
the URL query string.  A value like ``thumbnail&extra=1`` would inject
additional query parameters into the upstream request — the same
class of issue this PR's path-segment encoding addresses.  Wrap the
value in ``quote(value, safe="")`` so ``&`` / ``=`` / ``#`` cannot
terminate the ``variant`` value or open a new parameter.

Adds a regression test asserting that a malicious ``thumbnail&extra=1``
ends up percent-encoded in the URL, and that the legitimate
``thumbnail`` value still round-trips cleanly.
2026-05-01 00:32:02 +00:00
harish-berri
8671ec636b fix import error 2026-05-01 00:29:13 +00:00
user
06502d19a7 test(vector stores): allow primitive rag depth boundary 2026-04-30 17:28:02 -07:00
harish-berri
7c8fe86fd9
Merge branch 'litellm_internal_staging' into litellm_token_verification_query_opt 2026-04-30 17:25:12 -07:00
user
00442e653c chore(sso): bind generic SSO state to a session cookie
The Generic SSO PKCE flow used the URL ``state`` parameter as the
cache key for the PKCE ``code_verifier`` without binding the state
to the caller's browser.  An attacker who pre-minted a state and
cached a verifier under it could hand the resulting login link to a
victim; the victim's auth code would then be exchanged with the
attacker's verifier on the callback, producing an access token
under the attacker's control (Login CSRF / token theft).

The non-PKCE branch is unaffected because it delegates to
fastapi-sso's ``verify_and_process``, which performs its own
session-cookie check.  The PKCE branch bypasses that helper, which
is exactly the gap this commit closes.

Two-part fix in ``ui_sso.py``:

- ``get_generic_sso_redirect_response`` now sets a
  ``litellm_oauth_state`` cookie (HttpOnly, SameSite=Lax, 10-min TTL)
  carrying the state value used in the redirect URL.  The cookie is
  set on the redirect response just like the existing
  ``litellm_cp_return_to`` cookie a few lines earlier in the file.
- ``get_generic_sso_response`` validates ``request.cookies.get(
  "litellm_oauth_state")`` against ``request.query_params.get(
  "state")`` via ``secrets.compare_digest`` before invoking the
  PKCE token exchange.  Mismatch (or either being missing) raises a
  ``ProxyException`` with HTTP 400.

The pre-existing TODO above the redirect logic ("state should be a
random string and added to the user session with cookie") is now
addressed and removed.

Tests cover the redirect-side cookie set, the missing-cookie reject
shape, the URL/cookie-mismatch reject shape, and the matching-cookie
happy path.
2026-05-01 00:19:13 +00:00
user
9db8ecac12 update budget reservation auth test expectation 2026-04-30 17:13:49 -07:00
yuneng-jiang
bdcc23853c
Merge pull request #26835 from stuxf/codex/cli-sso-flow-binding
chore(cli): tighten CLI SSO session flow
2026-04-30 17:10:27 -07:00