Commit graph

4669 commits

Author SHA1 Message Date
yucheng-berri
d44d281d1d
fix(proxy): emit timing headers and overhead for /v1/messages and /v1/responses (#38840) 2026-08-29 18:11:58 -07:00
ryan-crabbe-berri
9da0b30888
Merge pull request #38843 from BerriAI/litellm_mag_budget_ui
feat(ui): set a model access group's shared budget from the dashboard
2026-08-29 17:27:17 -07:00
Mateo Wang
ff2f06e37f
Merge pull request #38444 from BerriAI/litellm_mcp_connector_bulk_import
feat(mcp): bulk-import Anthropic MCP connectors via API and admin UI
2026-08-29 17:21:57 -07:00
ryan-crabbe-berri
abfb6adc2b refactor(proxy): bound the budget window seed by time instead of request ids
The one-time seed for a budget window row subtracted the batch's own
LiteLLM_SpendLogs rows by request_id, and request_id is the client's
x-litellm-call-id whenever the response carries no id of its own. Carrying
that set through the queue meant an unbounded, client-controlled aggregate
that the commit-failure requeue kept alive across retries.

Every log row at or after a batch's earliest start is owed by an increment
that still reaches the row, so summing only rows before it needs nothing
from the request. That drops request_ids end to end and closes the
cross-pod double count the id list could not see.
2026-08-29 17:13:26 -07:00
yucheng-berri
842c423ccd
fix(guardrails): stop Lakera monitor mode forwarding unmasked PII on Responses-API bodies (#38841)
#34940 widened the mask-in-place safety guard so a Responses-API
`instructions` field (and a combined messages+input body) skips the
PII masking branch. With `on_flagged: "monitor"` that fell straight
through to "allow", so PII that used to be masked now reaches the
model unredacted.

Monitor means "don't block", not "don't redact". Recover the one shape
whose payload is still fully writable: mask it and write the redacted
instructions back into `data["instructions"]` directly, since
apply_redacted_messages_back has no path for that field and would
otherwise fold the instructions text into `data["input"]`.

The combined messages+input and multimodal shapes stay unmasked - both
are unsafe to write back, not merely unwritable - and now log an error
naming the reason instead of passing silently.

No block/allow decision changes: block and inject_system_message keep
the exact outcomes #34940 shipped.
2026-08-29 17:07:39 -07:00
ryan-crabbe-berri
e257383cc6
Merge pull request #38249 from BerriAI/litellm_fix_empty_user_models_visibility
fix(proxy): list all non-team models for users with an empty model list
2026-08-29 17:04:28 -07:00
ryan-crabbe-berri
6f8a3b8661 fix(proxy): scope model listing direct access to the calling key's grant
Direct access on /model/info was read from the user record alone, so an
unrestricted user calling with a key limited to a few models saw every non-team
deployment, including ones the key gets a 403 on. Resolve the key's grant the
same way and intersect the two.

Resolving a grant now also expands access groups, which the key path needs and
the user path was missing.

Claude-Session: https://claude.ai/code/session_01XL7LBFEew4wi8gphVCDq6n
2026-08-29 16:54:02 -07:00
ryan-crabbe-berri
2ffd3d0e7d
Merge pull request #35887 from BerriAI/litellm_window_spend_reader
perf(proxy): read budget-window spend from the maintained window table
2026-08-29 16:44:53 -07:00
mateo-berri
6bd3699d43 fix(responses): keep guardrailed input items and bridge stream usage intact
- _write_back_structured_messages now patches only the rewritten rows back
  into the original input items, so reasoning items (encrypted_content),
  function_call ids, and web_search_call items survive a guardrail rewrite
  verbatim; rewrites that cannot be row-mapped fall back to the previous
  full conversion
- the responses bridge stream snapshot restores usage hidden in
  _hidden_params when stream_options is unset, so converted fake streams
  report real input_tokens instead of 0
2026-08-29 16:39:14 -07:00
ryan-crabbe-berri
f82708f41d Merge branch 'litellm_window_spend_writer' into litellm_window_spend_reader 2026-08-29 16:36:09 -07:00
ryan-crabbe-berri
ce96db5a61 test(proxy): pass the window spend args in the access group requeue test 2026-08-29 16:36:04 -07:00
ryan-crabbe-berri
0777e37849 feat(ui): set a model access group's shared budget from the dashboard
Model access group budgets shipped API-only, so the only way to give a group a
budget was a curl. Adds an Access Group Budgets tab under Models & Endpoints
that lists every group with the spend drawn against its shared pool, and a
modal to set, edit or clear the budget.

/access_group/list now carries each group's budget and spend inline, so the
table renders from one read instead of one follow-up request per row.
2026-08-29 16:32:28 -07:00
mateo-berri
a85e16f731 Merge remote-tracking branch 'origin/litellm_fix_audio_speech_content_type' into litellm_fix_gemini_tts_container 2026-08-29 16:27:27 -07:00
ryan-crabbe-berri
3cc2f615da Merge branch 'litellm_window_spend_writer' into litellm_window_spend_reader 2026-08-29 16:24:07 -07:00
yuneng-jiang
e4ae1c1f2e
Merge pull request #38835 from BerriAI/litellm_38816_classifier_cost_savings
fix(proxy): count auto-router classifier cost in savings and benchmarks
2026-08-29 16:23:50 -07:00
ryan-crabbe-berri
140950f52d Merge branch 'litellm_window_spend_schema' into litellm_window_spend_writer 2026-08-29 16:23:45 -07:00
Mateo Wang
0c8321efc3
Merge pull request #38741 from BerriAI/litellm_fix_anthropic_messages_dict_detail_error
fix(anthropic_endpoints): serialize dict-detail HTTPExceptions on /v1/messages like sibling surfaces
2026-08-29 16:20:08 -07:00
Tin Chi Lo
d3db7cebca fix(proxy): count auto-router classifier cost in savings and benchmarks
The LLM classifier's cost was recorded on the routing decision but never
reached any savings surface: per-request autorouter_savings stayed gross
and the session rollup recorded only the served request's spend, so
/auto_router/benchmarks overstated savings and understated routed spend.

Net the classifier cost into the savings figure at its one computation
owner and fold it into the rollup turn's spend, keeping
baseline_spend = spend + saved_spend. The response header's numeric
guard now shares the same reader.

Fixes #38816
2026-08-29 16:09:35 -07:00
ryan-crabbe-berri
ec934c490b
Merge pull request #38784 from BerriAI/litellm_model_access_group_budgets
feat(budgets): enforce shared budgets on model access groups
2026-08-29 16:06:54 -07:00
devin-ai-integration[bot]
3e2999f29f
fix(proxy): run SMTP send_email off the event loop with a connection timeout (#38473)
* fix(proxy): run SMTP send_email off the event loop with a connection timeout

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

* fix(proxy): format utils.py and update _create_smtp_connection tests for timeout

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

* fix(proxy): keep malformed SMTP_TIMEOUT inside the email error boundary

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

* chore: retrigger ci

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

* ci: exclude misaligned circleci coverage flag from merged codecov report

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

* chore: retrigger ci for codecov and benchmarks

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

* ci: disable carryforward for the circleci codecov flag

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

* ci: exclude carried-forward coverage from the codecov patch status

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

* ci: stop carrying forward the dead circleci codecov flag

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-08-29 16:05:57 -07:00
ryan-crabbe-berri
faad94af94 Merge branch 'litellm_window_spend_writer' into litellm_window_spend_reader 2026-08-29 16:02:46 -07:00
ryan-crabbe-berri
b2a08100e1 Merge branch 'litellm_window_spend_schema' into litellm_window_spend_writer 2026-08-29 16:02:23 -07:00
ryan-crabbe-berri
40a7fe9221 fix(budgets): make the model access group ceiling exclusive
A pool whose recorded spend has reached max_budget has nothing left to give, so
the next request is refused rather than admitted. This departs from the tag
check it otherwise mirrors and matches where keys and organizations already
draw the line.

A non-positive budget now means no budget here too, so the read-time check and
the reservation path agree on what counts as unbudgeted.
2026-08-29 15:20:10 -07:00
mateo-berri
1695b7f7b1 fix(headroom): fake-stream converted sync /v1/responses calls too
The sync response_api_handler agentic branch returned the completed ResponsesAPIResponse for a request the Headroom guardrail had converted from streaming, so litellm.responses(stream=True) handed callers a non-iterable object. Wrap it in the same fake stream the async path uses
2026-08-29 15:02:20 -07:00
ryan-crabbe-berri
d7c0bc1e6d test(budgets): clear the test-quality violations this branch added
The four model access group callback tests now share one helper, so nine
patches of proxy_server internals become three, and both mock-echo assertions
go with them. The delete_access_group tests share a context manager for the
same reason.

test_group_exactly_at_its_max_budget_passes gained the assertion it was
missing: it now proves the group reached the spend comparison, which a group
skipped for a missing budget row would not. The route-allowed patch beside it
was dead, so it is gone.

What is left is suppressed with the collaborator each one cannot inject.
2026-08-29 14:44:00 -07:00
Mateo Wang
20cfccaf5f
Merge pull request #37208 from BerriAI/litellm_managed_batches_observability
fix(batches): aggregate reasoning tokens and per-line pass/fail counts
2026-08-29 14:43:03 -07:00
mateo-berri
ef96af5121 fix(headroom): resolve CCR retrieval on streaming /v1/responses
Streaming /v1/responses requests that carried Headroom's retrieve tool were
sent upstream as streams, so the model's headroom_retrieve function_call was
streamed straight back to a client that never declared the tool and the
retrieval never resolved. Chat completions already avoid this by converting
the request to non-stream in the pre-call deployment hook, letting the
agentic loop resolve the retrieve, and fake-streaming the final answer.

The hook now converts responses call types too, the responses handler wraps
the resolved result as a fake stream whenever any interception converted the
stream (shared converted_stream_requested helper instead of per-integration
key checks), and the follow-up request filter drops every non-code-interpreter
interception key through is_interception_internal_key.

Resolves LIT-6481
2026-08-29 14:34:42 -07:00
Mateo Wang
c62c2afa09
Merge pull request #38234 from BerriAI/litellm_request_timeouts
fix(proxy): give every `requests` call a timeout so a silent server cannot hang the caller
2026-08-29 14:30:46 -07:00
ryan-crabbe-berri
acf3ed7d9b fix(budgets): narrow model access group spend counters to the served deployment
The database writer already intersects the auth-matched groups with the ones
the served deployment declares, but the live spend counters got the unnarrowed
set. A caller granted two pools that both cover a model group debited both
counters while only one row moved, so the in-memory ceiling could block a pool
its persisted spend never touched.

Narrow once at the callback so both consumers read the same set.
2026-08-29 14:25:56 -07:00
mateo-berri
429ad06972 fix(guardrails): write structured_messages rewrites back into /v1/responses input
Message-rewriting guardrails such as Headroom return their rewrite in
structured_messages and leave texts untouched. The responses guardrail
translation only mapped texts back, so compression never reached the
upstream request on /v1/responses while the retrieve tool still got
injected. Convert the returned messages back to Responses input (plus
instructions) the way the chat and Anthropic handlers already do, and
keep developer messages as input_text in the chat-to-responses bridge.

Resolves LIT-6494
2026-08-29 14:23:57 -07:00
ryan-crabbe-berri
6b2e7f8a1f refactor(budgets): declare route dependencies with Annotated instead of argument defaults 2026-08-29 14:10:58 -07:00
ryan-crabbe-berri
b5ec80d903 Merge commit 'a5f47a271a' into litellm_window_spend_reader 2026-08-29 13:55:53 -07:00
ryan-crabbe-berri
a5f47a271a fix(proxy): re-queue budget window spend increments when the commit fails
Budget enforcement trusts a current LiteLLM_BudgetWindowSpend row without
reconciling it against LiteLLM_SpendLogs, so an increment dropped after a
failed commit let the entity spend past its window limit after the next
counter reseed. Failed increments now go back on the in-memory queue, or
back to the Redis buffer, and retry on the next scheduler tick like every
other spend category.
2026-08-29 13:55:36 -07:00
mateo-berri
4e2574ce08 fix(proxy): match /v1/audio/speech content-type to the returned audio format 2026-08-29 13:44:43 -07:00
mateo-berri
40f5d53c04 fix(mcp): harden connector import auth handling and registration
Send a non-Bearer Authorization header verbatim via auth_type authorization
instead of wrapping it as a bearer credential, and always drop the
Authorization header from static_headers so the plaintext copy cannot shadow
the encrypted credential at request time. Register each imported server with
the in-memory manager before the best-effort reload, matching the manual add
path. Let get_all_mcp_servers propagate read failures instead of returning [],
which silently disabled the import dedupe and allowed duplicate imports.
2026-08-29 13:41:22 -07:00
devin-ai-integration[bot]
645792955d
feat(proxy): cyberark conjur secret manager configuration via Admin UI (#38445)
* feat(proxy): CyberArk Conjur secret manager configuration via Admin UI

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

* test(ui): mock networking base-url helpers in AdminPanel test

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

* fix(proxy): restore deployment CyberArk env config on delete and roll back on persist failure

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

* fix(proxy): reinit env-configured hashicorp vault manager after cyberark persist rollback

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-08-29 13:36:08 -07:00
mateo-berri
4d5205c355 fix(proxy): give the remaining CLI clients a request timeout
The keys, credentials, models, model groups, and chat clients still sent
requests with no timeout, so a proxy that accepts the connection and
never answers pinned the caller forever. They now default to the same
30 seconds as their teams and users siblings, with chat on the OpenAI
SDK's 600 second default, and Client wires its timeout through to all of
them. S113 cannot see Session methods, so each client gets a
hanging-server regression test instead.
2026-08-29 13:32:39 -07:00
mateo-berri
8aba6e9203 Merge branch 'litellm_internal_staging' into litellm_request_timeouts 2026-08-29 13:32:33 -07:00
ryan-crabbe-berri
2c8efca0d3 Merge commit '56dd4e06ac' into litellm_window_spend_reader 2026-08-29 13:30:10 -07:00
ryan-crabbe-berri
56dd4e06ac test(proxy): satisfy the test-quality gate for the window spend writer tests 2026-08-29 13:29:59 -07:00
mateo-berri
d3268e4e18 test(mcp): wrap over-long connection error message calls 2026-08-29 13:26:04 -07:00
yassin
0557a95259 fix(mcp): lift Authorization headers into bearer credentials on import
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-29 20:11:45 +00:00
Mateo Wang
e0ed0a4c7a
Merge pull request #35017 from BerriAI/litellm_lit_4913_headroom_streaming_ccr
fix(headroom): resolve CCR retrieval on streaming /chat/completions
2026-08-29 13:01:35 -07:00
devin-ai-integration[bot]
f0340fef16
feat(mcp_gateway): add RFC 7662 introspection for gateway session tokens (#38726)
* feat(mcp_gateway): add RFC 7662 introspection for gateway session tokens

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

* fix(mcp_gateway): omit Bearer token_type for refresh introspection and allow mcp-scoped keys

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

* test(mcp_gateway): cover introspection of RS256-signed session tokens

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

* fix(mcp_gateway): load the discoverable router on a cold /introspect request

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

* chore(openapi): regenerate lazy snapshot and schema.d.ts for /introspect

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-08-29 12:56:04 -07:00
ryan-crabbe-berri
8e1d1f1ef0 fix(budgets): write the model access group spend counter after each call
Two problems, both caught in review.

The new table only landed in the root schema.prisma. Client generation reads
litellm/proxy/schema.prisma and packaging reads the copy under
litellm-proxy-extras, so the generated client had no
litellm_modelaccessgroupbudgettable and every budget read and write against
it would have failed at runtime. The root is the source of truth; both
copies are now byte-identical to it.

Nothing incremented spend:model_access_group:{group} after a call. Only the
reservation path ever wrote it, so with disable_budget_reservation the
read-time check was reading a counter nobody maintained and falling back to
the row's spend, which is cached for up to DEFAULT_MODEL_ACCESS_GROUP_CACHE_TTL.
A caller could run well past the pool inside that window, which is precisely
the case the read-time check exists to cover.

increment_spend_counters now takes the matched groups and charges them
through _init_and_increment_unreserved_spend_counter, so a group already
covered by a reservation is skipped rather than counted twice. The cost
callback sources the names with get_request_model_access_groups, the same
reader the spend writer uses.
2026-08-29 12:49:47 -07:00
mateo-berri
177463e9b0 Merge branch 'litellm_internal_staging' into litellm_mcp_connector_bulk_import
Regenerates the lazy OpenAPI snapshot and dashboard schema.d.ts from the merged tree.
Passes list/dict copies into NewMCPServerRequest from the connector conversion and switches the
server-managed NewMCPServerRequest fields to keyword defaults so the basedpyright budget gate stays flat.
2026-08-29 12:49:45 -07:00
mateo-berri
3275459aec fix(mcp): cap tools preview and test-connection at the listing timeout and name the unreachable upstream 2026-08-29 12:47:26 -07:00
mateo-berri
2affd800ec test(headroom): cover stream conversion after deployment-level compression 2026-08-29 12:45:56 -07:00
ryan-crabbe-berri
8463cb901e Merge remote-tracking branch 'origin/litellm_window_spend_writer' into litellm_window_spend_reader 2026-08-29 12:29:14 -07:00
ryan-crabbe-berri
2fac72392a Merge remote-tracking branch 'origin/litellm_window_spend_schema' into litellm_window_spend_writer 2026-08-29 12:27:58 -07:00