Commit graph

5728 commits

Author SHA1 Message Date
Yassin Kortam
6d8a960e1d
Merge pull request #41667 from BerriAI/litellm_mcp_client_allowlist
feat(mcp): allowlist MCP client applications at the gateway
2026-09-18 17:48:36 -07:00
yassin
e2141da81e fix(ui): treat MCP allowed clients with an empty alias or value as malformed
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 00:23:11 +00:00
yassin
c32309fb2d feat(ui): show MCP allowed clients as cards edited in a dialog
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 00:13:44 +00:00
Mateo Wang
3490754e65
Merge pull request #41871 from BerriAI/litellm_bedrock_eager_input_streaming
feat: honor eager_input_streaming on Bedrock and Anthropic Claude tools
2026-09-18 16:05:10 -07:00
yassin
da603c629b fix(ui): surface a malformed stored MCP allowlist as deny-all and let Save replace or remove it
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 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-18 22:37:30 +00:00
yassin
2231a3ca43 feat(mcp): give each allowed MCP client an alias and a value
mcp_allowed_clients entries become {alias, value} objects: the value is what the JWT claim or header must equal, the alias is the name the dashboard and logs show. The Network Settings section is renamed Allowed Clients with one alias/value row per client

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 22:29:17 +00:00
ryan-crabbe-berri
bc6b540205
Merge pull request #41888 from BerriAI/litellm_mcp_servers_my_connections_link
feat(ui): link MCP Servers page to the user's connected MCP servers
2026-09-18 15:27:05 -07:00
joshua
c9c4f21b55 fix(ui): let MCP Servers header actions wrap on narrow screens
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 22:10:43 +00:00
Mateo Wang
7283293d83
Merge pull request #41138 from BerriAI/litellm_bedrock_files_s3_endpoint_url
fix(bedrock): carry s3_endpoint_url and s3_region_name into file content downloads
2026-09-18 15:04:01 -07:00
joshua
19ef8e47a6 feat(ui): link MCP Servers page to the user's connected MCP servers
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 22:01:56 +00:00
ryan-crabbe-berri
2332d3f183
Merge pull request #40737 from BerriAI/litellm_logs_user_email_display
fix(ui): show internal user email in logs table and log detail drawer
2026-09-18 14:20:16 -07:00
Yassin Kortam
6759f28e73
Merge pull request #41557 from BerriAI/litellm_azure_speech_passthrough
feat(proxy): add Azure AI Speech pass-through route
2026-09-18 14:05:55 -07:00
ryan-crabbe-berri
c21e86a443 feat(ui): reset a team member's spend from the Members tab
Every member now carries a membership row, so a member who spent with no budget is over budget the moment a member budget is added later. The only fix was POST /team/{team_id}/member/{user_id}/reset_spend, which had no UI.

The Members tab gets a Reset spend action on rows that have current cycle spend. It confirms in a dialog, posts reset_to 0 through the typed client, and refreshes the team without remounting the page so the tab stays open. A team admin does not see it on their own row because the backend rejects that reset
2026-09-18 13:54:28 -07:00
yassin
9917375d4b Merge remote-tracking branch 'origin/main' into litellm_mcp_client_allowlist
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/_experimental/mcp_server/server.py
2026-09-18 20:46:50 +00:00
yassin
57fd311f3a Merge remote-tracking branch 'origin/main' into litellm_azure_speech_passthrough 2026-09-18 20:32:32 +00:00
yassin
737c6e1b2d Merge remote-tracking branch 'origin/main' into litellm_mcp_admin_terminate_sessions_revoke_credentials
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	tests/test_litellm/proxy/config_resolvers/test_settings_store.py
2026-09-18 20:32:31 +00:00
yucheng-berri
88e150bb59
Merge pull request #40982 from BerriAI/litellm_lit5285_login_rate_limit_v2
* feat(proxy): limit repeated failed Admin UI sign-in attempts

The Admin UI sign-in endpoints accept an unbounded number of password attempts.
All three call authenticate_user, and none of them keeps any record of how many
times a given caller has already been refused, so a misbehaving or misconfigured
client can retry indefinitely at full speed.

A LoginThrottle is now a required argument to authenticate_user, so the accounting
lives at the one function all three endpoints share and a fourth endpoint cannot be
added without deciding what to pass. Failures are counted per username and source
address over a fixed window and further attempts are refused with 429 and a
Retry-After header. The check runs before the database lookup and before the
password comparison, so a refused caller does no further work.

Only genuine credential rejections count. Configuration errors do not, a refused
attempt does not extend the window, and a successful sign-in clears the bucket.
The username is case folded because the user lookup is case insensitive, so casing
cannot multiply the allowance. Both credential rejections now return one identical
message. SSO is unaffected; it never calls this function.

