Commit graph

45353 commits

Author SHA1 Message Date
yucheng-berri
432954a2ab
fix(cache): make in-memory and disk cache increments atomic (#34013)
Some checks failed
CodSpeed Benchmarks / benchmarks (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
* fix(cache): make in-memory and disk increments atomic

* refactor(cache): narrow in-memory increment lock scope

* fix(cache): address follow-up review on increment tests/types

* fix(cache): refresh atomic increment coverage

* test(cache): widen increment race window with non-zero _SlowInt seed

The zero seed was falsy, so InMemoryCache.increment_cache's `get_cache(...) or 0`
and DiskCache.get_cache's truthiness guard both discarded the _SlowInt before
__add__ could run, leaving the sleep-based window-widening inert. Seed a non-zero
value and return _SlowInt from __add__ so the sleep fires on every read-modify-write
in both backends, making the concurrency regression deterministic.

* test(cache): cover InMemoryCache.async_increment delegation

Add a focused async test asserting async_increment accumulates through the
locked sync path, exercising the previously uncovered delegation line.

---------

Co-authored-by: Emerson Gomes <emerson.gomes@thalesgroup.com>
2026-07-20 15:51:01 -07:00
mateo-berri
3f712e3fde fix(router): stop custom model_info leaking onto shared backend cost map key 2026-07-20 15:49:19 -07:00
Yassin Kortam
583ddaf199
test(e2e): cover created key appearing in /key/list inventory (#34008) 2026-07-20 22:40:39 +00:00
Yassin Kortam
c208bec37f
test(e2e): cover user deletion removing it from user inventory (#34007) 2026-07-20 22:38:32 +00:00
Yassin Kortam
6f62022e84
test(e2e): cover team deletion persistence and key revocation (#33999) 2026-07-20 22:37:40 +00:00
Yassin Kortam
5c8e7e6924
test(e2e): cover organization update persistence via /organization/info (#34010) 2026-07-20 22:36:39 +00:00
Yassin Kortam
61b906f9b6
test(e2e): cover model deletion removing it from the catalog (#34006) 2026-07-20 22:36:15 +00:00
yuneng-jiang
9ac6cc7bee
Merge pull request #33885 from BerriAI/litellm_/global-guardrails-display-b172b4
fix(ui): hide guardrail group headers when only one group has entries
2026-07-20 15:30:38 -07:00
Yassin Kortam
6db7328afb
chore(e2e): prune reliability.perf.latency.under_slo coverage cell (#34024)
It is a non-binary latency SLO threshold rather than a deterministic pass/fail
behavior a single e2e test can assert, so it does not fit the coverage registry's
one-test-per-cell contract. The registry README already flagged the perf cells for
a support-check or prune, and throughput SLO under load is covered structurally by
the Locust load suite. Removing it keeps the denominator to behaviors an e2e test
can deterministically prove.
2026-07-20 15:28:23 -07:00
Mateo Wang
885a637c6e
Merge pull request #34037 from BerriAI/litellm_fix_model_info_unpack_flake
test(proxy): make model_info endpoint tests hermetic to kill an order/merge-skew flake
2026-07-20 18:28:13 -04:00
Yassin Kortam
f21704c672
test(e2e): cover user update persistence via /user/info (#33998)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 22:18:36 +00:00
Yassin Kortam
71131190ec
test(e2e): cover model registration persistence in /model/info (#33996)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 22:17:40 +00:00
Mateo Wang
3819ee5dc0
Merge pull request #33733 from BerriAI/litellm_lit_4162_bedrock_batch_tags
feat(bedrock): forward bedrock_tags to CreateModelInvocationJob for batch jobs
2026-07-20 18:12:21 -04:00
mateo-berri
c2bd8699be fix(proxy): require admin opt-in for request-body bedrock_tags
Caller-supplied bedrock_tags land as AWS resource tags under the proxy's
AWS identity, letting an authenticated caller forge ownership or
cost-allocation labels. Add bedrock_tags to _BANNED_REQUEST_BODY_PARAMS
so per-request tags need general_settings.allow_client_side_credentials
or configurable_clientside_auth_params on the deployment, matching the
aws_bedrock_project_id precedent. Deployment-level bedrock_tags in
litellm_params are unaffected.

Also stop an explicit empty bedrock_tags list in litellm_params from
falling through to optional_params
2026-07-20 15:00:41 -07:00
Yassin Kortam
eb27447a1d
test(e2e): cover team update persistence via /team/info (#33997)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 21:56:07 +00:00
mateo-berri
249e1f8ce7 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_4162_bedrock_batch_tags
# Conflicts:
#	tests/test_litellm/test_router.py
2026-07-20 14:45:02 -07:00
Yassin Kortam
68be053e96
test(e2e): cover created user appearing in /user/list (#34016) 2026-07-20 14:29:13 -07:00
mateo-berri
44604620a4 test(proxy): make model_info endpoint tests hermetic to kill an order/merge-skew flake
The model_info / get_model_info_with_id endpoint tests drove refactored
endpoints with bare, unspec'd MagicMock routers and models. Because the
mocks were unspec'd, any attribute or method the (refactored) endpoints
newly read auto-materialized a child MagicMock, and whether that child
was reached depended on process-global state (premium_user, and the real
get_available_models_for_user chain reading litellm globals) that sibling
tests in the same xdist worker mutate. When reached, the MagicMock either
unpacked to empty (a, b = mock.method() -> 'not enough values to unpack
(expected 2, got 0)') or leaked into RouterModelInfo(**model_info) and
failed Pydantic str validation. Pass in isolation, fail under xdist.

The original TestModelInfoEndpoint failure (#33807 CI) was the same class
surfaced by merge skew: #33721 added a get_configured_token_limits unpack
to create_model_info_response, and CI's merge commit ran that against the
un-updated bare-mock test before the #33742 band-aid landed.

Fix (test-only, no product change):
- TestModelInfoEndpoint: mock the real seam (get_available_models_for_user),
  configure the router methods the endpoint actually calls, return a real
  Deployment, and drop the dead proxy_server.get_key_models/get_team_models/
  get_complete_model_list patches the refactor had stranded.
- TestGetModelInfoWithIdBlocked: spec the model mock so unset enterprise
  columns read as None instead of child MagicMocks.
- test_ProxyConfig_get_model_info_with_id_missing_model_id_raises: pin
  premium_user so the asserted AttributeError no longer flips with the
  ambient license global.
2026-07-20 14:28:09 -07:00
Yassin Kortam
4c77a5433a
test(e2e): cover created team appearing in /team/list (#34015) 2026-07-20 14:27:49 -07:00
mateo-berri
f5dc1a3010 test(router): prove request-level bedrock_tags override deployment-level tags for acreate_batch 2026-07-20 14:26:35 -07:00
Yassin Kortam
53f5a8c380
test(e2e): cover key block persisting to /key/info (#34014)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 14:26:18 -07:00
Yassin Kortam
b9c59c37cc
test(e2e): cover model update persisting to /model/info (#34017)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 21:13:54 +00:00
yuneng-jiang
b25d1216fd
Merge pull request #34030 from BerriAI/litellm_/remove-oss-branch-gha-d9e13f
chore(ci): retire daily OSS branches in favor of litellm_internal_staging
2026-07-20 14:09:49 -07:00
devin-ai-integration[bot]
0b8817afbb
perf(bedrock): audio transcription via rust core (py->rust bridge) (#33990)
* feat(bedrock): add audio transcription via Converse with py->rust bridge

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(ci): exclude rust transcription rollout flag from docs check

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(bedrock): await rust/python fallback in async transcription dispatch

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(bedrock): cover audio transcription rust dispatch

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(bedrock): route audio transcription through rust

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(bedrock): move rust transcription dispatch out of main

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(bedrock): include rust transcription coverage shard

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-20 14:09:41 -07:00
Yuneng Jiang
551f0b6916
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/remove-oss-branch-gha-d9e13f 2026-07-20 13:48:38 -07:00
Tin Chi Lo
051cdd1dce fix(cli): tolerate an unreadable prior config when carrying the autoroute key forward
_load_persisted_master_key documents leniency on any unreadable prior
state but only caught parse errors; a permissions failure or non-UTF-8
bytes in config.yaml crashed configure instead of skipping the
carry-forward. Catch OSError and UnicodeDecodeError too and pin the
undecodable-file case with a regression test.
2026-07-20 13:25:25 -07:00
Tin Chi Lo
0f62ff41b6 fix(cli): stable port and persisted master key for lite autoroute up
lite autoroute up minted a fresh master key and picked a fresh OS-ephemeral
port on every run, so any client configured against one session (an
already-open Claude Code session, a hand-configured script) broke on the
next run. The port is now a stable default (5483, overridable with --port)
that refuses loudly when busy or when 4000 is requested, since proxy_cli
silently rebinds a busy 4000 to a random port. The master key is minted
once, persisted in the generated config.yaml, reused by every later up,
and carried forward when configure regenerates the config.
2026-07-20 13:08:12 -07:00
Yassin Kortam
214945a223
test(e2e): cover organization deletion removing it from /organization/info (#34009)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 19:36:59 +00:00
Tin
bf04ba8d3e refactor(mcp): extract the oauth2 spec dispatch to keep to_server_spec under the complexity ceiling
Some checks failed
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
The ID-JAG landing brought to_server_spec to the C901 boundary and the M2M
branch pushed it one over; the oauth2 sub-mode dispatch now lives in its own
_oauth2_spec helper, mirroring the file's per-mode spec builders
2026-07-20 12:32:34 -07:00
Yassin Kortam
72be5a9bc0
test(e2e): cover tag creation persisting for spend categorization (#34018) 2026-07-20 12:24:50 -07:00
Yassin Kortam
51df801159
test(e2e): cover key regeneration rotating to a working new key (#34000) 2026-07-20 12:23:22 -07:00
Tin
d2342296ae fix(mcp): keep the minted M2M bearer authoritative over injected Authorization headers
_resolve_v2_auth dropped the resolved client_credentials auth when extra_headers
already carried Authorization (MCPJWTSigner, static_headers), so the upstream got
the injected header instead of the minted token and the one-shot 401 refetch was
lost. M2M now joins token_exchange and authorization_code in the authoritative
set; the conflicting header is dropped
2026-07-20 12:00:12 -07:00
Tin
8bfd8baab8 fix(mcp): remember the rotated M2M bearer for later requests in the session
The auth object is the httpx client's auth for the whole MCP session; after a
401 recovery it kept sending the rejected token first, burning a 401 round trip
and the single retry on every subsequent call
2026-07-20 11:54:47 -07:00
Tin
5b64239afc fix(mcp): skip the M2M cache write when the token is already expired at mint
An expires_in of zero or below computes a ttl of 0; the entry could never be
served but still occupied a slot in the bounded backend, where it could evict
a live token. The mint still serves the current request and the next get
re-fetches under the per-server lock
2026-07-20 11:54:47 -07:00
Tin
9c191e6764 chore(lint): add reasons to the client_credentials noqa suppressions 2026-07-20 11:54:47 -07:00
Tin Chi Lo
7705f0b975 fix(mcp): bound the M2M lock dict and cap short-lived token TTL at real expiry
Greptile P2s: the per-server lock dict now evicts its oldest entry past
max_locks so ephemeral server ids (REST tools preview) cannot grow it
unbounded, and the min-cache floor is capped at the token's actual lifetime so
an expires_in below the skew is never served past expiry
2026-07-20 11:54:47 -07:00
Tin Chi Lo
4b1c9d4498 test(mcp): update _create_mcp_client graft tests for migrated M2M arm
The graft test pinned the pre-migration contract (M2M defers to v1). Replaced
with two tests pinning the new one: a complete-config M2M server resolves via
the v2 arm into ClientCredentialsBearerAuth, and an incomplete-config server
fails closed with a 500 misconfigured naming the missing grant fields
2026-07-20 11:54:47 -07:00
Tin Chi Lo
d4035a07c7 feat(mcp): migrate client_credentials (M2M) onto the v2 resolver arm
Replaces the not_implemented stub with a live arm: ClientCredentialsTokenSource
mints and caches the M2M token (rotation-aware identity key, expires_in-driven
TTL, audience and token_endpoint_auth_method support) and
ClientCredentialsBearerAuth retries an upstream 401 exactly once with a freshly
minted token. to_server_spec owns oauth2_flow=client_credentials servers and
fails closed on incomplete grant config instead of connecting unauthenticated
2026-07-20 11:54:47 -07:00
Yuneng Jiang
8a0bb4cc56
chore(ci): retire daily OSS branches in favor of litellm_internal_staging
Removes the scheduled workflow that cut litellm_oss_daily_YYYY_MM_DD
branches and the guardrails workflow that only ran on them. The secret
scan and ruff checks that workflow duplicated already run on PRs to
litellm_internal_staging via test-linting.yml, so no coverage is lost.

Retargets contributor-facing messaging in CONTRIBUTING.md, CLAUDE.md,
and the guard-main-branch error output at litellm_internal_staging.
2026-07-20 11:38:52 -07:00
tin-berri
ce88999ed1
Merge pull request #33182 from BerriAI/litellm_lit3637_session_token
feat(mcp): identity-only session tokens for the gateway DCR front door
2026-07-20 11:24:17 -07:00
yuneng-jiang
24b55d2e4f
Merge pull request #33994 from BerriAI/litellm_/ticket-implementation-review-409e1d
feat(spend): raise /spend/logs/v2 page_size cap to 1000
2026-07-20 10:26:33 -07:00
Yuneng Jiang
b7e7fab741
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/ticket-implementation-review-409e1d 2026-07-20 10:04:46 -07:00
Yuneng Jiang
479e997eed
feat(spend): raise /spend/logs/v2 page_size cap to 1000
Clients exporting large spend-log ranges were forced into 100-row pages,
which meant a bounded COUNT plus an increasingly deep OFFSET scan per
request. Larger pages reduce both the request count and the cumulative
OFFSET cost for the same result set.

The handler already excludes the heavy JSON columns (messages, response,
proxy_server_request) from the paginated SELECT and bounds the COUNT via
SPEND_LOGS_PAGINATION_COUNT_CAP, so per-row cost does not grow with page
size. 1000 matches the ceiling already used by the user and user-agent
analytics list endpoints.
2026-07-20 09:55:38 -07:00
devin-ai-integration[bot]
3fcd19d7ad
fix(fireworks_ai): restore Content-Type application/json header (fixes 415) (#33929)
* fix(fireworks_ai): set Content-Type application/json in validate_environment

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

* refactor(fireworks_ai): delegate chat validate_environment to OpenAIGPTConfig

Instead of re-adding the JSON Content-Type default inside FireworksAIMixin,
FireworksAIConfig now delegates header construction to OpenAIGPTConfig and only
layers the Fireworks-specific x-session-affinity header on top, so the
Content-Type default can no longer drift away from the OpenAI base and reintroduce
the 415.

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

* test(fireworks_ai): cover missing api key error path in chat validate_environment

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

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-20 09:52:35 -07:00
Vineet Puranik
067c9bbc96
chore(rust): migrate the litellm-rust workspace (core, ai-gateway, python-bridge) from Rust edition 2021 to edition 2024 (#33940)
* chore(deps): update cargo.lock file after cargo update

* chore(rust): migrate workspace crates to edition 2024

* chore(rust): migrate workspace crates to edition 2024 + fix clippy warnings after 2024 update

* chore(rust): add rust version to cargo workspace file

* chore(rust): fix clippy collapsible if warning
2026-07-20 08:52:06 -07:00
Yassin Kortam
8d96e959db
test(e2e): guard destructive spend-log truncate behind an explicit opt-in (#33751)
tests/e2e/conftest.py's pytest_sessionfinish truncated LiteLLM_SpendLogs
against whatever DATABASE_URL resolved to, gated only by "an e2e test body
ran". Pointed at a shared or staging DB, a routine local run wiped real
spend data. It also reached the truncate helper through a sys.path.insert
into quota_management/spend_tracking/spend_e2e_client.py, a cross-suite
import-by-path hack it then unwound in a finally.

The cleanup now routes through a new run_spend_log_cleanup in a top-level
tests/e2e/e2e_db.py, which fires the destructive truncate only when the
operator set E2E_RESET_SPEND_LOGS=1 and an e2e test actually ran. Any other
value (unset, 0, true, empty) leaves the DB untouched, so presence of the
variable alone or a test run alone never arms the truncate. The decision
plus the injectable truncate callable live in that pure helper, and conftest
is a thin adapter that supplies os.environ.get(...), the session stash, and
reset_spend_logs.

reset_spend_logs itself moved from spend_e2e_client.py into e2e_db.py
(implementation unchanged), sitting next to e2e_config and lifecycle so both
conftest and any suite import it by name; the sys.path munging is gone.
Nothing else imported reset_spend_logs, so spend_e2e_client.py drops the
definition, its __all__ entry, and the now-unused os import.
2026-07-20 08:47:39 -07:00
devin-ai-integration[bot]
34561482ed
feat(ui): add configuration tabs to the Cost Optimization page (#33899)
* feat(ui): add configuration tabs to Cost Optimization page

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

* feat(ui): reuse AutoRouter v2 and Router Settings prompt-caching panel in Cost Optimization; clarify Headroom compression

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

* feat(ui): add experimental dashboard banner with feedback discussion link to Cost Optimization

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

* feat(ui): add savings methodology note and per-key/team compression enterprise callout to Cost Optimization

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

* test(ui): assert active tab state in Cost Optimization tab-switch test

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

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-20 07:30:49 -07:00
yucheng-berri
bd44c9e305
fix(langfuse): send v4 ingestion header for otel callback (#33907)
Some checks are pending
CodSpeed Benchmarks / benchmarks (push) Waiting to run
LiteLLM Rust / rustfmt, clippy, test (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
* fix(langfuse): send v4 ingestion header for otel callback

* refactor(langfuse): inline otel ingestion header literals

* test(langfuse): assert v4 ingestion header on dynamic key config paths

* style: apply ruff format to langfuse otel header changes

* chore(langfuse): drop stale development annotation on json import

---------

Co-authored-by: Hassieb Pakzad <68423100+hassiebp@users.noreply.github.com>
2026-07-18 20:36:51 -07:00
tin-berri
ac1b4b35ce
Merge pull request #33174 from BerriAI/litellm_lit3637_aggregate_dcr
feat(mcp): always-on aggregate gateway DCR discovery front door
2026-07-18 19:25:26 -07:00
devin-ai-integration[bot]
cc45d18e9c
feat(complexity-router): add return_raw_model_name toggle for response model field (#33875)
* feat(complexity-router): optionally return raw model name

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

* test(proxy): restore asyncio import

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

* chore(tests): preserve staging asyncio import

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

* test(proxy): drop unused local asyncio import

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

* feat(dashboard): add complexity router raw model toggle

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

* refactor(complexity-router): move metadata key constant to constants.py

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

* style(proxy-tests): preserve module spacing

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

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-18 19:24:56 -07:00