Commit graph

18680 commits

Author SHA1 Message Date
jesus
bd6b07d6fb Merge remote-tracking branch 'origin/main' into litellm_v1_models_alias_metadata 2026-09-18 20:23:06 +00:00
mateo-berri
acfe9a899a merge main and drop config_data from its two routing group tests 2026-09-18 13:22:54 -07:00
yassin
6a6ae2d064 Merge remote-tracking branch 'origin/main' into litellm_daily_global_spend_table 2026-09-18 20:20:17 +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
yucheng
08d0819159 fix(proxy): keep the resolved values of config-owned keys across SettingsStore.clear()
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 20:18:48 +00:00
yucheng
d6b6ab31b7 test(proxy): compare the cleared settings store against an unmutated refill mapping
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 20:18:48 +00:00
yucheng
6464c1fd6c fix(proxy): make SettingsStore.clear() terminate when the config file owns a key
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 20:18:48 +00:00
mateo-berri
c54c0b049d fix(vertex_ai): keep reasoning_effort unsupported on Vertex AI Mistral partner models
Vertex AI Mistral models reused MistralConfig, whose reasoning_effort
advertisement checks the mistral provider entry of the cost map, so
vertex_ai/mistral-medium-3 started advertising reasoning_effort and
drop_params stopped dropping it, turning a 200 into a Vertex 400.
VertexAIMistralConfig scopes that lookup to the vertex_ai provider, and
MistralConfig now reads the provider from its custom_llm_provider
property instead of a hardcoded "mistral".
2026-09-18 13:16:56 -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
yassin
817aefc413 test(proxy): keep stored pass-through route tests off the shared FastAPI app so the allowlist coverage test stays order independent
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 20:15:48 +00:00
mateo-berri
1581c45615 fix(proxy): check and persist only the settings the request sent
POST /config/update compared litellm_settings after lowercasing the
callback list, so a config file spelling a callback in mixed case refused
the same list sent back, and it stored every general_settings model default
next to the keys the request set. Both now use the request as sent; only
the stored callback list is lowercased.

Also drops config_data from the router settings reload callers the previous
commit left behind and teaches the legacy MockProxyConfig the ownership
check.
2026-09-18 13:14:22 -07: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
Devin AI
04193745a6 fix(docs-test): restore line-anchored table regex in router settings check
A merge on this branch dropped the ^ anchor and MULTILINE flag from the
doc_key_pattern, so the unanchored match started swallowing key names
into captured fields whenever a row's description cell itself contains
a pipe (Literal unions and similar). The check then reported 27
long-documented keys as undocumented. Restores the exact pattern used
on main, verified against a live litellm-docs checkout: all 58 Router
init params resolve as documented.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 20:10:20 +00:00
mateo-berri
d1563e0b55 feat: honor eager_input_streaming on Bedrock and Anthropic Claude tools 2026-09-18 13:09:13 -07:00
ryan-crabbe-berri
d37320f8cf fix(proxy): report null cost for unpriced deployments on the /model/info id lookup
GET /model/info?litellm_model_id= went through _get_proxy_model_info, a copy of _enrich_model_info_with_litellm_data that missed the unpriced check, so the same deployment read as null in the list and as 0 on the id lookup. The id lookup now delegates to the shared helper, so the two paths cannot drift again
2026-09-18 13:08:55 -07:00
mateo-berri
74e9fb2323 fix(passthrough): validate only the winning timeout value in the resolver 2026-09-18 13:07:33 -07:00
yassin
b92820dcfe Merge branch 'litellm_usage_key_free_aggregate_split' into litellm_daily_global_spend_table 2026-09-18 20:06:47 +00:00
yassin
191ca14872 Merge remote-tracking branch 'origin/main' into litellm_usage_key_free_aggregate_split 2026-09-18 20:05:54 +00:00
Mateo Wang
57377c9249
Merge pull request #41860 from BerriAI/litellm_router_settings_doc_table_parse
test(docs): read only the first column of the router_settings reference table
2026-09-18 13:03:51 -07:00
Devin AI
fc186f5613 fix(bedrock): clamp maxTokens to the 16-token minimum for OpenAI GPT models on Converse
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 20:02:31 +00:00
jesus
8549189cbb test(proxy): use local_model_cost_map fixture for alias listing test
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:59:24 +00:00
ryan-crabbe-berri
6b54238083
Merge pull request #40807 from BerriAI/litellm_service_account_team_key_mgmt
feat(keys): let team service account keys use key management endpoints for their own team
2026-09-18 12:58:41 -07: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
jesus
32e6a86319 fix(router): report null cost for unpriced deployments instead of 0
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 12:52:57 -07:00
yassin
cf22e4b171 fix(proxy): make SettingsStore.clear terminate when the config file owns a key
MutableMapping.clear pops items until the mapping is empty, but __delitem__
keeps config-owned keys, so clear spun forever on any store that had loaded a
config file. unittest.mock.patch.dict calls clear on exit, which is why the
proxy-infra and proxy-endpoints shards hung at 99 percent until the 20 minute
job timeout on every run since the store started refusing config-owned writes

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:51:15 +00:00
Mateo Wang
a7a3f6802e
Merge pull request #41203 from clonylu/fix/anthropic-thinking-binding-beta-header
fix(anthropic): register thinking-binding-controls-2026-08-01 in beta headers config
2026-09-18 12:50:55 -07:00
mateo-berri
58beea2275 fix(passthrough): read the stream flag by truthiness in the timeout resolver 2026-09-18 12:45:54 -07:00
mateo-berri
8533cb9d59 test(proxy): drop the pass-through routes the reload tests add to the shared app
The two pass-through reload tests register routes on the shared app and
never take them off, so a later allowlist test in the same xdist worker
finds routes that no component exposes. Restore the app's route list
when each of those tests finishes.
2026-09-18 12:44:42 -07:00
Devin AI
4f904f69b6 test(proxy): restore app routes after pass-through reload tests
The two pass-through reload tests register real FastAPI routes on the
shared proxy app and only clean up the internal registry, so any test
running after them in the same worker sees stray /v1/kept-* and
/v1/deleted-* routes. test_component_allowlists counts those as
uncovered and fails. Snapshot app.routes and the registry up front and
restore both in a finally block.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:44:18 +00:00
mateo-berri
7cfec730d5 fix(proxy): refuse config-owned keys on POST /config/update
POST /config/update stored keys the config file owns and answered 200
while the file silently kept winning. Run the config-owned check for
general_settings, litellm_settings, and router_settings before the first
database read, the way /config/field/update already does, so a refused
request stores nothing.