max_failed_login_attempts (10) and failed_login_window_seconds (900) are read from
config.yaml, with LITELLM_DISABLE_LOGIN_RATE_LIMIT to turn the accounting off. They
are deliberately not database backed, so editing YAML always wins and an operator
refused by a bad value can recover.

* test(proxy): clear failed-login TTLs when resetting the throttle between tests

* feat(proxy): harden Admin UI login throttling

* docs(proxy): clarify login throttle configuration

* fix(proxy): keep the login throttle inside the type budget and fail safe on secret errors

* fix(proxy): warn about per-worker sign-in counters without a module global

* fix: honor environment login throttle settings

* fix: satisfy login setting type checks

* refactor(proxy): keep authenticate_user within the C901 budget after merge

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

* fix(proxy): write failed-login counters and their expiry in one Redis call

Use RedisCache.async_increment_with_floor (a single Lua INCRBY + EXPIRE) for the
shared login counters instead of the two-step INCRBYFLOAT then EXPIRE, so a
counter can never be committed to Redis without its expiry. The repair in
_remaining_window now only covers expiries stripped out of band (PERSIST, a
restore) and uses the same atomic call

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

* fix(proxy): resolve the login rate limit kill switch once per process

Reading LITELLM_DISABLE_LOGIN_RATE_LIMIT through get_secret_bool on every
unauthenticated sign-in attempt meant a hosted secret manager in read mode
was queried once per password guess, before any counter was checked

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

* fix(proxy): count failed sign-ins in Redis alone while it answers

Every worker spends one shared budget and a successful sign-in clears it for all
of them. This worker's own counter is only consulted while Redis raises, so an
outage degrades to per-worker accounting instead of switching the control off

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

* fix(proxy): keep counting the failed sign-ins Redis missed once it answers again

A guess is recorded in exactly one place, Redis or this worker's own store when Redis
refused it, so the count is the sum of the two. Redis is read through
async_batch_get_counts, which raises on failure, instead of async_get_cache, which
swallows it into None and read as an empty counter

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

* refactor(proxy): read the shared sign-in counter through a tuple of keys

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

* refactor(caching): spell out the key collections batch_get_counts accepts

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

* fix(proxy): warn about per-worker login counters even without general_settings

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

* chore(ui): regenerate schema.d.ts after merging main

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

* refactor(proxy): inject settings and Redis cache into LoginThrottle.from_request

Removes the runtime import of proxy_server from login_throttle so the
throttle module no longer participates in the import cycle CodeQL
flagged (py/cyclic-import). Callers pass general_settings and
redis_usage_cache explicitly; behavior is unchanged.

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

* test(proxy): drop the section banner comment from the login tests

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

* refactor(proxy): assert separate login counter stores in the spray regression test instead of a comment

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

* feat(proxy): throttle failed Admin UI sign-ins per source and source/username

Replace the username-global lockout with counters keyed by source address and by
source/username pair. Each has a fixed counting window (60s) and a separate
block TTL (300s). Blocks are soft: a correct password still signs in, wrong
passwords from a blocked key take one of 5 held slots per worker and are held
30s before a 429. Once a pair is blocked its failures stop counting against the
source. The source scope runs only when trusted_proxy_ranges is set, IPv6 is
grouped by /64, and per-source limits accept IP and CIDR overrides with
longest-prefix matching. Redis is authoritative through one Lua script per
failure, with bounded per-worker fallback when Redis raises.

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

* test(proxy): explain the internal patches in the login throttle tests

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

* fix(proxy): key held sign-in attempts on the source while the source is blocked

An active source block now takes precedence over a pair block, so every blocked username behind one blocked source shares the source's five held slots instead of getting five each

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

* fix(proxy): apply source overrides to IPv4-mapped IPv6 sign-in sources

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

* fix(proxy): catch only Redis failures when falling back to local login counters

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

* fix(proxy): type the login throttle's local store and pass frozen Redis script arguments

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

* fix(proxy): keep the sign-in hold pool from refusing a correct password

The held-attempt cap ran before the password check, so five parked wrong
guesses from a blocked source turned the soft block into a lockout for the
real user. The slot is now taken only after a wrong password, and the
pool-full refusal carries the block's remaining time as Retry-After

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

* test(proxy): stub DATABASE_URL in the hold-pool regression test so it passes off the dev box

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

* chore(proxy): drop a comment that restated the NUM_WORKERS assignment

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

* fix(proxy): match IPv4-mapped IPv6 peers against IPv4 trusted proxy ranges

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

