Commit graph

7328 commits

Author SHA1 Message Date
user
66c0fe23da handle bad reservation counters after spend write 2026-04-30 22:55:26 -07:00
user
6ef26945fa test(proxy): narrow media resource decoding 2026-04-30 22:55:00 -07:00
user
0704f672c5 test(proxy): cover resource model extraction fallbacks 2026-04-30 22:21:57 -07:00
user
336fe8276f chore(proxy): align resource model auth checks 2026-04-30 21:59:56 -07:00
user
0b1ea9eb8f harden budget reservation edge cases 2026-04-30 21:49:31 -07:00
user
5397ac4562
fix(guardrails): redact `data["input"]` for Responses-API mask paths
Greptile P1: Aim's ``_anonymize_request`` and Lakera v2's mask-PII path
both wrote redacted content only to ``data["messages"]``. The Responses
API backend reads ``data["input"]``, so when a request arrived via
``/v1/responses`` with a plain string ``input`` the hook would update
``messages`` (which the backend ignores) and leave ``input`` carrying
the original unredacted text. Net effect: anonymize/mask silently passed
PII through to the LLM.

Add ``apply_redacted_messages_back`` to ``_content_utils`` — it writes
the redacted messages back to ``data["messages"]`` AND, when present,
re-flattens the redacted content into ``data["input"]``. Aim and
Lakera v2 now route their mask writeback through this helper. List
``input`` (multimodal) is still handled by the upstream
block-on-multimodal guard.

Adds unit tests for the helper and regression tests asserting
``data["input"]`` is redacted for both hooks on Responses-API string
input.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 04:41:57 +00:00
Sameer Kankute
b540a71e47
feat(mcp): enforce org-level MCP server and toolset permissions
Apply organization object_permission as a ceiling on allowed MCP servers
and tool permissions, consistent with vector store org checks.

Includes unit tests for org ceiling, intersection, and tool filtering.

Made-with: Cursor
2026-05-01 10:10:38 +05:30
user
40817caa4a
fix(guardrails): degrade Lasso/Aim mask paths to block on multimodal
Two more in-place rewrite paths exhibit the same regression as Lakera v2:
overwriting ``data["messages"]`` with text-only redacted versions silently
strips image/audio parts from multimodal requests.

- ``LassoGuardrail._run_lasso_guardrail``: when ``mask=True`` AND input
  is multimodal/Responses-API list, fall back to the classify endpoint
  (which raises on BLOCK actions but never overwrites the payload).
- ``AimGuardrail._anonymize_request``: when input is multimodal, raise
  the standard 400 instead of replacing ``data["messages"]`` with the
  text-only ``redacted_chat`` from Aim. The error message tells the
  user to either send plain string content or rely on block-mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 04:28:22 +00:00
user
9fcf234750
fix(guardrails): degrade Lakera v2 mask mode to block on multimodal input
Mask-in-place uses the offsets that Lakera returns for the inspection
payload. ``build_inspection_messages`` flattens multimodal content into
joined text before sending to Lakera, so the offsets refer to the
flattened representation. Writing those offsets back via
``_mask_pii_in_messages`` and overwriting ``data["messages"]`` would
silently strip image/audio parts from the original request — that is a
real functional regression for Lakera + mask mode + multimodal input.

Detect multimodal input (any list-format ``content`` or non-string
``data["input"]``) up front and skip the mask-in-place branch in that
case. The hook then falls into the standard block-on-detect path so PII
is still blocked but the multimodal payload is never silently rewritten.

Per-part masking that preserves multimodal structure is the right
long-term fix; tracking that as a follow-up.

Also: add ``has_non_string_content`` to ``_content_utils`` (with tests)
and a regression test that asserts multimodal+PII raises an HTTPException
instead of returning a flattened request body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 04:22:57 +00:00
user
b53adf7cff address budget reservation review edges 2026-04-30 21:21:26 -07:00
user
7514bb4740
fix(guardrails): close mixed-list gap, drop dead code, rename helper
Greptile P2 follow-ups on _content_utils.py:

- Drop unreachable ``_resolve_messages``. The new
  ``_iter_inspection_messages`` walks ``messages`` AND ``input``
  independently; leaving the old fallback-only variant around invited a
  future maintainer to wire it back up and silently narrow coverage.
- Rename ``iter_user_text`` → ``iter_message_text``. The helper walks
  every role (user, assistant, system); the old name implied user-turn
  content only. Callers and tests updated.
- Close mixed-list coverage gap. When ``data["input"]`` was a list
  mixing content-part dicts and bare strings, ``iter_message_text`` and
  ``build_inspection_messages`` only saw the dict parts while
  ``walk_user_text`` already inspected both. ``_iter_text_parts_in_content``
  now treats bare strings inside a content list as text fragments, so
  read and write helpers agree on coverage.

Adds two regression tests for the mixed-list shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 04:10:04 +00:00
user
ce17639cf7 remove budget reservation disable flag 2026-04-30 20:57:20 -07:00
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