The config reload re-loaded the merged settings as yaml settings, which
turned every saved router setting read-only after one tick. Read the
saved router settings row instead so database-owned values stay writable.

The integration harness seeds num_retries through /config/update instead
of the config file, which is what the effective-settings and observed
routing tests need to keep exercising a database-owned value.
2026-09-18 12:42:08 -07:00
mateo-berri
c0b0ba20b2 fix(azure-ai): keep FLUX.2 tolerant of OpenAI-only image params 2026-09-18 12:41:40 -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
1e7c5400fd fix(proxy): canonicalize azure speech paths and bill uploaded short audio
Resolve dot segments in the /azure_speech endpoint path before the endpoint family and the admin-only batch guard are decided, so the guard and the forwarded upstream path agree. Bill short-audio requests for the longer of the uploaded audio duration and the recognized duration, so a NoMatch or silence response still charges for the audio Azure processed

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:27:25 +00:00
mateo-berri
b4dc081c27 fix(mistral): never round reasoning_effort none onto the strength ladder 2026-09-18 12:22:34 -07:00
Devin AI
a774e71175 test(bedrock_mantle): document mantle in-region pricing invariant
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:18:45 +00:00
Devin AI
2299846b48 fix(bedrock_mantle): align gpt-5.6-sol pricing with the AWS in-region model card
Co-authored-by: kusumakarb <kusumakarbvss@gmail.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:18:02 +00:00
yassin
b5ad17e848 test(docs): read only the first column of the router_settings reference table
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:16:26 +00:00
yassin
649d299c44 Merge remote-tracking branch 'origin/main' into litellm_fix_tpm_window_reset_sibling_counters 2026-09-18 19:13:05 +00:00
yassin
afa8b8a903 test(docs): read only the first column of the router_settings reference table
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:11:39 +00:00
yassin
84f7adec2e fix(passthrough): match deepgram listen routes served under a path prefix
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:06:24 +00:00
Mateo Wang
a2626726a2
Merge pull request #40500 from BerriAI/litellm_bedrock_aws_auth_params
fix(bedrock): send aws_session_tags on every STS call via one typed auth struct
2026-09-18 12:01:52 -07: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
mateo-berri
c1e39810ec fix(mistral): send reasoning_effort as a level the model accepts
Declare the live-verified reasoning_effort_levels on the Mistral cost-map
entries and round an undeclared request to the nearest declared level
(up to the weakest level at least as strong, down to the strongest when
the request exceeds the ceiling). Codex's default medium no longer 400s
on mistral-medium-latest, mistral-small-latest, or the vibe-cli family;
an entry that declares nothing keeps forwarding the value verbatim
2026-09-18 11:58:04 -07:00
kerry
fb8816d826 Merge remote-tracking branch 'origin/main' into litellm_lit_8128_off_peak_pricing_schema 2026-09-18 18:55:24 +00:00
mateo-berri
73fddb999e fix(router): resolve stream_timeout before generic timeouts on the passthrough route 2026-09-18 11:53:26 -07:00
yassin
307df09792 test(mcp): assert the self-revoke response instead of echoing the delete mock
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 18:51:43 +00:00
ryan-crabbe-berri
839c040b53
Merge pull request #41695 from BerriAI/litellm_issue_classifier
ci: classify new issues into domain, provider, kind, priority and lift labels
2026-09-18 11:50:56 -07:00
Mateo Wang
adb5ee0881
Merge pull request #41275 from BerriAI/litellm_azure_strip_file_format
fix(azure): strip litellm format field from file and image content parts
2026-09-18 11:46:23 -07:00