* fix(proxy): keep mapped-notation trusted proxy ranges matching mapped peers

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

* feat(proxy): hard-block throttled Admin UI sign-ins with no credential bypass

A blocked source, or source and username pair, is now refused with 429 before the database lookup and password check, in place of the soft block that held wrong guesses for 30 seconds and let a correct password through. The env admin credentials and the master key typed into the login form are refused like any other credential while blocked; recovery is the master key as an API bearer token, which never goes through the sign-in path

trusted_proxy_ranges: [] now means clients connect directly, so the peer address is the source and the per-source limit stays on. Only an unset or malformed value leaves the topology unknown, warns at startup and turns the per-source limit off

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

* refactor(proxy): move login throttle sentinels into constants

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

* feat(proxy): derive the per-username sign-in allowance from the address limit

The per-address-and-username allowance is now half the effective address allowance, rounded up, instead of a separate max_failed_login_attempts_per_user setting. A per-address override therefore raises or effectively removes both limits for that address, and no second override table is needed

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

* refactor(proxy): raise the sign-in block explicitly and type the empty settings mapping

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

* feat(proxy): round the per-username sign-in allowance down and exempt an address with an override of 0

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

* test(proxy): import LoginThrottle under TYPE_CHECKING for the throttle helper annotation

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

* fix(proxy): break ties between equivalent login limit overrides deterministically

Two spellings of one network share a prefix length, so the exemption wins the tie, then the higher limit, regardless of mapping order

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

* fix(proxy): treat a malformed trusted_proxy_ranges entry as an undeclared topology

A list with an entry that is not an address or CIDR range no longer switches
the per-source Admin UI sign-in limit on against the direct peer address, so a
typo cannot make a shared ingress address the bucket for every user behind it

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

* fix(proxy): reject blank trusted_proxy_ranges entries before they are dropped

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

---------

