Commit graph

2107 commits

Author SHA1 Message Date
user
09c4d0d01e fix(proxy): gate redaction opt out controls 2026-04-30 00:49:33 -07:00
user
7c3c9ab7ef test(proxy): opt mock-response fixtures into client mocks 2026-04-29 23:00:45 -07:00
user
7497674661 fix(proxy): sanitize redaction controls at ingress 2026-04-29 22:52:31 -07:00
user
842eea0131 chore(proxy): harden request control fields 2026-04-29 22:35:17 -07:00
Sameer Kankute
d3891e6eae
Merge pull request #26719 from BerriAI/litellm_fix-bedrock-stream-interrupt-spend-da73
fix(passthrough): track spend for interrupted Bedrock streams
2026-04-30 09:09:08 +05:30
Sameer Kankute
6588564a88
Merge pull request #26691 from BerriAI/litellm_team_search_credentials_metadata
feat(proxy): add team-level search provider credentials
2026-04-30 08:35:17 +05:30
ryan-crabbe-berri
b1b98c3fc7
Merge pull request #26821 from BerriAI/litellm_fix-guardrail-auth-check
fix(proxy/auth): tighten guardrail modification permission check
2026-04-29 19:48:34 -07:00
ryan-crabbe-berri
7c8e95888a
Merge pull request #26831 from BerriAI/litellm_fix-passthrough-batch-identity
fix(proxy): inherit caller identity in passthrough batch managed-object
2026-04-29 19:48:26 -07:00
yuneng-jiang
383bf12001
Merge pull request #26809 from BerriAI/litellm_teamMemberNullBudgetFallback
[Fix] Team member null budget fallback
2026-04-29 19:44:41 -07:00
Cursor Agent
8759413312
refactor: trim explanatory comments from streaming-flush fix
Strip module-level docstrings and per-test/per-block prose from the
LIT-2642 fix and tests. Keep one short comment in each streaming site
that flags the GeneratorExit-vs-Exception subtlety, since that's the
non-obvious reason the flush lives in finally rather than after the loop.

Pure cleanup; no behavior change. All 12 regression tests still pass.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-04-30 02:39:28 +00:00
Cursor Agent
1ef034bff6
fix(passthrough): flush spend tracking on interrupted Bedrock streams
When a client disconnects mid-stream from a Bedrock pass-through endpoint,
Starlette calls aclose() on the async generator, raising GeneratorExit
(a BaseException, not Exception) at the suspended yield. The previous
`except Exception` blocks in _async_streaming/_sync_streaming
(litellm/passthrough/main.py) and PassThroughStreamingHandler.chunk_processor
did not catch GeneratorExit, so the post-loop flush that hands collected
raw bytes to async_flush_passthrough_collected_chunks /
_route_streaming_logging_to_handler never ran. All per-chunk usage data
was silently dropped, undercounting spend for interrupted Bedrock invoke
and converse streams.

Move the flush into a finally block in all three sites and guard with a
`flush_scheduled` flag so the success path still flushes exactly once.
Also pull raise_for_status() out of the chunk-collection try block in
_async_streaming so 4xx/5xx responses still raise and don't enter the
flush path with zero bytes (preserving the behavior tested by
test_async_streaming_error_propagation.py).

Add regression coverage:
- test_async_streaming_flushes_on_client_disconnect
- test_async_streaming_flushes_on_upstream_exception_with_partial_data
- test_sync_streaming_flushes_on_early_close
- test_chunk_processor_logs_on_client_disconnect
plus baseline tests for normal completion and the 4xx no-flush path.

Fixes LIT-2642.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-04-30 02:39:28 +00:00
yuneng-jiang
2e561bd04e
Merge pull request #26463 from stuxf/fix/mcp-routing-auth
fix(mcp): tighten public-route detection and OAuth2 fallback gating
2026-04-29 19:31:00 -07:00
yuneng-jiang
c6c546ba86
Merge pull request #26484 from stuxf/fix/master-key-pass-the-hash
chore(auth): substitute alias for master key on UserAPIKeyAuth
2026-04-29 19:30:09 -07:00
yuneng-jiang
00d71f586c
Merge pull request #26489 from stuxf/fix/vector-store-list-cred-leak
chore(vector-stores): redact credentials in list/info/update responses; gate update by per-store access
2026-04-29 19:28:32 -07:00
stuxf
dedaf74a5e
chore(auth): tighten clientside api_base handling (#26518)
* chore(auth): validate clientside api_base against SSRF guard; clear admin secrets on base override

Two related issues with how the proxy handles client-supplied
``api_base`` / ``base_url`` overrides on chat-completion requests:

1. **SSRF gate bypass** — ``check_complete_credentials()`` returned
   ``True`` for any non-empty ``api_key``, allowing the
   ``is_request_body_safe`` ``banned_params`` loop to admit ``api_base``
   / ``base_url`` values that point at private (RFC 1918), loopback,
   link-local, or cloud-metadata addresses. Now: when the gate sees a
   client-supplied ``api_base`` / ``base_url``, it runs the URL through
   ``litellm_core_utils.url_utils.validate_url`` (DNS-resolves, blocks
   internal/IMDS/LL networks, defends against rebinding). Rejection
   raises with a clear message.

2. **Admin-config leak on base override** —
   ``get_dynamic_litellm_params`` only carried the three clientside keys
   (``api_key``, ``api_base``, ``base_url``) from request to upstream
   call. Other admin-configured fields on ``litellm_params`` —
   ``organization``, ``extra_body``, ``extra_headers``, ``api_version``,
   ``azure_ad_token``, AWS / Vertex creds, etc. — flowed through
   unchanged. With base redirected to a client-controlled server, those
   admin secrets were sent to the attacker. Now: when ``api_base`` /
   ``base_url`` is in ``request_kwargs``, drop those admin-config
   fields from ``litellm_params`` unless the caller re-supplied them.

Tests cover the SSRF-target rejection per URL field, the admin-secret
clearing on base override, the don't-clear case when only ``api_key``
is overridden (BYOK pattern), and the don't-overwrite case when the
caller resupplies fields like ``organization`` themselves.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(vertex-batches): wrap api_base GET in safe_get for defense-in-depth

The vertex batches status-poll fetches an attacker-influenceable
``api_base`` URL with a raw ``sync_handler.get()``. The proxy auth gate
already validates clientside ``api_base`` before reaching this sink, so
the proxy flow is covered. This adds the per-sink wrap so SDK callers
and any future code path that bypasses the proxy gate pick up the same
SSRF defense from ``url_utils.safe_get``.

Operators with a legitimate private Vertex base can either allowlist
the host via ``litellm.user_url_allowed_hosts`` or disable validation
with ``litellm.user_url_validation = False``.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(auth): hoist url_utils import; derive admin-config field list from CredentialLiteLLMParams

/simplify pass:
- Move ``from litellm.litellm_core_utils.url_utils import SSRFError, validate_url``
  to module top in ``proxy/auth/auth_utils.py``. CLAUDE.md prefers
  module-level imports unless avoiding a circular dependency, and
  there's no cycle here (``url_utils`` doesn't depend on ``proxy.auth``).
