Commit graph

726 commits

Author SHA1 Message Date
devin-ai-integration[bot]
d08746feb1
feat(proxy): email alerts at configured percentages of a team member budget (#42665)
* feat(proxy): email alerts at configured percentages of a team member budget

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

* fix(alerting): label team member budget crossings as team member budget

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

* test(auth): cover the team member alert dispatch from _check_team_member_budget

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

* fix(email): drop the emoji from the team member budget alert template

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

* fix(proxy): ignore team member alert thresholds outside 1 to 100 on both the backend and the dashboard

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

* fix(proxy): bound team member alert threshold key length before int parsing

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

* test(integration): drop the legacy covers marker from the team member alert test

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

* fix(team): reject malformed team_member_max_budget_alert_emails on team writes

Thresholds outside 1-100, non-list recipients, and invalid emails now return 422 on
/team/new, /team/update and PATCH /team/{id} instead of being stored and silently
ignored. The value is stored canonically. Read-side LiteLLM_TeamTable is unchanged,
and the PATCH body stays a raw merge patch so a null threshold still deletes it.

* fix(auth): enforce and alert on team member budgets only in common_checks

The builder re-checked the team member budget inline before common_checks ran the same
check, so one request that crossed a team_member_max_budget_alert_emails threshold
dispatched two alerts. Drop the inline check; common_checks is the single authorization
point and already covers per-member rows, the team default member budget, zero-cost
skips and the cross-pod spend counter. Its 422 message now uses the TeamMember=user:team
form the builder and budget reservation already returned.

* Revert "fix(team): reject malformed team_member_max_budget_alert_emails on team writes"

This reverts commit 703e754b46.

* fix(alerting): keep BaseBudgetAlertType.get_event_message zero-arg

Requiring user_info broke existing callers and out-of-tree subclasses. The team member
label now comes from SlackAlerting.budget_alerts, so the interface and its Readme are
unchanged from main.

* fix(mcp): keep team member budget enforcement on the MCP OAuth auth dependency

The MCP OAuth dependency stops at _user_api_key_auth_builder and never reaches common_checks, so removing the builder's inline member budget check would have let over-budget members through there. Enforce it explicitly for that caller.

* fix(auth): keep main's team member budget enforcement, alert once per request

Restore the builder's team member budget check and 422 message exactly as on main and drop the MCP-only gate. The builder sends the member alert only on the request it rejects; common_checks sends it for requests that get past the builder, so no request alerts twice.

* test(integration): read team member alert deliveries without a shared accumulator

* test(integration): match team member alert deliveries by subject so other alerts cannot race the count

* refactor(proxy): build the team member alert threshold config without mutable collections

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

* refactor(proxy): collapse the alert recipient isinstance checks into one call

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

* test(integration): read the SMTP sink through lock-guarded snapshots and assert the exact deliveries

---------

Co-authored-by: ryan <ryan@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-26 02:42:45 +00:00
devin-ai-integration[bot]
7b4fd47c6e
fix(jwt): let x-litellm-team-id select DB membership teams when the token also carries a team claim (#43206)
* fix(jwt): let x-litellm-team-id select DB membership teams when the token also carries a team claim

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

* docs(jwt): describe header team selection under fallback_to_db_teams

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-25 15:28:14 -07:00
devin-ai-integration[bot]
d33d36ce86
fix(caching): propagate auth cache invalidation over Redis Cluster via a node-level pub/sub client (#43110)
* fix(caching): give Redis Cluster clients a node-level pub/sub client so auth invalidation propagates

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

* refactor(caching): shorten pub/sub client docstrings

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

* style(caching): noqa BLE001 on best-effort pubsub client close

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

* refactor(caching): satisfy LIT002/LIT006 in pubsub client derivation

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

* test(auth): rename fake pubsub hook to init_pubsub_client

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

* test(proxy): rename fake pubsub hook to init_pubsub_client

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

* test(caching): drop class-level health ping patch from pub/sub client tests

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

* fix(caching): close cluster pubsub pools and cover failure paths

* fix(caching): clean up expired cluster pubsub clients safely

* test(mcp): arm cancellation deadline after requests start

---------

Co-authored-by: joshua <joshua@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com>
2026-09-25 07:56:46 -07:00
devin-ai-integration[bot]
1d51a8dfc3
fix(proxy): authorize key model aliases the same way as team aliases (#43049) 2026-09-24 19:51:02 -07:00
devin-ai-integration[bot]
4584958574
feat(agents): add optional per-agent kill switch webhook (#42841) 2026-09-24 18:26:50 -05:00
devin-ai-integration[bot]
c2eb549ee6
feat(usage): search team keys beyond the top-N in the Team usage view (#42857) 2026-09-24 15:04:12 -05:00
devin-ai-integration[bot]
5a8ec13786
feat(usage): search keys beyond the top-N usage subset (#42827) 2026-09-24 15:04:03 -05:00
devin-ai-integration[bot]
e135a199ad
fix(proxy): fail parked DB lookups at a deadline and flip readiness while they stall (#42654)
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
* fix(proxy): fail parked DB lookups at a deadline and flip readiness while they stall

Under a load burst with a slow authentication database every request parked inside
the pod with no deadline while /health/readiness kept answering 200 (its own ping
gets a fresh connection), so the load balancer kept sending traffic until the pod hit
its memory limit, and the parked requests completed against the provider minutes
after every client had hung up

Every pre-request read (key, team, user, end user, budget, membership, organization,
object permission, jwt mapping, project, proxy budget, spend counter reseed) now runs
under one deadline, PROXY_DB_LOOKUP_DEADLINE_SECONDS (default 10 s). A lookup that
hits it fails the request with the existing 503 "authentication database is
temporarily unreachable" answer, honours allow_requests_on_db_unavailable, and never
triggers the transport reconnect (the transport is fine, the query is slow), which is
what turned the repro's stall into "too many clients". Writes stay unbounded

A deadline hit marks the pod stalled for PROXY_DB_LOOKUP_STALL_WINDOW_SECONDS
(default 30 s, 0 disables), during which /health/readiness answers 503 with
"db": "stalled" behind the same fail-open gate, so the pod leaves rotation before it
fills its memory. The existing litellm_in_flight_requests gauge already exposes the
parked set on /metrics

The deadline is enforced on the wall clock: bounded_db_lookup waits on the lookup
task with asyncio.wait and raises DBLookupDeadlineExceeded when the deadline passes
even if the lookup absorbs its cancellation, where asyncio.wait_for on 3.12+ would
sit on the cancelled task for as long as it takes

The failure spend-log row no longer re-runs the key and team lookups when the
failure itself is a database connection or deadline error, so a request that hit
the deadline is answered after one deadline instead of two

* fix(proxy): bound the spend counter gate wait and narrow the stalled lookup shortcut

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

* fix(proxy): keep the global spend lookup on the prisma client handle

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

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@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-24 10:09:49 -05:00
devin-ai-integration[bot]
b0407ad33e
ci: add merge smoke checks workflow with loopback-only harness and 11 curated cases (#42709)
* ci: add dashboard and core smoke checks across supported Python versions

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

* ci: tighten merge smoke harness and keep mapped test diffs additive

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

* ci: terminate proxy on readiness timeout and use contextlib.suppress in teardown

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

---------

Co-authored-by: yuneng <yuneng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-23 11:01:08 -07:00
Oliver Jensen
bc3b5b1d5b
fix(proxy): revoke UI session tokens on logout and password change (#42463)
* fix(proxy): revoke UI session tokens on logout and password change

Adds POST /session/logout to revoke the presented UI session key server
side (previously logout was client-side only and the key stayed valid
until expiry). Password changes now revoke the user's other UI sessions:
self-change keeps the caller's session, admin reset and onboarding claim
revoke all. The BYOK OAuth cookie auth now re-resolves the embedded key
against the DB so revoked sessions get a 401.

* fix(proxy): satisfy B008 budget and backend allowlist for /session/logout

* refactor(proxy): satisfy type-discipline budget in session_endpoints
2026-09-23 10:31:38 +02:00
devin-ai-integration[bot]
075536eca1
chore(cost-map): remove models past their deprecation date (#42435)
* chore(cost-map): remove models past their deprecation date

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

* test(cost-calc): drop the empty parametrize left behind by the gemini web search removal

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

* fix(cost-map): drop merge base block left by conflict resolution

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

* test(cost-calc): drop gemini image cost tests pinned on removed model

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

---------

Co-authored-by: kerry <kerry@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-22 21:19:26 +00:00
joshua-berri
0d0f73cd14
fix(mcp): restore legacy SSE and bounded cancellation cleanup (#42382)
* fix(mcp): restore legacy SSE and bounded cancellation cleanup

* fix(mcp): drain termination despite repeated task cancellation

* fix(mcp): admit virtual keys on legacy SSE message routes

* fix(mcp): preserve cancellation through late transport failures

* fix(mcp): preserve process exits during cleanup

* test(mcp): drain idle sockets before server shutdown

---------

Co-authored-by: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com>
2026-09-22 11:27:50 -07:00
devin-ai-integration[bot]
08639fcf42
fix(jwt): say x-litellm-team-id matched no team id or alias in the 403 (#42495)
* fix(jwt): say x-litellm-team-id matched no team id or alias in the 403

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

* fix(jwt): tell the caller when x-litellm-team-id names an alias shared by several teams

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

* fix(jwt): deny a shared x-litellm-team-id alias exactly like an unknown value

A distinct 403 for an alias several teams share was raised before the
allowed-teams check, so any JWT could probe which aliases exist. The
alias lookup now treats the duplicate as a miss, and both denials say
the value does not resolve to a team id or a unique team alias, which
is true for unknown, unauthorized and duplicate values alike

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

---------

Co-authored-by: ryan <ryan@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-22 10:51:49 -07:00
Mateo Wang
f3c920a06b
fix(bedrock/claude_platform): strip body params the AWS endpoint rejects (#31203)
* fix(bedrock/claude_platform): strip body params the AWS endpoint rejects

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

* test(bedrock/claude_platform): assert exact bodies through a strict fake gateway for every workspace alias and auth mode

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

* fix(proxy): block Claude Platform workspace id aliases in request bodies without admin opt-in

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

---------

Co-authored-by: Venkata Donavalli <vdonavalli@live.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: shrey kharbanda <shreshth@berri.ai>
2026-09-22 09:57:05 -07:00
devin-ai-integration[bot]
071cb49d32
fix(jwt): accept a team alias in x-litellm-team-id (#42445)
* fix(jwt): accept a team alias in x-litellm-team-id

The header only matched canonical team ids, so a JWT caller selecting one of their teams by its alias got a 403 even though they belonged to it. The header value is now resolved through the existing alias lookup before the JWT allowed-team check and the DB membership fallback, while a value that is already a team id never costs an alias lookup and denials keep naming the value the caller sent

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

* fix(jwt): only alias a header team id the database provably lacks

Under fallback_to_db_teams a header value whose team row read fails for any reason other than TeamNotFoundError now keeps the membership denial instead of falling through to the alias lookup, so a degraded read cannot select a different team that carries the value as an alias. Drops the HeaderTeam docstring that only restated its fields

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

---------

Co-authored-by: ryan <ryan@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-22 02:22:00 -07:00
devin-ai-integration[bot]
25ebb9458c
fix(proxy): surface a database outage from the user read as 503 no_db_connection (#42399)
get_user_object wrapped every failed read, a refused connection included, in
ValueError("User doesn't exist in db ..."), so JWT callers got a 401 naming a
missing user while Postgres was down and virtual-key callers got 503
no_db_connection for the same outage. A connection or transport error now
propagates as-is and the auth exception mapper answers 503 no_db_connection;
a genuinely missing row and query-level errors still answer 401.

The MCP auth and token-exchange docstrings and the exception-chain helper's
docstring described the old wrap and are updated to the new contract.

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-09-21 20:52:41 -07:00
devin-ai-integration[bot]
3252852b0f
fix(auth): fail closed when the JWT single-team fallback or compact editor membership read hits a DB outage (#42344)
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-09-21 20:04:30 -07:00
devin-ai-integration[bot]
e7cd97c6b6
fix(proxy): release unclaimed budget reservations at request end (#42304)
* fix(proxy): release unclaimed budget reservations at request end

* fix(proxy): release unclaimed budget reservations of websocket sessions too

* test(proxy): drop the structural middleware inheritance check

* fix(proxy): claim the budget reservation on streaming pass-through before its cost callback

The SSE chunk processor hands its success handler to the logging worker
after the response, so the request-end release freed the reservation
first and left the key unguarded until the worker drained. Claim it at
both end-of-stream hand-offs, the immediate enqueue and the coroutine
parked for deferred dispatch.

Give the xai realtime test double the litellm_params attribute every
real Logging object carries, since the wrapper now reads it.

* test(pass-through): give the vertex streaming test doubles a litellm_params dict

The spec'd Logging mocks in test_vertex_ai_anthropic_streaming_cost_injection.py
lacked the instance attribute the chunk processor now reads to claim the budget
reservation. Also restores main's _lazy_openapi_snapshot.json: the branch's copy
had been regenerated under Python 3.14, which dedents one docstring description
that the CI regeneration on Python 3.12 keeps indented, and the PR adds no lazily
loaded route, so main's file is the correct one.

* fix(pass-through): claim the budget reservation only after its cost callback is enqueued

Every pass-through success hand-off stamped callback_bound before handing the
coroutine to the logging worker. When that enqueue raised, the reservation stayed
claimed with no callback left to reconcile it, so the request-end release skipped it
and the reserved cost stayed pinned on the key's counter. Enqueue first, then claim,
so a failed hand-off leaves the reservation for the request-end release.

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-09-21 19:51:12 -07:00
Yassin Kortam
9cc5b78c33
Merge pull request #41634 from BerriAI/litellm_agent_access_groups
feat(agents): attach access groups to agents and enforce them for models, MCP servers and agent calls
2026-09-21 19:13:46 -05:00
ryan-crabbe-berri
582c664168
Merge pull request #42346 from BerriAI/litellm_jwt_team_allowed_routes_passthrough_grant
fix(auth): let jwt team_allowed_routes paths grant auth=true passthrough
2026-09-21 16:38:05 -07:00
ryan-crabbe-berri
0efcb9956d
Merge pull request #42278 from BerriAI/litellm_cherry_pick_password_breach_reset
feat(auth): breached password detection, self-service change-password and forced password reset
2026-09-21 15:47:46 -07:00
yassin
82eef2fcca fix(proxy): scope agent permissions to invoking caller
An agent key that echoes the x-litellm-user-id / x-litellm-team-id headers
forwarded by /a2a is capped at that user's and team's models, MCP servers
and agents, on top of its own grants and access group ceiling. The echoed
ids only narrow, and nested A2A hops forward the original human caller

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 22:32:32 +00:00
ryan-crabbe-berri
dac88cc6d0 fix(auth): keep blanket wildcards and team-less jwts out of the passthrough grant
A team_allowed_routes entry that names no path segment, such as * or /*, is a blanket grant like a named route group, so it no longer opens auth=true passthroughs. The grant in the shared route check now also requires a team on the JWT token, because team_allowed_routes should not apply to a JWT that resolved no team
2026-09-21 15:28:25 -07:00
ryan
8a1c6e49fb feat(auth): only allow password-login dashboard sessions to call /user/password/change
Password login now stamps login_method=username_password into the UI session key
metadata, and change_password rejects any caller that is not a litellm-dashboard
key carrying that marker with 403 before the user row is read. SSO sessions and
user-associated virtual keys can no longer use the endpoint as a current_password
guessing oracle. The forced-reset session is still minted by the password login
path, so it keeps access to the endpoint

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 22:22:12 +00:00
ryan-crabbe-berri
a8003102b2 fix(auth): let jwt team_allowed_routes paths grant auth=true passthrough
Explicit paths and trailing-wildcard prefixes in litellm_jwtauth.team_allowed_routes
passed the JWT route check but were then denied by the auth-enforced passthrough
gates, which only read allowed_passthrough_routes from key or team metadata. Both
gates now also accept an explicit team_allowed_routes entry for tokens built by JWT
auth. Named route groups still never grant, and virtual keys, including JWT-mapped
ones, stay key-scoped
2026-09-21 15:11:48 -07:00
Mateo Wang
7b8bc54237
Merge pull request #42036 from BerriAI/litellm_team_membership_lookup_fail_closed
fix(auth): fail closed when the team membership lookup hits a db outage
2026-09-21 14:58:46 -07:00
ryan
7c8aed072f chore: merge main into litellm_cherry_pick_password_breach_reset
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 21:54:35 +00:00
yuneng-jiang
5e0512b611
Merge pull request #42291 from BerriAI/litellm_lit7597_detach_credential
fix(proxy): detach stored credential when model editor selects None
2026-09-21 14:05:28 -07:00
kerry
9611af7817 Merge remote-tracking branch 'origin/main' into litellm_upgrade_banner_changelog_stats 2026-09-21 20:06:41 +00:00
yuneng
10d343c3ee fix(proxy): detach stored credential when model editor selects None (LIT-7597)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 19:22:57 +00:00
ryan
6bfbc7dba4 test(auth): pass throttle to authenticate_user in reset-required tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 18:53:39 +00:00
ryan
3fe405a5cc feat(auth): breached password detection, self-service change-password and forced password reset
Cherry-pick of merge commit b3882d8e43 (PRs #39321, #39562, #40107), which landed on litellm_internal_staging instead of main.

Co-authored-by: ojensen-berri <ojensen@berri.ai>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 18:48:35 +00:00
kerry
4595d18047 Merge remote-tracking branch 'origin/main' into pr40429 2026-09-21 17:42:28 +00:00
ryan-crabbe-berri
cc1a3157d3
Merge pull request #42121 from BerriAI/litellm_utils_model_info_lookup
feat(proxy): add GET /utils/model_info to look up cost map info for unregistered models
2026-09-21 10:42:28 -07:00
Yassin Kortam
def37c6532
Merge pull request #42097 from BerriAI/litellm_budget_exceeded_422
fix(proxy): return 422 instead of 429 for BudgetExceededError
2026-09-21 08:35:30 -05:00
joshua-berri
c25601fb5f
Merge pull request #42064 from BerriAI/litellm_fix_jwt_deactivated_users_8217
fix(auth): reject deactivated JWT users and refresh cached status
2026-09-20 19:35:53 +00:00
ryan
59f98d5363 feat(proxy): add GET /utils/model_info to look up cost map info for unregistered models 2026-09-20 10:30:26 +00:00
kerry
3777b0b0d9 test(proxy): document why the release info route check asserts by not raising
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-20 09:54:29 +00:00
yassin
d338d3f2d2 style(agents): tidy typing and docstring in access group ceiling helpers
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-20 09:40:46 +00:00
yassin
31caa98a41 Merge remote-tracking branch 'origin/main' into litellm_agent_access_groups
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	tests/test_litellm/proxy/auth/test_auth_checks.py
2026-09-20 08:52:45 +00:00
kerry
a36a62a7f9 Merge remote-tracking branch 'origin/main' into litellm_upgrade_banner_changelog_stats 2026-09-20 08:44:11 +00:00
yassin
f9244749e0 fix(proxy): return 422 instead of 429 for BudgetExceededError
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-20 06:00:06 +00:00
Joshua Valluru
124196cbaa fix(auth): preserve scope-admin email policy during status lookup 2026-09-19 19:48:53 -07:00
Joshua Valluru
6ea74d70f1 fix(auth): enforce SCIM status for admin JWTs and refresh SCIM caches 2026-09-19 19:33:47 -07:00
Mateo Wang
b6dd3d932c
Merge pull request #42019 from BerriAI/litellm_master_key_boot_enforcement
feat(proxy)!: refuse to start with an unset, empty, or publicly known master key
2026-09-19 19:04:02 -07:00
ryan-crabbe-berri
ecf17513fb refactor(proxy): rename the local development override to dangerously_permit_weak_or_unset_master_key so the name says exactly what it permits 2026-09-19 18:53:14 -07:00
ryan-crabbe-berri
99f99cfb46 fix(proxy): stop the boot when the requested master key migration fails, unless allow_requests_on_db_unavailable tolerates the outage 2026-09-19 18:10:52 -07:00
Joshua Valluru
a82f0a0bd2 fix(auth): reject deactivated JWT users and invalidate cached status 2026-09-19 18:10:49 -07:00
ryan-crabbe-berri
38d776bd2b feat(proxy): re-encrypt stored secrets at boot from LITELLM_MIGRATE_FROM_MASTER_KEY so an unsafe key can be replaced while the proxy refuses to start
Rotating through POST /key/regenerate needs a running proxy, which a refused boot does not have. The refusal now counts the stored values that decrypt under the unsafe key. When there are none it only asks for a new key. When there are some it also asks for LITELLM_MIGRATE_FROM_MASTER_KEY, and the next boot with a safe key re-encrypts them and logs that the variable can be deleted. Leaving the variable set afterwards is a no-op with one notice.
2026-09-19 17:32:58 -07:00
mateo-berri
b3b280d463 test(auth): model the membership row read in the fakes the loader now reaches 2026-09-19 16:32:22 -07:00