Commit graph

4669 commits

Author SHA1 Message Date
Yassin Kortam
c16c2cd6ee
fix(proxy): mark session/SSO/SAML cookies Secure behind a TLS-terminating reverse proxy (#39391)
* fix(proxy): mark session/SSO/SAML cookies Secure behind a TLS-terminating reverse proxy

litellm only sees a plain-HTTP hop when TLS terminates at a reverse proxy,
so cookie Secure attributes previously derived from (or defaulted without
regard to) the literal request scheme could be dropped in production. The
token session cookie set by every login path never carried Secure/HttpOnly/
SameSite at all.

Adds IPAddressUtils.is_request_https, a single trust-aware resolver used by
every cookie-setting call site: PROXY_BASE_URL, then X-Forwarded-Proto only
from a configured trusted proxy (general_settings.use_x_forwarded_for +
mcp_trusted_proxy_ranges), then the literal scheme. An unconfigured or
untrusted caller cannot spoof the header to force Secure on.

Resolves LIT-6748

* fix(proxy): make the shared session-cookie helper public, type new test helpers

set_session_token_cookie is imported across modules (ui_sso.py ->
proxy_server.py), so the leading underscore was misleading and breached
basedpyright's reportPrivateUsage budget with zero headroom. Also adds
missing parameter/return type annotations to the new test helper functions
per repo convention.
2026-09-02 16:42:29 -07:00
mateo-berri
4ebac321a1 merge: origin/litellm_internal_staging into litellm_lite_login_prefill_code 2026-09-02 16:38:30 -07:00
mateo-berri
8c646a916c feat(cli): pre-fill the SSO verification code in the browser when the proxy allows it 2026-09-02 16:19:17 -07:00
mateo-berri
ddc2582374 fix(vector-store): build the Router executor from the router kwarg and gate the SDK fallback on explicit config 2026-09-02 16:15:20 -07:00
Yassin Kortam
bc2370f3fd
test(proxy): verify NO_DOCS/NO_REDOC/NO_OPENAPI restrict every doc surface (#39378)
* test(proxy): verify NO_DOCS/NO_REDOC/NO_OPENAPI gate every doc surface

Confirms /docs, /redoc and /openapi.json are reachable by default and
each 404 with no schema in the body when NO_DOCS/NO_REDOC/NO_OPENAPI are
set, and that disabling them does not affect other routes.

* test(proxy): pin the production docs_url construction to the real helpers

Addresses Greptile 4/5 feedback: the disabled-path tests built a fresh
FastAPI() app, which could miss drift in the actual
FastAPI(docs_url=..., ...) call in proxy_server.py. This pins that
exact call site against _get_docs_url/_get_redoc_url/_get_openapi_url.
2026-09-02 16:06:04 -07:00
mateo-berri
61ae06d4d9 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_vector_store_request_embedding_resolution 2026-09-02 15:24:21 -07:00
Yassin Kortam
6c5fb0ef6f
fix(proxy): build coordination Redis from REDIS_* env vars unconditionally (#39410)
Coordination Redis (spend counters, budget-window enforcement, and the
reset_spend cache-eviction broadcast) previously only attached when a
deployment set general_settings.coordination_redis or
litellm_settings.cache. Bare REDIS_HOST/REDIS_PORT env vars alone did
nothing, so a multi-replica proxy with no cache block got no cross-pod
coordination at all: a key reset on one pod never cleared another
pod's stale budget enforcement.

The inferred Redis is pinged before being adopted, and a malformed
REDIS_CLUSTER_NODES/REDIS_SENTINEL_NODES value is tolerated too: env
vars can be set for an unrelated reason with nothing reachable there,
and guessing wrong must not turn a previously harmless in-memory-only
proxy into one that fails to boot or raises on its next cache write.
2026-09-02 15:21:15 -07:00
yucheng-berri
b4f5b6aa94
fix(logging): redact credential query params from the uvicorn access log (#39293)
* fix(logging): redact credential query params from the uvicorn access log

Raw virtual keys reached container stdout two ways:

- `GET /key/info?key=sk-...`, `/global/spend/report?api_key=sk-...`,
  `/key/spend/report`, `/spend/logs`, `/user/daily/activity` and the Gemini
  passthrough routes all put the credential in the request target, and
  `uvicorn.access` had no redaction filter (only `uvicorn.error` did).
- the key budget error interpolates `LiteLLM_VerificationToken.key_name`,
  a column with no enforced shape, into a message that is both logged and
  returned to the caller.

`SecretRedactionFilter` cannot be reused on an access logger: it collapses the
record into `record.msg` and clears `record.args`, and uvicorn's AccessFormatter
unpacks those args at emit time, so every access line would raise TypeError.
`AccessLogRedactionFilter` scrubs the positional args in place instead.

An access line is the one input to the secret regex an unauthenticated caller
controls end to end, so two bounds go with it. The request target is cut back to
a whole query parameter under 512 characters before it is scanned, since a half
parameter is too short to match its own pattern and would be logged raw, and the
dropped tail is not logged at all. The connection-string pattern is bounded too,
because its user half could previously re-scan the rest of the string from every
`://`: a 16 KB URL of `a://` pairs took 314s and now takes 0.12s, with the caps
set high enough that an RDS IAM auth token used as a DSN password still redacts.

Credential query params are terminated by `&` like the existing `key=` and
`sig=` patterns, so redacting one param no longer swallows the rest of the
request line, and a second credential in the same query string is now redacted
on its own instead of surviving once the first one stops the span. `key_name` is
echoed into the budget error only when it still has the masked `sk-...abcd`
shape `abbreviate_api_key` writes, so a value put there by a direct DB write or
a migration falls back to the key alias.

Also point the `/key/info` and spend-report examples at the sha256 hash both
endpoints already accept, so callers stop putting raw keys in URLs that
third-party access logs record.

Resolves LIT-5909

* test(logging): assert on emitted access lines instead of filter registration

The two registration tests checked that an AccessLogRedactionFilter instance
sits in uvicorn.access.filters, which is the shape of the code rather than its
behavior. Handing the logger a real access record and reading what a handler
wrote covers the same wiring and still fails when the registration is removed.

* fix(logging): redact percent-encoded credentials from access logs

?k%65y=sk%2D... is a working credential once the request parser decodes it,
but the redaction patterns match literal text and never see it. Decode the
request target as a detector and drop the query when decoding reveals a
secret. The decoded text is never logged back, so a %0A cannot forge a
following log line

Also accept any four non-space characters in the masked key_name check, since
abbreviate_api_key copies the last four characters of a custom key verbatim
and those can be punctuation or non-ASCII

* fix(auth): keep control codes out of the masked key label

/key/generate accepts a custom key ending in an escape sequence, and
abbreviate_api_key copies those four characters into key_name verbatim, so
the over-budget message carried them to a terminal and a log viewer. Bar
whitespace and C0/C1 control codes from the four, and keep everything else
2026-09-02 15:10:36 -07:00
devin-ai-integration[bot]
a76cb6feaf
feat(mcp): semantic tool search for the native MCP Gateway (#39404)
The mcp_tool_search virtual tool only did substring token matching, so a native MCP client asking for "FX" could not find a tool described as "foreign exchange rates" even though the same catalog is ranked by embeddings on /responses and /chat/completions.

Adds litellm_settings.mcp_tool_search (embedding_model, top_k, similarity_threshold, core_tools). With an embedding model the caller's authorized catalog from _list_mcp_tools is ranked by cosine similarity of name plus description; configured core tools the caller can reach come first and do not consume top_k. Without an embedding model the keyword fallback keeps the old behavior. Settings are hot-reloadable from the DB, exposed on /get and /update mcp_tool_search_settings, and editable from the Admin UI under MCP Servers > Tool Search. The embedding index is shared with agent_search via a new SemanticTextIndex.

Resolves LIT-6751

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 15:09:44 -07:00
devin-ai-integration[bot]
748c2026d7
fix(proxy): word database 503s by whether the fault is transient (#39256)
Permanent Prisma/query-engine faults keep the 503 status and no_db_connection type but stop claiming the database is temporarily unreachable. A permanent fault anywhere in the exception chain outranks the transport error that surfaced it. MCP bridge and DCR flows gain a faulted resolution state with matching wording. Resolves LIT-5208

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 15:05:55 -07:00
devin-ai-integration[bot]
346813b374
fix(proxy/db): keep prisma predicates from raising TypeError under a mocked prisma module (#39253)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 15:05:37 -07:00
mateo-berri
cdf8383d9e Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_fix_mcp_oauth_tool_fetch_auth
# Conflicts:
#	litellm/proxy/_experimental/mcp_server/rest_endpoints.py
2026-09-02 14:58:08 -07:00
Mateo Wang
86ca146ea2
Merge pull request #39271 from BerriAI/litellm_fix_presidio_sibling_callback_leak
fix(guardrails): track and tear down presidio sibling callbacks on delete and update
2026-09-02 14:39:47 -07:00
Yassin Kortam
8e65265bb4
fix(agents): redact secret litellm_params fields from all /v1/agents responses (#39389)
* fix(agents): redact secret litellm_params fields from all /v1/agents responses

Secret-bearing litellm_params fields (aws_secret_access_key, api_key, and
similar) are now write-only: list, get, create, update, and patch
responses always replace them with a fixed marker, regardless of caller
role. Editing an agent no longer requires resending a real credential --
an update that omits a sensitive field, or echoes the marker back,
preserves the stored value; a real value still rotates it.

* fix(agents): redact secrets nested inside dicts/lists in litellm_params too

Greptile found that a secret nested one level down under a
non-sensitively-named key, or inside a list of per-provider configs, was
neither redacted on read nor restored symmetrically on write (the marker
string could get persisted as the real value). Recurse into lists on the
read side, and mirror that recursion on the write side so restoration
isn't limited to top-level keys. Also fixes a regression the redact
rewrite introduced (a plain string leaf like a model name was being
misinterpreted as a JSON blob and redacted), and suppresses 3 new
test-quality-gate findings on an established repo-wide mocking pattern
this PR's new tests also use.

* fix(agents): guard list-position credential restore against misassignment

Two more real gaps Greptile/veria found in the recursive redact/restore
mechanism, verified directly against the exact reported shape
(litellm_params.model_list, each entry carrying its own nested
litellm_params.api_key/aws_secret_access_key) before fixing:

- Positional restoration inside a list could attach one entry's stored
  credential to a different entry if the list were reordered or resized
  between GET and PUT/PATCH. Restoration by index now only fires when the
  incoming and existing entries match on every non-secret field; otherwise
  the caller's own value is used (never a guessed cross-entry secret).
- A subtree collapsed to the flat REDACTED_BY_LITELM marker by the
  read-side recursion depth cap couldn't be recovered on write (the marker
  string itself would get persisted). Restore now recognizes that shape and
  recovers the whole existing subtree.

Both covered by regression tests mirroring the exact model_list shape
reported, mutation-verified.

* fix(agents): simplify list-entry credential restore to positional matching

The content-match guard from the previous commit fixed one Greptile
finding (cross-entry misassignment on reorder) but introduced a worse one:
it also rejected restoration whenever an entry's own non-secret fields
changed, which is the common case (rename a model_list entry while
leaving its own secret masked) -- silently dropping the stored credential
on an ordinary edit.

There is no stable per-element identity in a plain dict[str, object]
schema, so no rule can satisfy both 'restore whenever the entry itself
only had its secret masked' and 'never restore across a reorder' at once.
Positional correspondence is what every other part of this restore (and
the endpoints' full-replace-on-PUT semantics) already assumes, so drop
the content-match gate and rely on it here too: this fixes the common
case correctly and accepts cross-entry misassignment on a simultaneous
reorder-plus-masked-echo as a known, narrow, documented limitation (not a
leak between different agents or tenants, since it only reshuffles one
agent's own stored values). Tests updated to pin the accepted trade-off
explicitly rather than asserting it away, and to cover the previously
broken ordinary-edit case.
2026-09-02 14:28:44 -07:00
Yassin Kortam
25991fe78a
feat(auth): enforce configurable password policy and SSO-only login (#39381)
Adds a configurable password-strength policy (default: min 12 chars,
upper/lower/number/special, all individually toggleable, floored at 8
so a misconfigured minimum cannot disable the length check, and
unicode-aware so an accented letter cannot satisfy the special-
character requirement) enforced on every path that sets a local
user's password: /user/update, /user/bulk_update, and the invitation
onboarding claim flow.

Adds general_settings.disable_password_login_when_sso_enabled, which
rejects username/password login on /login, /v2/login and /v3/login
(including the UI_USERNAME/UI_PASSWORD admin fallback) once ANY
configured SSO provider is FULLY ready: every companion secret/
endpoint an OAuth provider needs, checked independently per provider
so a stray leftover client id for an unused provider can't mask a
different, fully configured one; and for SAML, the optional
python3-saml runtime being importable, checked without letting a
fully-missing package's ModuleNotFoundError take down password login
itself. SSO becomes the enforced boundary for interactive UI access
without an incomplete, mixed, or half-installed SSO setup locking
every admin out or breaking login outright. Master-key API access is
untouched, and unsetting the setting plus a restart restores password
login as the documented recovery path.
2026-09-02 14:28:13 -07:00
Yassin Kortam
646f3404a5
fix(security): restrict and validate file uploads at /v1/files and /upload/logo (#39379)
* fix(security): restrict and validate file uploads at /v1/files and /upload/logo

Extends fast-fail upload validation to every purpose at POST /v1/files,
not just purpose=batch: a configurable max_file_size_mb size cap and a
blocked_file_extensions denylist, plus rejection of filenames carrying a
directory-traversal component before anything is read, stored, or
forwarded to a provider.

Also fixes two concrete gaps found while auditing every upload surface:
the Azure Blob Storage backend derived a blob path's extension with
filename.split(".")[-1], which does not parse path structure and let a
crafted filename embed a directory traversal sequence into the stored
blob path; and POST /upload/logo (the admin UI logo upload) had no
role check at all, so any authenticated API key, not just a proxy
admin, could write a file to the server's disk.

* fix(lint): drop cast()/mutation from settings coercion, sync blocked_file_extensions on reload

Replaces the TypeAdapter+cast() reads of max_file_size_mb and
blocked_file_extensions with small isinstance-based validators, since the
codebase's cast() budget (LIT006) had no headroom left. Also adds the
blocked_file_extensions reload block that was missing from
_update_general_settings: it was registered as an editable setting but
never re-synced into runtime state, so a value set through the DB-backed
settings editor would silently never take effect (Greptile finding).

* fix(security): declare max_file_size_mb and blocked_file_extensions on ConfigGeneralSettings

The DB-backed general-settings update endpoints validate every field
through ConfigGeneralSettings.model_fields before persisting it, so
without these declarations an operator could never actually set either
setting through that path even though both were registered for the
Admin UI's settings editor and reloaded on config refresh (Greptile
finding). blocked_file_extensions is typed as a tuple, not a list, to
stay out of the immutable-collections lint budget; the stored JSON
value is unaffected since the raw request payload, not the validated
model, is what gets persisted.

* chore: regenerate schema.d.ts for the new ConfigGeneralSettings fields

* fix(security): normalize configured blocked_file_extensions casing

check_blocked_extension lowercased the uploaded filename's extension
before comparing but compared it against blocked_extensions verbatim,
so an admin-configured blocked_file_extensions: ['.EXE'] would never
match an uploaded payload.exe (Greptile finding). Normalizes the
configured values the same way at comparison time, and adds the
missing case (mismatched-case config, lowercase upload) as a
regression test, mutation-checked against the unfixed comparison.

* fix(security): restore caller-owned stream position after size inspection

_file_size_bytes unconditionally seeked back to 0 after measuring a
BinaryIO's length, discarding wherever the caller had actually
positioned it (Greptile finding). Saves and restores the original
position instead. Rewrites the existing test that had encoded the
old "always resets to 0" behavior as its expectation, and adds a
sibling case for the under-cap path; both are mutation-checked
against the unfixed always-reset-to-0 behavior.
2026-09-02 14:27:46 -07:00
Yassin Kortam
711430216e
fix(ui): preserve full AgentCore runtime ARN in agent edit form (#39382)
parseDynamicAgentForForm recovered a credential field's value from a
stored model string by splitting both the model_template and the model
on "/" and matching by array index. That breaks for any placeholder
value that itself contains "/", such as a Bedrock AgentCore runtime ARN
resource path (runtime/<runtime-id>), silently dropping everything
after the first slash when populating the edit form. Saving without
touching the field then persisted the truncated ARN.

Replace the index-matching split with a non-mutating template parse
(split on the placeholder pattern, escape and rejoin the literal
segments into a regex) so a placeholder captures everything it needs
regardless of embedded slashes. Also add a lightweight ARN-shape
validator for the AgentCore runtime ARN field, guarded against a
malformed pattern string, so a truncated value is rejected client-side
before it reaches the backend.

Resolves LIT-6737
2026-09-02 13:33:14 -07:00
mateo-berri
6fae4b3c39 fix(guardrails): keep the presidio output masker from unmasking after an in-memory update 2026-09-02 13:32:21 -07:00
mateo-berri
53a3d74b92 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_presidio_sibling_callback_leak 2026-09-02 12:55:37 -07:00
devin-ai-integration[bot]
987ab76921
fix(proxy): share per-model budget counters across replicas through the spend counter cache (#39375)
* fix(proxy): share per-model budget counters across replicas through the spend counter cache

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

* test(proxy): keep the shared fake Redis store immutable

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 12:40:59 -07:00
devin-ai-integration[bot]
cde9d94c36
feat(agentcore-a2a): derive runtime session id from A2A message.contextId (#39371)
Native AgentCore A2A always sent either a fresh generated runtime session id
or the single configured runtimeSessionId, so related turns lost context and
unrelated callers shared one AgentCore microVM. The runtime session id is now
params.message.contextId scoped to the calling key hash, then runtimeSessionId,
then generated, and is length-validated (33-256) before the header is signed.
Invalid ids surface as JSON-RPC -32602 / HTTP 400 instead of a 500.

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 12:40:24 -07:00
Ali Ahmed
a677242d6f
fix(headroom): stop re-compressing retrieved CCR content in client tool loops (#38591)
When the headroom_retrieve tool is exposed to a client that runs its own
tool-execution loop (the LiteLLM MCP gateway path), the client executes the
retrieve call and sends the recovered original content back as a tool result
on the next turn. The guardrail then compressed that row again, and because
CCR is content-addressed it collapsed back to the exact same hash it was just
retrieved from. The model never saw the expansion and the agent looped.

Hold tool-result rows that carry headroom_retrieve output back from the
compression service, the same way the live turn and trailing tool exchange are
already protected, so the expansion survives. Retrieve calls are matched by the
direct headroom_retrieve name and the mcp__<server>__headroom_retrieve gateway
name. Because a long gateway name is truncated past 64 chars in the
OpenAI-translated view the guardrail scans, the pairing also falls back to the
tool-call id read from the request's own untranslated messages, which is never
truncated.

Fixes #38558
2026-09-02 12:07:13 -07:00
Mateo Wang
b600f02fc2
Merge pull request #34788 from BerriAI/litellm_fix_s3_vectors_search
fix(vector_stores): s3 vectors search router bypass + rag query config drop + ui error swallow
2026-09-02 11:35:56 -07:00
Mateo Wang
a43228ef72
Merge pull request #39249 from BerriAI/litellm_router_settings_reject_unknown_keys
fix: apply optional_pre_call_checks and reject unsupported router settings on /config/update
2026-09-02 11:23:22 -07:00
devin-ai-integration[bot]
ffc0a8e428
fix: run access group key sync UPDATEs on the writer, not the read replica (#39128)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 10:51:17 -07:00
Mateo Wang
6ed2cdd428
Merge pull request #39238 from BerriAI/litellm_anthropic_models_display_name
feat(proxy): configurable display_name for the Anthropic-shaped /v1/models listing
2026-09-02 10:18:43 -07:00
mateo
385957e830 fix: reject constructor-managed router settings
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 07:49:25 +00:00
mateo
974b331a4d fix: accept persistable router settings
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 06:15:52 +00:00
mateo-berri
7cde2cd77f test(guardrails): type the presidio sibling test helpers precisely 2026-09-01 22:39:17 -07:00
mateo-berri
c18511be7d fix(guardrails): track and tear down presidio sibling callbacks
initialize_presidio registers up to three callbacks per guardrail but the
registry only kept the first, so deleting or re-syncing the guardrail left
the post_call siblings serving the old config. The initializer now returns
every callback it registered, the registry tracks primary and siblings per
guardrail id, delete purges all of them from every callback list, and
update pushes the new params into each while siblings keep their stage.
2026-09-01 22:28:38 -07:00
mateo-berri
4a5d0b8163 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_headroom_ccr_streaming_responses
# Conflicts:
#	litellm/llms/openai/responses/guardrail_translation/handler.py
#	tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py
2026-09-01 22:25:59 -07:00
mateo
cb511f70cc fix: preserve config update authorization order
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 02:20:51 +00:00
mateo
0608f0a00f fix: reject unknown runtime router settings
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 02:17:02 +00:00
Mateo Wang
92d453373a
Merge pull request #39036 from BerriAI/litellm_fix_stream_modify_response_chunks
fix(guardrails): deliver modify_response block as valid SSE on streaming chat and Responses
2026-09-01 18:44:10 -07:00
yucheng-berri
62f032cca5
fix(proxy): keep passthrough logging metadata and model_info dicts when team callbacks are wired (#39216)
* fix(proxy): keep passthrough logging metadata and model_info dicts when team callbacks are wired

Passing team callback vars into Logging(kwargs=...) makes get_litellm_params materialize a full litellm_params, where metadata and model_info default to None instead of being absent. Readers that resolve them as .get(key, {}).get(...) then raise, so any passthrough request from a team with logging callbacks 500s once a pre-call guardrail is on, and the router strategy loggers log a traceback per request.

* test(proxy): annotate the closure dicts the passthrough logging tests record into
2026-09-01 18:07:04 -07:00
devin-ai-integration[bot]
2b616fc479
feat(scim): add placeholder listing and merge so a shadowed account can be healed (#39231)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 01:04:53 +00:00
devin-ai-integration[bot]
47b9d838aa
perf(scim): resolve group members with one user table read per member (#39228)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 17:49:40 -07:00
devin-ai-integration[bot]
69029c139e
fix(mcp): report per-server outcomes in aggregate REST tools/list (#39232)
GET /mcp-rest/tools/list without server_id returned only the tools of
the servers that answered and silently dropped any server whose listing
failed (for example an OAuth-protected server without credentials), so
clients could not tell a partial listing from a complete one.

The aggregate response now carries a server_outcomes map keyed by server
alias with the same classified outcome (ok/auth_required/forbidden/...)
that the MCP protocol path already puts in _meta. Healthy tools and the
HTTP 200 status are unchanged.

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 17:48:01 -07:00
mateo-berri
fcd9052179 feat(proxy): honor model_info.display_name in the Anthropic-shaped /v1/models listing 2026-09-01 17:10:52 -07:00
Mateo Wang
3dac3f7a36
Merge pull request #35816 from BerriAI/litellm_anthropic_stream_model_alias
fix(proxy): report requested model on Anthropic streaming message_start
2026-09-01 16:52:41 -07:00
ryan-crabbe-berri
fc1a5fd7f9
Merge pull request #39206 from BerriAI/litellm_lit_3925_clear_team_key_create
fix: stop a cleared Team field from blocking personal key creation
2026-09-01 16:21:52 -07:00
devin-ai-integration[bot]
3888a85045
fix(budget): reject known estimates over remaining budget under fail_closed_budget_enforcement (#39214)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 16:13:26 -07:00
Yujong Lee
6805d01709 fix(vector-store): preserve aliases with embedding config 2026-09-01 15:33:18 -07:00
Yujong Lee
5635811726 fix(vector-store): route embeddings through router 2026-09-01 15:33:18 -07:00
Yujong Lee
bfa5eac76b fix(vector-store): resolve embedding aliases for search 2026-09-01 15:33:18 -07:00
ryan-crabbe-berri
55d638412b fix: stop a cleared Team field from blocking personal key creation
Clearing the Team combobox in the Create Key modal left team_id set to an
empty string, so /key/generate treated the request as team key generation
and failed with a team-not-found error for non-admin members.

TeamDropdown now emits null on clear, and GenerateKeyRequest normalizes an
empty team_id to None so the request runs the personal key path.
2026-09-01 15:17:00 -07:00
devin-ai-integration[bot]
846900320e
feat(alerting): slack alerts for per-user daily/monthly spend thresholds and spend anomaly detection (#38438)
* feat(alerting): slack alerts for per-user daily/monthly spend thresholds and spend anomaly detection

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

* test(alerting): use specific ValidationError matches in config rejection test

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

* fix(proxy): tolerate mocked slack alerting args when scheduling user spend scan

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

* fix(alerting): reject non-finite values in user spend alert settings

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 15:09:03 -07:00
devin-ai-integration[bot]
f9c6eda909
fix(cli): quote the Claude Code apiKeyHelper for cmd.exe on Windows (#39174)
* fix(cli): quote the Claude Code apiKeyHelper for cmd.exe on Windows

lite up and lite login --config-claude wrote the helper command with
POSIX shlex quoting, so a backslashed Windows install path came out
wrapped in single quotes that cmd.exe and PowerShell take literally.
Quote every token with the cmd.exe rules already used for agent shims
when running on Windows, and keep the POSIX output unchanged elsewhere.

Resolves LIT-6627

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

* test(cli): split the Windows apiKeyHelper with cmd.exe and C runtime rules

The invocation test pulled tokens back out with a regex, which cannot see
the doubled quotes or the percent guard quote_for_cmd emits. Model the two
parsers that read the helper on Windows instead and check argv round
trips for backslashed, spaced, metacharacter, percent and quoted tokens

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 15:08:00 -07:00
mateo-berri
a38dfecd96 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_stream_modify_response_chunks 2026-09-01 14:49:06 -07:00
Mateo Wang
b52b5d9421
Merge pull request #38734 from BerriAI/litellm_fix_bedrock_buffered_responses_stream
fix(bedrock): route streamed responses-API output through the unified guardrail
2026-09-01 14:44:41 -07:00