- Replace the hardcoded ``_ADMIN_CONFIG_FIELDS_TO_CLEAR_ON_BASE_OVERRIDE``
  literal with ``_admin_config_fields_to_clear_on_base_override()`` that
  derives the typed-field portion from
  ``CredentialLiteLLMParams.model_fields``. Adds three fields the
  hardcoded list missed (``aws_bedrock_runtime_endpoint``,
  ``watsonx_region_name``, ``region_name``) and stays in sync as new
  provider fields are declared on the model. The kwargs-only set
  (``organization``, ``extra_body``, ``azure_ad_token``, ``aws_session_token``,
  ``aws_sts_endpoint``, ``aws_web_identity_token``, ``aws_role_name``, …)
  remains explicit since those fields aren't on the typed model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(auth): close field-echo bypass; gate URL check on toggle; cover async batch path

Three issues from review:

1. ``get_dynamic_litellm_params`` used ``if field not in request_kwargs:
   pop`` to clear admin-set provider config when the caller redirected
   ``api_base``. A caller could *echo* any clear-list field name (with any
   value, including an empty string) to skip the pop, leaving the admin's
   value in ``litellm_params`` to be forwarded to the redirected upstream.
   Fix: always pop, then write the caller's value back if they resupplied
   the field.

2. ``check_complete_credentials`` called ``validate_url`` directly. That
   helper doesn't itself consult ``litellm.user_url_validation``; the
   toggle is honoured by ``safe_get`` / ``async_safe_get``. Mirror that
   here so admins who explicitly disabled URL validation aren't blocked
   at the proxy boundary.

3. ``VertexAIBatchesHandler._async_retrieve_batch`` still used a bare
   ``await client.get(api_base, ...)`` while the sync sibling was wrapped
   in ``safe_get``. Wrap the async call in ``async_safe_get`` so SDK
   callers on the async path get the same DNS-rebind / private /
   cloud-metadata defenses as the sync path.

Tests:

- ``TestCheckCompleteCredentialsBlocksSSRF`` is now mock-only; an autouse
  fixture flips the toggle on, ``validate_url`` is patched in the
  parametrized blocking tests, and the positive path no longer makes a
  real DNS call to api.openai.com.
- ``test_skips_url_validation_when_toggle_is_off`` documents the new
  toggle-off behaviour and asserts ``validate_url`` is not called.
- ``test_caller_resupplied_value_overrides_admin_value_on_base_override``
  replaces the prior test that asserted the buggy
  preserve-admin-value-on-echo behaviour.
- ``test_field_echo_does_not_preserve_admin_value`` is a focused
  regression test for the empty-string echo vector.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(auth): close provider-confusion credential exfil; expand banned-params; cover OCI

Three additions on top of the entry-point URL gate so the cluster is
fully closed against caller-supplied ``api_base`` redirection:

1. ``get_llm_provider_logic.py`` matched registered openai-compatible
   endpoints against ``api_base`` with an unanchored substring search
   (``if endpoint in api_base:``). A caller could pass an api_base like
   ``https://attacker.com/api.groq.com/openai/v1`` to coerce the proxy
   into reading ``GROQ_API_KEY`` from the environment and forwarding it
   as a Bearer credential to the attacker's host. Replaced with parsed-
   URL semantics (hostname exact-match plus segment-bounded path-prefix)
   in a new ``_endpoint_matches_api_base`` helper.

2. ``is_request_body_safe`` rejects ``api_base`` / ``base_url`` /
   ``user_config`` / a handful of AWS / vertex fields, but the list
   omitted three other endpoint-targeting fields:
   * ``aws_bedrock_runtime_endpoint`` — Bedrock endpoint redirect
   * ``langsmith_base_url`` / ``langfuse_host`` — observability callback
     hostnames; attacker-controlled values exfiltrate the entire request
     payload (incl. message content) via the logging hook.
   Added all three to the blocklist.