Co-authored-by: Yucheng Zhu <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 13:18:50 -07:00
Yassin Kortam
2e46b10320
Merge pull request #41293 from BerriAI/litellm_usage_key_free_aggregate_split
perf(proxy): split aggregated usage query into key-free rollups and bounded top-N keys
2026-09-18 13:16:49 -07:00
yassin
1feffc3635 Merge remote-tracking branch 'origin/main' into litellm_mcp_admin_terminate_sessions_revoke_credentials 2026-09-18 20:16:22 +00:00
ryan-crabbe-berri
6e3b6d6d03
Merge pull request #41830 from BerriAI/litellm_scim_multivalued_optional_value
fix(scim): accept entitlements and roles entries without a value on SCIM user PUT
2026-09-18 13:13:51 -07:00
mateo-berri
d1563e0b55 feat: honor eager_input_streaming on Bedrock and Anthropic Claude tools 2026-09-18 13:09:13 -07:00
yassin
191ca14872 Merge remote-tracking branch 'origin/main' into litellm_usage_key_free_aggregate_split 2026-09-18 20:05:54 +00:00
Yassin Kortam
2edda5aec3
Merge pull request #41757 from BerriAI/litellm_typesafe_compaction_guardrail
feat(guardrails): add TypeSafe Jev relevance-based compaction guardrail
2026-09-18 12:56:52 -07:00
yassin
fe8cf02823 feat(mcp): resolve the allowlisted client identity from the JWT claim or an opt-in header instead of clientInfo.name
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:27:43 +00:00
yassin
c5919a3c0e Merge remote-tracking branch 'origin/main' into litellm_mcp_client_allowlist
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/_experimental/mcp_server/server.py
#	litellm/proxy/proxy_server.py
2026-09-18 19:01:08 +00:00
ryan-crabbe-berri
ed9e666b54
Merge pull request #41351 from BerriAI/litellm_routing_groups_atomic_validation
fix(router): validate routing_groups at save time and keep invalid DB groups from blocking SSO load
2026-09-18 11:59:21 -07:00
yassin
1e6b33ffab Merge remote-tracking branch 'origin/main' into litellm_azure_speech_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	helm/litellm/templates/ingress.yaml
#	litellm/proxy/_types.py
#	litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
#	litellm/proxy/pass_through_endpoints/success_handler.py
#	terraform/litellm/aws/locals.tf
#	terraform/litellm/gcp/locals.tf
#	tests/test_litellm/proxy/middleware/test_billable_request_metrics_middleware.py
2026-09-18 18:42:22 +00:00
yassin
048aaad627 Merge remote-tracking branch 'origin/main' into litellm_mcp_admin_terminate_sessions_revoke_credentials 2026-09-18 18:39:16 +00:00
Yassin Kortam
0594dd7caf
Merge pull request #41539 from BerriAI/litellm_vault_login_secret_namespace
feat(vault): add separate login and secret namespaces for HashiCorp Vault
2026-09-18 11:38:49 -07:00
Yassin Kortam
84ae0805ba
Merge pull request #41620 from BerriAI/litellm_team_member_temp_budget_increase
feat(proxy): temporary budget increase for team members
2026-09-18 11:38:34 -07:00
Yassin Kortam
f6d9b2552f
Merge pull request #41692 from BerriAI/litellm_mcp_gateway_sessions_by_client_user
feat(mcp): show live gateway sessions by AI client and user
2026-09-18 11:37:16 -07:00
Yassin Kortam
ca79c393d5
Merge pull request #41636 from BerriAI/litellm_per_key_end_user_default_budget
feat(proxy): per-key default budget for dynamically created customers
2026-09-18 11:36:59 -07:00
yassin
fa70e49b81 chore: merge main into litellm_transcribe_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 17:54:59 +00:00
ryan-crabbe-berri
a9ee15372f
Merge pull request #39308 from BerriAI/litellm_ui_per_second_video_pricing
fix(ui): show per-second pricing for video models instead of $0.00 token costs
2026-09-18 10:05:26 -07:00
ryan
c3bc55d18f chore: merge main into litellm_routing_groups_atomic_validation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:59:23 +00:00
ryan-crabbe-berri
4e2117832a
Merge pull request #40700 from BerriAI/litellm_ui_editable_model_team_id
fix(ui): let admins change a model's team from the model edit page
2026-09-18 09:53:34 -07:00
yuneng-jiang
c4ab1d98e9
Merge pull request #41779 from BerriAI/litellm_settings_store_precedence
refactor(proxy): make the config file win over the database
2026-09-18 09:52:09 -07:00
ryan
4f86035a79 style(ui): format pricing test fixtures with prettier
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:16:28 +00:00
ryan
d197ca20fa fix(ui): type transformModelData output as ModelData so the dashboard build typechecks
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:13:28 +00:00
ryan
b5070408e7 refactor(ui): move per-second cost formatter to dataUtils and type transformModelData input
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 15:48:56 +00:00
Mateo Wang
861f79797f
Merge pull request #41663 from BerriAI/litellm_remove_legacy_interactions_schema_flag
refactor(interactions): remove expired use_legacy_interactions_schema shim
2026-09-18 08:48:30 -07:00
Mateo Wang
48c4204b43
Merge pull request #41658 from BerriAI/litellm_remove_orphaned_use_delete_project_hook
chore(ui): remove orphaned useDeleteProject hook and its test
2026-09-18 08:48:05 -07:00
Mateo Wang
444d345d69
Merge pull request #41657 from BerriAI/litellm_remove_dead_use_key_list_hook
refactor(ui): remove dead useKeyList hook from key_list.tsx
2026-09-18 08:47:56 -07:00
Mateo Wang
8ff4991583
Merge pull request #41656 from BerriAI/litellm_remove_dead_networking_and_marketplace_helpers
refactor(ui): remove dead networking exports and orphaned Claude Code marketplace helpers
2026-09-18 08:47:46 -07:00
Mateo Wang
e9823c6063
Merge pull request #41655 from BerriAI/litellm_remove_unused_access_group_types
chore(ui): remove unused access-groups type interfaces
2026-09-18 08:47:34 -07:00
Mateo Wang
c51bd68ef4
Merge pull request #41653 from BerriAI/litellm_remove_dead_role_exports
refactor(ui): drop unused rolesAllowedToSeeUsage, viewOnlyRoles and isViewOnlyRole exports
2026-09-18 08:47:25 -07:00
Mateo Wang
57a273b087
Merge pull request #41651 from BerriAI/litellm_cost_tracking_dead_barrel_exports
refactor(ui): drop unused cost-tracking barrel re-exports and response types
2026-09-18 08:47:16 -07:00
Mateo Wang
1f0554c80d
Merge pull request #41650 from BerriAI/litellm_remove_dead_create_credential_from_model
refactor(ui): remove unused createCredentialFromModel helper and CredentialValues interface
2026-09-18 08:47:08 -07:00
Mateo Wang
1f50923211
Merge pull request #41649 from BerriAI/litellm_remove_dead_compareui_modelselector
chore(ui): remove dead compareUI ModelSelector and its test
2026-09-18 08:46:57 -07:00
Mateo Wang
aa9a08959a
Merge pull request #41647 from BerriAI/litellm_remove_unused_newbadge
chore(ui): remove unused NewBadge component and its test
2026-09-18 08:46:45 -07:00