3. ``_admin_config_fields_to_clear_on_base_override`` derives its typed-
   field list from ``CredentialLiteLLMParams.model_fields``, which does
   not declare any of the OCI provider's auth fields. Added
   ``oci_signer``, ``oci_user``, ``oci_fingerprint``, ``oci_tenancy``,
   ``oci_key``, and ``oci_key_file`` to the kwargs-only fixed list so
   they are cleared on caller-redirected ``api_base`` like the AWS /
   Azure / Vertex equivalents.

Tests:

- ``TestEndpointMatchesApiBase`` — direct unit tests on the new
  matcher: legitimate provider URLs (5 shapes) match; attacker
  smuggling via path injection, suffix label, prefix label, userinfo
  ``@`` injection, and path-segment lookalikes (7 shapes) do not.
- ``TestGetLlmProviderRejectsAttackerSmuggledApiBase`` — end-to-end
  invariant that ``GROQ_API_KEY`` is never read against an attacker-
  controlled host while the legitimate ``api.groq.com`` path still
  resolves the provider correctly.
- ``TestIsRequestBodySafeBlocksEndpointTargetingFields`` — parametrized
  coverage that each of the three new banned-params raises a clear
  rejection naming the offending field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(auth): remove implicit api-key bypass + add posthog/braintrust/slack to blocklist

The historical ``check_complete_credentials`` clause inside
``is_request_body_safe`` was a third, *implicit*, *caller-controlled*
BYOK path: any caller that supplied a non-empty ``api_key`` caused the
entire banned-params blocklist to be skipped. That turned every missing
entry on the blocklist into an exploitable SSRF / credential-exfil hole
and is the root cause of the chain of api_base advisories that have
been re-discovered with each new integration:

* GHSA-jh89-88fc-qrfp (critical, triage) — env-var exfil via api_base
* GHSA-3frq-6r6h-7j64 (high, triage) — admin org / extra_body leak
* veria-admin Dv_m860l, b_yRJeQ5, stN90yjP, LBlyOAc8, U2TD78kg —
  variations on "list X is missing field Y"

Two explicit, admin-controlled BYOK paths already exist and remain:
``general_settings.allow_client_side_credentials = true`` (proxy-wide)
and ``configurable_clientside_auth_params: [...]`` per deployment.
Removing the implicit bypass converts the failure mode of a missing
blocklist entry from "live credential leak" to "predictable 400 with
a clear remediation message," which is the structural fix.

Also adds the three remaining endpoint-targeting fields the dynamic
callback layer reads from request body: ``posthog_host``,
``braintrust_host``, ``slack_webhook_url``. ``slack_webhook_url`` in
particular was a direct exfil channel (caller-set webhook → proxy
mirrors every request to attacker's Slack).

Tests:

- ``test_api_key_does_not_bypass_blocklist`` — parametrized regression
  asserting api_key=anything no longer skips the gate for any of the
  five highest-risk fields.
- ``test_admin_opt_in_proxy_wide_still_allows`` — confirms the
  documented BYOK opt-in still works.
- Extends ``test_endpoint_targeting_field_in_request_body_is_rejected``
  to cover posthog / braintrust / slack.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(auth): block sagemaker_base_url, s3_endpoint_url, deployment_url

Provider-specific endpoint overrides surfaced by a wider audit of
``optional_params`` consumers in ``litellm/llms/``. Same threat as
``api_base``: a caller-supplied value redirects the outbound request
to an attacker host.

* ``s3_endpoint_url`` — read in ``litellm/llms/bedrock/files/transformation.py``
  to build the S3 upload URL for Bedrock files. Caller redirects file
  uploads to attacker-controlled S3.
* ``sagemaker_base_url`` — read in ``litellm/llms/sagemaker/{chat,completion}/*``.
  Caller redirects SageMaker traffic. This is the primary vector
  described in veria-admin mNqEBBtG.
* ``deployment_url`` — popped in ``litellm/llms/sap/chat/transformation.py``.
  Caller redirects SAP deployment requests.

Tests parametrize ``test_endpoint_targeting_field_in_request_body_is_rejected``
to cover the three new fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:27:22 -07:00
ishaan-berri
4a7af1ff68
feat(proxy): durable agent workflow run tracking via /v1/workflows/runs (#26793)
* feat(schema): add workflow run tracking tables (LiteLLM_WorkflowRun, LiteLLM_WorkflowEvent, LiteLLM_WorkflowMessage)

* feat(proxy): add /v1/workflows/runs endpoints for durable agent workflow tracking

* feat(proxy): register workflow management router in proxy_server

* docs(workflows): add README for workflow run tracking API

* test(workflows): add unit tests for /v1/workflows/runs endpoints

* fix(workflows): atomic event+status update via tx(), run_id 404 guard, sequence retry on collision

* test(workflows): add tx mock, 404 on unknown run_id, retry-on-collision tests

* fix(workflows): constrain status to Literal enum, rename total→count in list responses

* add tenant isolation and bounded limits to workflow endpoints

* add created_by column and index to LiteLLM_WorkflowRun

* add ownership and bounded-limit tests for workflow endpoints

* Fix workflow run ownership for null owners

* guard prisma import in workflow_management_endpoints

* sync schema.prisma copies with workflow run models

* black: format workflow_management_endpoints.py

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-04-29 17:12:18 -07:00
Ryan Crabbe
2461139593
fix(proxy): inherit caller identity in passthrough batch managed-object
Read user_id and team_id from the request's litellm_params metadata when
fabricating the UserAPIKeyAuth handed to the managed_files hook, so
batches created via passthrough are attributed to the real requester
instead of a hardcoded fallback. Adds parametrized regression coverage
for both the populated-metadata and empty-kwargs cases.
2026-04-29 16:25:13 -07:00
yuneng-jiang
fc0cc9c581
Merge pull request #26225 from BerriAI/litellm_dbReconnectNonBlocking
[Fix] Proxy: reconnect Prisma DB without blocking the event loop
2026-04-29 16:09:22 -07:00
Ryan Crabbe
2ccb4b94e5
fix(proxy/auth): gate guardrail modification check on key presence
Use key-in-dict membership instead of truthy value lookup so explicitly
supplied empty/falsy payloads still trigger the permission check.
Adds parametrized regression coverage across all gated keys.
2026-04-29 14:56:47 -07:00
Mateo Wang
9bc317b4d0
Merge pull request #26584 from BerriAI/litellm_mcp-oauth-azure-entra-discovery2
[Feat]Add support for azure entra discovery endpoint
2026-04-29 14:28:41 -07:00
Yuneng Jiang
4f6192a49e Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_dbReconnectNonBlocking_local
# Conflicts:
#	tests/test_litellm/proxy/db/test_prisma_self_heal.py
2026-04-29 13:57:35 -07:00
yuneng-jiang
602a6cff81
Merge pull request #26756 from BerriAI/litellm_prisma_reconnect_hardening
fix(proxy): self-heal Prisma read paths + harden reconnect state machine
2026-04-29 13:49:40 -07:00
Mateo Wang
97a3bd5ff4
Merge pull request #26733 from BerriAI/litellm_mcp-short-prefix-id-0e42
feat(mcp): opt-in short-ID tool prefix to keep MCP tool names under the 60-char limit
2026-04-29 13:48:01 -07:00
Sameer Kankute
4cecfec9f9
feat(proxy): LiteLLM headers on Google native generateContent routes (#25500)
* feat(proxy): return LiteLLM headers on Google native generateContent routes

Wire build_litellm_proxy_success_headers_from_llm_response for :generateContent
and :streamGenerateContent so x-litellm-*, rate limit, and provider headers
match the OpenAI-style proxy path. Add unit test.

Annotate httpx.HTTPStatusError branch so pyright accepts .response after optional
exception transform. Remove unused variable in streaming tracer test (Ruff F841).

Made-with: Cursor

* fix(proxy): prefill Google GenAI stream _hidden_params for proxy headers

- Pass model_id, api_base, and process_response_headers output into streaming
  iterators so streamGenerateContent gets the same x-litellm-* headers as
  non-streaming paths.
- Drop request_data deployment mutation from build_litellm_proxy_success_headers_from_llm_response.
- Avoid logging raw request key names in oversized debug payload (code scanning).
- Extend tests for streaming iterator shape, metadata fallback, and helper.

Made-with: Cursor

* Update litellm/proxy/common_request_processing.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* remove unused key count

* Fix greptile review

* Update litellm/proxy/common_request_processing.py

Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com>
2026-04-29 12:34:14 -07:00
user
294ac8383e
fix(vector-stores): recurse into nested litellm_params; handle JSON-string shape
Three issues surfaced in review of the previous commit:

1. **Veria — Medium**: ``litellm_params`` carries a nested
   ``litellm_embedding_config`` dict (auto-resolved from the model
   registry on create / update) which itself holds ``api_key`` /
   ``aws_*`` / ``vertex_credentials``. The previous redactor only
   inspected top-level keys, so the nested values passed through
   unredacted. Recurse into nested dicts.

2. **Greptile — P2**: when ``litellm_params`` is a JSON-serialized
   string (the in-memory registry occasionally stores it that way), the
   previous redactor silently no-op'd via the ``isinstance(..., dict)``
   guard and echoed the raw payload back. Now: parse, redact, re-serialize.
   If the string is not valid JSON, replace it with the redaction
   sentinel rather than echo it.

3. **mypy** flagged ``_redact_sensitive_litellm_params``'s
   ``Optional[Dict[str, Any]]`` signature as incompatible with the
   ``object``-typed call site. Widened to ``Any -> Any`` to reflect the
   actual contract (the function now handles dict / str / None / other).

Also fixes a related test regression in
``test_remove_sensitive_info_from_deployment_with_excluded_keys``: the
``"credentials"`` plural addition to ``SensitiveDataMasker`` defaults
caused the first call (without ``excluded_keys``) to mutate the input
dict's ``litellm_credentials_name`` to a masked value. The second call
(with ``excluded_keys``) then saw the already-masked value rather than
the original. Construct fresh input for each call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:56:40 +00:00
user
51d560ba2e
chore(vector-stores): also gate /vector_store/update; upstream credentials plural in masker
Two architectural extensions to the credential-redaction in the previous
commit:

1. ``/vector_store/update`` had two gaps:
   - No per-store access control. Any authenticated principal that
     passed the premium-feature gate could mutate *any* vector store,
     including stores belonging to other teams.
   - The response returned the full DB row including ``litellm_params``,
     so the caller could read another team's persisted provider
     credentials by submitting a no-op metadata change.
   Mirror the access-control check ``/vector_store/info`` already
   performs (``_check_vector_store_access`` against the existing row),
   redact ``litellm_params`` in the response, and add an
   ``except HTTPException: raise`` guard so the 403/404 responses don't
   get rewritten as 500 by the catch-all.

2. ``SensitiveDataMasker``'s default ``sensitive_patterns`` set used
   segment-exact matching, so ``credential`` matched ``vertex_credential``
   but not ``vertex_credentials`` (the actual Vertex field name). The
   previous commit worked around this with a per-call extension; this
   commit puts the plural in the upstream defaults so every caller
   (Redis config dump, MCP debug headers, cache routes, ...) gets the
   correct behavior. The local override in
   ``vector_store_endpoints/management_endpoints.py`` is removed.

Also updates ``test_excluded_keys_exact_match`` which relied on
``credentials`` *not* being a sensitive pattern to demonstrate
case-sensitive ``excluded_keys`` matching. The intent of the test
(case-sensitive match) is preserved; the assertion now reflects that
when ``excluded_keys`` fails to apply (wrong case), the field falls
through to standard pattern-based masking instead of being passed
through unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:56:09 +00:00
user
a99943ec49
test+style: drop _redact_vector_store wrapper; inherit masker defaults
/simplify pass:
- Remove the single-call-site ``_redact_vector_store`` wrapper. Inline
  the two-line redaction at its only caller in ``list_vector_stores``;
  ``get_vector_store_info`` was already calling the inner helper directly.
- Inherit ``SensitiveDataMasker``'s default sensitive-key set instead of
  duplicating the 12-element list, then add only the plural
  ``credentials`` extension. Won't drift if upstream defaults change.
- Trim the over-explained docstring on ``_redact_sensitive_litellm_params``
  to a one-paragraph summary; the WHY (credential-leakage class) belongs
  in the commit message, not in every consumer's IDE tooltip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:55:40 +00:00
user
0806cca340
chore(vector-stores): redact credentials from list/info responses
``LiteLLM_ManagedVectorStore.litellm_params`` carries the upstream provider
credential — OpenAI ``api_key``, AWS ``aws_access_key_id`` /
``aws_secret_access_key``, GCP ``vertex_credentials``, etc. ``GET
/vector_store/list`` and ``POST /vector_store/info`` return these
verbatim to any authenticated principal. Because both routes are in
``openai_routes``, ``RouteChecks.is_llm_api_route`` short-circuits the
standard role gate, so even read-only users and narrowly-scoped keys can
read every stored credential.

Replace credential-bearing values with the ``REDACTED_BY_LITELM``
sentinel in both responses while preserving non-secret keys
(``api_base``, ``region``, ``model``, ``api_version``) so callers can
still see *which* upstream is configured. Detection reuses
``SensitiveDataMasker.is_sensitive_key`` with the default heuristics
plus the plural ``credentials`` pattern (covers Vertex's
``vertex_credentials`` field, which the singular ``credential`` pattern
misses on segment-exact matching).

Applied at:
- ``list_vector_stores`` (``GET /vector_store/list``,
  ``GET /v1/vector_store/list``)
- ``get_vector_store_info`` (``POST /vector_store/info``), both the
  in-memory-registry path and the prisma-DB fallback

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:55:40 +00:00
user
9d9f09934e
chore(auth): substitute alias for master key on UserAPIKeyAuth
Two related changes to how the master-key auth path interacts with
downstream consumers of UserAPIKeyAuth.api_key:

1. The master-key auth branch in user_api_key_auth.py now sets
   `valid_token.api_key` to a stable alias
   (`LITELLM_PROXY_MASTER_KEY_ALIAS = "litellm_proxy_master_key"`) instead
   of the raw master key. Downstream consumers — spend logging,
   Prometheus metrics, audit trails, rate limiting, cost tracking — now
   receive the alias instead of the master key (which they would
   previously hash and propagate). Neither the raw master key nor its
   hash flows past the auth layer.

2. `_is_master_key` in spend_tracking_utils.py is reduced to a strict
   raw-only constant-time comparison. The hashed form is no longer
   considered equivalent.

Side effects:

- The two hash-detection blocks in `get_logging_payload` are removed.
  They were re-detecting the master key per spend-log write to swap in
  the alias; that detection happens once at the auth layer now.

- The `disable_adding_master_key_hash_to_db` general setting becomes a
  no-op. Operators can remove it from their config; existing config is
  still accepted.

- Operator dashboards that filter Prometheus metrics by the master-key
  hash will need to switch to the `api_key="litellm_proxy_master_key"`
  label.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:53:12 +00:00
Michael Riad Zaky
04687ba48e Trim verbose comments and docstrings 2026-04-29 11:07:35 -07:00
Michael Riad Zaky
2b9ad4d4eb Fall through to team default when per-member budget max_budget is NULL 2026-04-29 10:14:09 -07:00
Sameer Kankute
8af338a202
Merge pull request #26757 from BerriAI/litellm_internal_staging
merge main
2026-04-29 12:44:09 +05:30
Sameer Kankute
4b03cb68a2
feat(proxy): move search tool access to object permissions
Store search tool allowlists only on object permissions, wire auth/management/UI flows to object_permission.search_tools, and remove legacy team-metadata search credential code and tests.

Made-with: Cursor
2026-04-29 12:29:20 +05:30
Yuneng Jiang
aa2ef41200 fix(proxy): preserve original transport error if reconnect itself raises
Greptile review on #26756 (P2): if `attempt_db_reconnect` itself raises
(e.g. lock cancellation, timer error, unexpected internal failure), the
original `httpx.ReadError` / transport error was lost — `failure_handler`
and `db_exceptions` alerts then logged the reconnect exception instead of
the actual DB transport problem, masking the root cause.

Wrap the reconnect call in a try/except. On reconnect failure, re-raise
the *original* `first_exc` and chain the reconnect error as `__cause__`
so it remains visible for debuggability without becoming the primary
exception observers see.

Adds `test_call_with_db_reconnect_retry_preserves_original_error_when_reconnect_raises`
asserting (a) the propagated exception is the original transport error
and (b) the reconnect exception is attached as `__cause__`.
2026-04-28 23:55:46 -07:00
Yuneng Jiang
1c9c219a74 fix(proxy): self-heal Prisma read paths + harden reconnect state machine
Two related fixes layered on top of the existing reconnect plumbing:

1. Restore reconnect-and-retry on `PrismaClient.get_generic_data` (issue
   #25143). 1.83.x lost the transport-reconnect-and-retry-once branch that
   1.82.6 had on this method, so transient `httpx.ReadError` flaps now
   surface immediately as `db_exceptions` alerts. `_update_config_from_db`
   fans out four concurrent `get_generic_data` reads, so a single transport
   blip used to mark four alerts and a stale config window.

   Adds `call_with_db_reconnect_retry` to `litellm/proxy/db/exception_handler.py`
   — a single canonical "try DB read, on transport error reconnect once and
   retry once" wrapper. Mirrors the inline pattern in
   `auth_checks._fetch_key_object_from_db_with_reconnect` so we have one
   implementation rather than three drifting copies, and gives future read
   paths a clean opt-in.

2. Fix the `_engine_confirmed_dead` flag-reset bug in
   `_run_reconnect_cycle`. The flag was cleared before `_do_heavy_reconnect()`
   ran, so any failure inside the heavy reconnect (timeout, missing
   DATABASE_URL, recreate failure) left the flag False — and the next
   attempt could silently demote to the lightweight path even though the
   engine was genuinely dead. Move the reset into the success branch so the
   flag stays True across heavy-reconnect failures and the next attempt
   re-enters the heavy branch.

Tests:

- `tests/test_litellm/proxy/db/test_exception_handler_reconnect_retry.py`
  (new) — 9 tests covering the helper's contract: happy path, retry on
  transport error, no retry on data-layer errors, propagation when reconnect
  fails, propagation after second transport error, `hasattr` guard for
  partial mocks, fresh-coroutine-per-call invariant, explicit timeout
  override, default timeouts read off the prisma_client.
- `tests/test_litellm/proxy/db/test_prisma_self_heal.py` — adds:
  - `test_get_generic_data_retries_on_transport_error_for_config_table`
  - `test_get_generic_data_propagates_when_reconnect_fails`
  - `test_engine_confirmed_dead_persists_across_failed_heavy_reconnect`
    (regression test for the flag-reset bug).

All 16 self-heal tests + 9 helper tests + 535 auth/exception-handler tests
pass locally.
2026-04-28 23:44:34 -07:00
Yuneng Jiang
8c91c8b2c4 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_dbReconnectNonBlocking 2026-04-28 23:29:36 -07:00
Cursor Agent
3fb5056305
fix(mcp): address greptile review on short tool prefix
- server.py: drop the redundant server_id append in
  _get_filtered_mcp_servers_from_mcp_server_names. iter_known_server_prefixes
  already yields server_id unconditionally, so the manual append (and its
  misleading comment) was a no-op duplicate.
- utils.py: rewrite the SHORT_MCP_TOOL_PREFIX docstring to accurately
  describe the collision behaviour. The previous wording said collisions
  were 'cosmetic only', but a natural-hash collision IS a routing-correctness
  issue, which is precisely why we already added _assign_unique_short_prefix
  to rehash deterministically. The new comment cross-references that path.
- utils.py: restrict the first character of the short prefix to [A-Za-z]
  via a 52-char alphabet for position 0 only. The remaining two positions
  still use the full base62 alphabet. This keeps prefixes valid identifiers
  on every backend and gives 52*62*62 = 199_888 distinct prefixes (still
  comfortably more than any realistic deployment).
- tests: add coverage proving the first character of the prefix is always
  alphabetic across many server_ids and rehash attempts.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-04-29 03:59:40 +00:00
Sameer Kankute
af5b7be51d
Merge pull request #26742 from BerriAI/litellm_internal_staging
merge main
2026-04-29 09:20:12 +05:30
Cursor Agent
df3dbd18d6
feat(mcp): rehash short tool prefix on collision and cache per server
Two MCP servers can natural-hash to the same three-character base62
prefix. With 62**3 = 238_328 slots the birthday bound is ~488 servers
for 50% collision probability, so a single proxy hosting more than
~100 MCP servers has a non-trivial chance of seeing a collision in
practice — and a collision means tool names from two different servers
share a routing key, causing silent mis-routing.

Mitigation:

- compute_short_server_prefix(server_id, attempt=N) folds an attempt
  counter into the SHA-256 seed, so rehashes are deterministic and
  produce a fresh three-char prefix space per attempt.
- New MCPServer.short_prefix field caches the resolved (post-dedup)
  prefix on the model so it stays stable across the process lifetime.
- MCPServerManager._assign_unique_short_prefix walks attempts 0..N
  until it finds a prefix not already used by another server in the
  combined registry. Logs an INFO line when a rehash happens so
  operators have a breadcrumb if it ever does.
- Wired into every registration path: load_servers_from_config,
  add_server, update_server, reload_servers_from_database. The
  database reload path also carries the previously-resolved prefix
  forward so reloads don't churn it.
- get_server_prefix prefers the cached short_prefix when set, so the
  resolved value (not the raw natural hash) is used everywhere.
- iter_known_server_prefixes yields the cached short_prefix too, so
  reverse-lookup tolerance covers the rehashed form.

No-op when LITELLM_USE_SHORT_MCP_TOOL_PREFIX is disabled — the field
stays None and behaviour is unchanged.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-04-29 03:43:34 +00:00
Cursor Agent
fc49c181bc
feat(mcp): opt-in short-ID tool prefix to stay under 60-char tool name limit
Adds LITELLM_USE_SHORT_MCP_TOOL_PREFIX. When enabled, tool / prompt /
resource / resource-template names emitted from MCP servers are prefixed
with a deterministic three-character base62 ID derived from the server's
server_id (SHA-256 → base62) instead of the (potentially long)
alias / server_name. This keeps namespaced tool names well under the
60-character upper bound enforced by some model APIs while still letting
us distinguish MCP-routed tools from local tools.

Behavioural notes:

- Default off — when the env var is unset, the long-prefix behaviour
  is unchanged. The plan is to flip the default in a future release
  and remove the gate after a deprecation window.
- Prefix derivation is deterministic, so it is stable across processes,
  workers and restarts without any persistence layer.
- Reverse-lookup is tolerant: _create_prefixed_tools registers every
  known prefix form (alias / server_name / server_id / short ID) in
  the routing map and _get_mcp_server_from_tool_name resolves any of
  them. Old clients holding cached long-prefixed names continue to
  route correctly even after the flag is enabled.
- _get_allowed_mcp_servers_from_mcp_server_names accepts the short
  prefix in /mcp/{server_name}-style URLs.
- The OpenAPI tool-listing path now filters by the active server
  prefix instead of server.name so spec-backed servers benefit too.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-04-29 01:41:24 +00:00
Krrish Dholakia
fd32f29e39
Revert "lazy-load optional feature routers on first request (#26534)" (#26727)
This reverts commit 21ed38971d.
2026-04-29 00:21:41 +00:00
Michael-RZ-Berri
21ed38971d
lazy-load optional feature routers on first request (#26534)
Co-authored-by: Michael Riad Zaky <michaelr@Mac.localdomain>
2026-04-28 17:04:40 -07:00
Michael Riad Zaky
6052ce1017 cache LiteLLM_Config param reads in DualCache + batch scheduler-tick fetch 2026-04-28 16:29:50 -07:00
Sameer Kankute
2d2f540480
feat(proxy): add team-level search provider credential resolution
Allow search requests to resolve provider credentials from request metadata, team metadata, and default team settings with clear precedence, and expose this flow in proxy docs/UI with regression tests.

Made-with: Cursor
2026-04-28 16:58:03 +05:30
yuneng-jiang
761e124c17
Merge pull request #26460 from BerriAI/litellm_expired_dashboard_key_cleanup
feat(proxy): Add cleanup job for expired LiteLLM dashboard session keys
2026-04-27 20:22:05 -07:00
Ryan Crabbe
84527b0135
feat(proxy): add --timeout_worker_healthcheck flag for uvicorn worker triage
Adds a CLI flag (`--timeout_worker_healthcheck`, env `TIMEOUT_WORKER_HEALTHCHECK`)
that forwards to uvicorn's `timeout_worker_healthcheck` Config kwarg (added in
uvicorn 0.37.0). Lets operators raise the supervisor's worker-ping timeout above
the default 5s when triaging workers being killed and respawned under load.

The helper introspects `uvicorn.Config.__init__` and only sets the kwarg if
supported, otherwise prints a warning - so the existing uvicorn>=0.32.1,<1.0.0
floor pin is unaffected. Gunicorn and Hypercorn paths are unchanged (the uvicorn
supervisor isn't running there); the value is also not passed to the helper at
all on those paths so the "uvicorn too old" warning never fires spuriously.
2026-04-27 11:06:56 -07:00
OmriShukrun_
0304fe0dc5
fix noma v2 deepcopy crashing in build scan payload - new PR (#26605)
* Use auth key name if there are no app id in in headers or in extra_data

* use key alias instead of key name

* Fix

* last priority key alias

* Fix

* Add tests

* [Feat] Day-0 support for GPT-5.5 and GPT-5.5 Pro (#26449)

* feat(openai): day-0 support for GPT-5.5 and GPT-5.5 Pro

Add pricing + capability entries for the new GPT-5.5 family launched by
OpenAI on 2026-04-24:

- gpt-5.5 / gpt-5.5-2026-04-23 (chat): $5/$30/$0.50 per 1M
  input/output/cached input
- gpt-5.5-pro / gpt-5.5-pro-2026-04-23 (responses-only): $60/$360/$6
  per 1M input/output/cached input

Other fees (long-context >272k, flex, batches, priority, cache
discounts) follow the same ratios as GPT-5.4, with context window
retained at 1.05M input / 128K output.

No transformation / classifier code changes are required:
OpenAIGPT5Config.is_model_gpt_5_4_plus_model() already matches 5.5+ via
numeric version parsing, and model registration is driven from the
JSON. The existing responses-API bridge for tools + reasoning_effort
(litellm/main.py:970) already covers gpt-5.5-pro.

Tests:
- GPT5_MODELS regression list now covers gpt-5.5-pro and dated variants
- New test_generic_cost_per_token_gpt55_pro cost-calc test
- Updated test_generic_cost_per_token_gpt55 for long-context fields

* fix(openai): mirror reasoning_effort flags onto gpt-5.5 dated variants

gpt-5.5-2026-04-23 and gpt-5.5-pro-2026-04-23 were missing the
supports_none_reasoning_effort, supports_xhigh_reasoning_effort, and
supports_minimal_reasoning_effort flags that their non-dated
counterparts define. Reasoning-effort routing in OpenAIGPT5Config is
fully capability-driven from these JSON flags — since an absent flag
is treated as False for opt-in levels (xhigh), users pinning to a
dated snapshot would silently lose xhigh support and diverge from the
base alias on logprobs + flexible temperature handling.

Copy the flags onto both dated variants so every dated snapshot
inherits the base model's reasoning-effort capability profile.

Adds a parametrized regression test that asserts
supports_{none,minimal,xhigh}_reasoning_effort parity between each
dated variant and its non-dated counterpart, preventing future drift
when new snapshots are added.

* [Feat] Add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants) (#26361)

* feat(azure): add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants)

Azure variants of OpenAI's GPT-5.5 family. Microsoft has not yet
shipped GPT-5.5 on Azure OpenAI (latest GA on the Foundry models page
is GPT-5.4 as of 2026-04-24), but adding the entries day-0 mirrors the
established precedent for azure/gpt-5.4* (which were in the cost map
before the Azure rollout) so cost tracking and capability flags work
the moment customers deploy.

Schema follows the existing azure/gpt-5.4* shape:
- Same base/long-context pricing as openai/gpt-5.5*: $5/$30 chat,
  $60/$360 pro per 1M, with priority tier 2x base
- Azure variants drop the flex/batches keys (Azure has no flex tier)
  but keep priority pricing, matching gpt-5.4* precedent
- mode=chat for the thinking model, mode=responses for pro

reasoning_effort capability flags mirror the OpenAI variants exactly
since Azure proxies the same API contract: minimal rejection on both
chat and pro, low/none rejection on pro. Once #26456 (which sets
supports_low_reasoning_effort + minimal=false on openai/gpt-5.5*)
lands, OpenAI and Azure flag profiles align.

Tests pin entry presence + pricing for all four Azure variants and
verify the live-API-derived reasoning_effort flags.

* test: register supports_low_reasoning_effort in cost-map JSON schema

azure/gpt-5.5-pro and azure/gpt-5.5-pro-2026-04-23 added in this branch
carry supports_low_reasoning_effort=false. The strict
'additionalProperties: false' schema in
test_aaamodel_prices_and_context_window_json_is_valid rejected the new
key. Register it alongside the other supports_*_reasoning_effort
entries.

Note: the runtime side of this flag (code that reads it) lands in
#26456. Until that PR merges the flag is inert for both Azure and
OpenAI pro entries, but having the schema accept it lets cost-map
tests pass on either merge order.

* Use sanitize deep copy style to replace deepcopy usage

* Added test checking error is not happening anymore

* Added warning log when json copy failed

* Reduce to one change

* Fix spaces

---------

Co-authored-by: Ido Lavi <ido@noma.security>
Co-authored-by: yuneng-jiang <yuneng@berri.ai>
Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com>
Co-authored-by: TomAlon <tom@noma.security>
2026-04-27 08:51:26 -07:00
Sameer Kankute
3e4f9af955
Add support for azure entra discovery endpoint 2026-04-27 13:56:35 +05:30
clyang
3f5e28fcdc
Adding Cycraft XecGuard integration (#26011) 2026-04-27 08:58:38 +05:30
Tuhin Subhra Patra
9b78dc78c2
fix(proxy): invoke post-call guardrails on pass-through endpoint responses (#20270) (#26262)
* fix(proxy): invoke post-call guardrails on pass-through endpoint responses (#20270)

Wire post_call_success_hook into non-streaming pass-through response path,
gated on explicit guardrail config (opt-in only, no backwards-compat break).

- Call post_call_success_hook after reading non-streaming response body
- Build enriched hook_data with guardrails metadata and litellm_logging_obj
  at call site (avoids mutation of _parsed_body which is shared by logging)
- Handle ModifyResponseException with provider-agnostic error envelope,
  post_call_failure_hook, and defensive try/except
- Strip stale content-length when guardrail modifies response body
- Move ModifyResponseException to litellm.exceptions to break cyclic import;
  re-export from custom_guardrail for backwards compat
- Add call_type fallback in UnifiedLLMGuardrails for pass-through endpoints
  using CallTypes.pass_through.value enum

* test: add unit tests for pass-through post-call guardrails

5 tests covering the post-call guardrail invocation on pass-through endpoints:
- post_call_success_hook fires when guardrails configured
- post_call_success_hook skipped when no guardrails (backwards compat)
- ModifyResponseException returns 200 with provider-agnostic error
- UnifiedLLMGuardrails resolves call_type from logging_obj for pass-through
- ModifyResponseException re-export from custom_guardrail stays in sync
2026-04-27 08:58:22 +05:30