Commit graph

43876 commits

Author SHA1 Message Date
yuneng-jiang
dcb789f3ed
refactor(ui): migrate policy impact popover to shadcn (#36653)
* test(ui): characterize policy impact popover

* refactor(ui): migrate policy impact popover to shadcn

* test(ui): type policy impact mocks
2026-08-12 12:42:21 -07:00
yuneng-jiang
806564b14c
refactor(ui): migrate models-and-endpoints to shadcn (#36648)
* test(ui): characterize models and endpoints components

* refactor(ui): migrate models and endpoints to shadcn

* fix(ui): preserve numeric input boundaries

* fix(ui): label models numeric controls

* fix(ui): preserve comma-containing utterances

* fix(ui): deduplicate pasted router utterances

* refactor(ui): keep utterance dedup immutable

* fix(ui): keep model refresh action on tab row
2026-08-12 12:42:17 -07:00
yuneng-jiang
5a617d808a
refactor(ui): migrate team settings to shadcn (#36641)
* test(ui): characterize default team settings

* refactor(ui): migrate teams settings to shadcn

* chore(ui): prune teams lint suppression

* test(ui): preserve teams settings contracts
2026-08-12 12:41:44 -07:00
yuneng-jiang
17c20a5793
refactor(ui): migrate prompts to shadcn (#36643)
* test(ui): characterize prompt editor controls

* refactor(ui): migrate prompts to shadcn

* fix(ui): preserve prompts interaction contracts

* fix(ui): restore prompts history contracts

* fix(ui): preserve prompts escape layering
2026-08-12 12:41:25 -07:00
yuneng-jiang
d329951999
refactor(ui): migrate users dashboard to shadcn (#36642)
* test(ui): characterize users dashboard behavior

* refactor(ui): migrate users dashboard to shadcn

* fix(ui): preserve users tab state
2026-08-12 12:41:10 -07:00
yuneng-jiang
4445eb71f6
refactor(ui): migrate admin-panel to shadcn (#36635)
* test(ui): characterize admin settings components

* refactor(ui): migrate admin-panel to shadcn

* fix(ui): restore compatible page grouping

* test(ui): cover legacy page grouping runtimes

* test(ui): restore admin settings rendering contracts
2026-08-12 12:40:49 -07:00
yuneng-jiang
7d12f21e31
refactor(ui): migrate cost-tracking to shadcn (#36631)
* refactor(ui): migrate cost-tracking helpers to shadcn

* fix(ui): restore export menu keyboard navigation
2026-08-12 12:40:12 -07:00
yuneng-jiang
2b9e3db6b0
refactor(ui): migrate cost-optimization to shadcn (#36629) 2026-08-12 12:40:04 -07:00
Yassin Kortam
eefbe2eb18
fix(proxy): log requests rejected for an unparsable body in spend logs (#36673)
A request whose body never parses is rejected in auth, before the endpoint
runs, so nothing downstream fires the failure hook that writes the spend log
row Request Logs reads. The caller sees a 400 that leaves no trace.

Auth now records that rejection through the same post_call_failure_hook the
endpoints use, keyed to the caller it already authenticated. Logging is
best-effort: a logging failure is swallowed so the 400 the caller sees is
unchanged. The path where the key is also rejected is left alone, since the
auth failure handler already logs that request.
2026-08-12 12:37:15 -07:00
Yassin Kortam
a01b421ce9
fix(mcp): bound MCP client requests with a session read timeout (#36675)
An upstream that ends its response stream without a JSON-RPC reply leaves the
request pending forever. Tool discovery then only ended when an outer cancel
scope killed it, which logged a cancelled list_tools, ignored the timeout the
operator configured, and reported no tools to the client. Prompts and resources
had no outer guard at all.

Give the client session a read timeout so every request it sends is bounded,
including initialize. The SDK reports its own elapsed timeout as an McpError
carrying an HTTP status code in the field that otherwise holds JSON-RPC error
codes, and it relays an upstream's JSON-RPC error through that same class and
field, so the code alone cannot separate the two: an upstream answering with
application code 408 would be blamed on the gateway as a 504. Translate the
SDK's timeout into a TimeoutError in the module that configures the timeout,
matching on the elapsed timeout in the exception's context chain rather than on
the number, so the listing taxonomy never has to read a JSON-RPC code as an HTTP
status and every caller gets the same signal.

The bare cancellation warning is replaced by a line naming the server and the
budget that elapsed, and quiet_on_error does not demote it.
2026-08-12 12:36:24 -07:00
Yassin Kortam
258fe3e4ba
fix(passthrough): carry the budget reservation into request metadata (#36592)
A successful pass-through request left its pre-call budget reservation in
the shared Redis spend counter. `_init_kwargs_for_pass_through_endpoint`
built the request metadata from the sanitized key fields only, so
`_PROXY_track_cost_callback` resolved `budget_reservation = None` and
`increment_spend_counters` added the actual cost on top of a reservation
nobody released. The counter drifted above real spend on every request
until the key falsely tripped BudgetExceededError, while the Postgres
spend stayed far below the limit. The failure path was unaffected because
it releases `user_api_key_dict.budget_reservation` directly.

The reservation is now set alongside the other internal keys, after the
client-supplied metadata merge, so a request body cannot forge one that
names arbitrary counter keys.
2026-08-12 12:34:13 -07:00
yuneng-jiang
98a79ccf92
Merge pull request #36685 from BerriAI/litellm_restore_shadowed_tests
test: rename tests that a later definition shadowed
2026-08-12 12:06:37 -07:00
daniel-meismer-zocdoc
b4a4277a27
fix(ui): align spend and budget columns (#35176)
* fix(ui): align spend and budget columns

* fix(ui): preserve sub-threshold money formatting

Co-Authored-By: Codex

* fix(ui): use two-decimal summary amounts

Co-Authored-By: Codex

* test(ui): tolerate organization lookup in access checks

Scope denied-role assertions to the protected page endpoints so the
organization membership lookup does not make the tests fail.

Generated with AI

Co-Authored-By: Claude Code
Co-Authored-By: Codex
2026-08-12 11:58:27 -07:00
Yuneng Jiang
a5b84d337a
test: address review on the restored SQS tests
Greptile flagged that the newly collected SQS tests construct SQSLogger without
mocking asyncio.create_task, so the constructor's periodic_flush task
(while True: sleep; flush_queue) is left running on the session-scoped event
loop. That is correct, and checking each test against the survivor that shadowed
it changes the answer for two of the three.

test_async_log_success_event_adds_to_queue and its failure variant assert exactly
what their survivors assert, that the payload lands in log_queue. The only
difference is whether create_task is mocked, and nothing asserts anything about
that, so restoring them added a leaked task for no coverage. Both renames are
reverted; those definitions stay shadowed and belong in a deletion set instead.

test_async_send_batch keeps its rename. Its assertion, that async_send_message is
not awaited inline, is only meaningful with a real create_task: under a MagicMock
the await count is trivially zero. So it now wraps the real create_task in a spy
that records the tasks and cancels them in a finally block, which covers both the
periodic_flush task and the dispatched send.

Verification against staging for tests/logging_callback_tests/test_sqs_logger.py:
17 passed and 2 "periodic_flush was never awaited" warnings before, 18 passed and
the same 2 after, so the restored test adds no leak. Those 2 warnings are
pre-existing and come from the survivors mocking create_task with MagicMock.
Across the seven touched files, collection goes from 401 to 409 with nothing
lost, and all 409 pass.
2026-08-12 11:47:11 -07:00
yuneng-jiang
5621f098b2
Merge pull request #36681 from BerriAI/litellm_/loving-babbage-cd55fc
test: remove tests that never execute
2026-08-12 11:41:02 -07:00
Mateo Wang
f082f18e2e
Merge pull request #36628 from BerriAI/litellm_fix_autorouter_consumed_tags 2026-08-12 11:35:15 -07:00
Yuneng Jiang
584a8a0554
ci: drop deleted files from the proxy-server-core shard
The proxy-server-core matrix entry named test_proxy_server_caching.py and
test_proxy_server_langfuse.py by path. This PR deletes both, so pytest exited 5
with "no tests collected" and the whole shard failed without running the four
files that do exist.

assert-shard-coverage did not catch it because it only checks one direction:
every file under tests/proxy_unit_tests/ must appear in some shard. It never
checks that every path a shard names still exists, so a stale entry passes.

After this change no shard names a missing path and no file is left without a
shard. The shard collects 85 tests.
2026-08-12 11:21:39 -07:00
Yuneng Jiang
ff4120863b
test: rename tests that a later definition shadowed
Python keeps only the last binding for a name, so when a file defines the same
test twice the earlier one is unreachable. pytest cannot collect a function that
no longer exists, so nothing reports it and the file still looks like it covers
the scenario.

These ten are cases where the two definitions have different bodies, meaning a
real test was replaced rather than duplicated. Each is renamed to say what it
actually covers, which makes it reachable again:

- test_gemini_frequency_penalty: the dead copy checks the parameter is listed in
  get_supported_openai_params for vertex_ai; the survivor checks get_optional_params
  maps a value for gemini. Different function and different provider.
- test_async_log_success_event_adds_to_queue and the failure variant: the dead
  copies run without mocking asyncio.create_task, so they exercise the real task
  path the survivors mock out.
- test_async_send_batch_triggers_tasks: the dead copy asserts send is not awaited
  directly; the survivor asserts create_task was called.
- test_model_id_in_required_metrics: the dead copy checks the model_id label on
  twelve further metrics the survivor dropped.
- test_anthropic_messages_pt_file_block_preserves_cache_control: the dead copy
  passes model and llm_provider explicitly and uses real base64 PDF content.
- test_translate_streaming_openai_chunk_to_anthropic_with_thinking: the dead copy
  covers thinking_delta; the survivor covers signature_delta.
- test_client_initialization and test_client_without_api_key: the dead copies
  assert the resource clients are wired with the right base URL and key; the
  survivors only construct the object.
- test_client_initialization_strips_trailing_slash: the dead copy constructs
  ModelsManagementClient directly rather than going through Client.

Verification: collecting the seven touched files gives 401 node IDs before and
411 after, the ten new names and nothing else, with nothing lost. All ten pass.
Running the touched files in full gives 299 passed, and test_optional_params.py
goes from 111 passed to 112.

Two further shadowed definitions were left alone rather than renamed: the dead
copies of test_prompt_caching and test_cost_calculator_with_base_model_with_router
have no assertions at all, one being a bare pass and the other a lone import, so
restoring them would add tests that cannot fail.
2026-08-12 11:15:54 -07:00
yuneng-jiang
5cbe8353be
Merge pull request #34635 from BerriAI/litellm_/remove-no-guard-mirror-tests
test: remove four mirror test files that exercise none of their module
2026-08-12 10:51:57 -07:00
Yuneng Jiang
075781568d
test: remove tests that never execute
Three groups, all verified by running the suite rather than by inspection.

18 files whose every test function carries an unconditional @pytest.mark.skip,
39 test functions in total. They are collected on every CI run and always skip,
so they advertise coverage the suite does not have. Reasons on the marks include
"AWS Suspended Account", "lakera deprecated their v1 endpoint" and "moved to
using 'otel' for logging"; 26 of the marks predate 2025.

30 test functions with a byte-identical body and identical decorators to a
sibling in the same file and class, differing only in name. Deleting one of each
pair removes no coverage. Four further candidates were excluded because they
override an inherited test, where deleting the override un-shadows the base
class implementation instead of removing a duplicate.

9 test functions that a later definition of the same name shadows, so Python
never binds them and pytest cannot collect them.

One file that is a demo script rather than a test; its own docstring says to run
it with python.

Verification: collecting the 26 edited files gives 2,492 node IDs before and
2,462 after. The 30 duplicate deletions account for exactly 30 removals, the 9
shadowed deletions account for 0 (confirming at runtime that they were never
collectable), nothing unexplained disappeared, and nothing new appeared. No
other test or module imports any deleted symbol.
2026-08-12 10:45:38 -07:00
yuneng-jiang
16ce5031f0
Merge branch 'litellm_internal_staging' into litellm_/remove-no-guard-mirror-tests 2026-08-12 10:38:24 -07:00
Yassin Kortam
b0626cad8c
perf(proxy): stagger scheduled background jobs across jobs and pods (#36589)
APScheduler anchors an interval job at now + interval, so every scheduled
background job registered in one proxy startup shares a single firing instant
for the life of the process, and every replica a rollout brought up together
shares that instant too. Each tick the spend flushes, budget reset sweep,
config-in-DB reload, credential reload and cost pollers all hit Postgres at the
same moment, on every pod, competing with request-path auth and budget queries.

Shift each eligible job by a deterministic offset derived from
sha256(job_id, identity), where identity covers the pod and the worker process.
The offset lives in the trigger rather than in a one-off next_run_time, because
a cron trigger recomputes each fire from the wall clock and would otherwise snap
straight back onto the shared instant. An interval job is never offset by more
than one of its own periods.

Only schedules LiteLLM chose are shifted: interval jobs always, cron jobs only
when the id is one of the product's own defaults, so an operator-supplied
crontab keeps the instant it asks for. general_settings.scheduled_job_stagger
turns it off, widens the window, replaces the identity, or pins a job. The
applied offsets are logged once at startup and each fire logs its scheduled
instant against its actual start.

Resolves LIT-5433
2026-08-12 09:17:31 -07:00
mateo
3f0306188a fix(slack_alerting): poll while the deprecation alert is disabled instead of sleeping a day
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-12 16:11:50 +00:00
Mateo Wang
8c2edbfc66
Merge pull request #36590 from BerriAI/litellm_lit012_readonly_typeddict
feat(lint): gate writable TypedDict fields with LIT012
2026-08-12 08:39:34 -07:00
mateo-berri
2278118493 fix(slack_alerting): poll for the router inside the loop instead of a capped pre-wait
A capped pre-wait still burns the first daily pass when the router takes
longer than the cap to appear (a >10 minute boot), and reads the router
in two places. Folding the poll into the loop makes the first alert
unconditional on boot duration and keeps a single read per pass.
2026-08-12 08:34:10 -07:00
mateo
6276eabf19 fix(proxy): wait for the router before the first deprecation alert
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-12 15:19:26 +00:00
Praveen11558
0ca0fa22b8
fix: refactor HTTP handler initialization with client support (#30952)
* bug: Refactor HTTP handler initialization with client support

* Update transformation.py

* bug: fixing the passing of clientID for the psc calls

* Update llm_http_handler.py

* Update llm_http_handler.py

* Update transformation.py

* Remove duplicate 'plugins' field definition

Removed duplicate definition of 'plugins' field.

* Update proxy_server.py

* Update transformation.py

* Update transformation.py

* Update test_vertex_gemma_transformation.py

* Refactor HTTP client handling for Vertex Gemma

* Refactor tests to use mock_get_client for HTTP calls

* Update transformation.py

* Update transformation.py

* Refactor patches for async HTTP client in tests

* fix: refactor HTTP handler initialization with client support

---------

Co-authored-by: michelligabriele <gabriele.michelli@icloud.com>
2026-08-12 15:17:51 +02:00
Devin AI
d79b56481d fix(model_prices): sync Groq registry with provider docs
Add missing Groq models and provider-announced deprecation dates

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-12 13:15:14 +00:00
Mateo Wang
f64479e74d
Merge pull request #34177 from atomic/fix/nvidia-nim-ranking-image-passages-top-n
fix(nvidia_nim): preserve image passages and stop sending top_k to /v1/ranking
2026-08-12 01:42:00 -07:00
Marty Sullivan
b048ce4cc1 refactor(logging): drop the type-gate commentary
The comment restated what the gate does and carried incident detail that would drift,
including a claim about downstream callbacks that the evidence does not support. The
rationale belongs in the regression test, which fails if the copy is ever reintroduced
ahead of the gate, rather than in prose that can rot silently

Also corrects that test's docstring for the same overclaim: the raise aborts the handler
body at the redaction call, and what that costs a given deployment was not established
2026-08-12 04:15:33 -04:00
mateo-berri
6dea3a5715 fix(router): spend only the router-selecting tags, keep the caller's other tags constraining the routed tier 2026-08-12 01:10:55 -07:00
Mateo Wang
f8caaf4d2d
Merge pull request #32536 from dcadenas/litellm_fix_codex_responses_namespace_tools
fix(responses): preserve Codex namespace tool calls
2026-08-12 01:10:30 -07:00
Marty Sullivan
132bee892a fix(logging): stop deepcopying results redaction cannot redact
perform_redaction deepcopies the result before inspecting it, but every shape it does not
recognize falls through to the placeholder return at the end of that block, so the copy is
built and then discarded. Binary and HTTP response bodies land in exactly that case: batch
output, file content and audio responses hold an unpicklable `_thread.lock`, so
copy.deepcopy raises TypeError

The raise lands inside the try in Logging.success_handler that also wraps the callback
loop, so the handler body aborts at the redaction call and everything after it is skipped.
It surfaces only as "[Non-Blocking] Exception occurred while success logging cannot pickle
'_thread.lock' object", which is why it can run unnoticed. The async handler body reaches
perform_redaction the same way. Only deployments with message redaction enabled are
affected, since perform_redaction runs only when turn_off_message_logging resolves true

Deciding redactability before copying fixes the crash as a consequence rather than catching
it, and keeps the deepcopy off large batch bodies it was never going to help. Behaviour for
every recognized shape is unchanged: the copy still shields the caller's object from
in-place redaction

Observed on a live gateway with turn_off_message_logging enabled, where every managed-batch
output download logged that error; after this change the error no longer appears
2026-08-12 03:59:22 -04:00
mateo-berri
29c13c47d0 test(router): reference _model_group_with_consumed_request_tags directly for the router coverage gate 2026-08-12 00:37:33 -07:00
mateo-berri
3e41941e35 test(router): reference _forwardable_alias_marker_params directly for the router coverage gate 2026-08-12 00:36:01 -07:00
Mateo Wang
cfcd0cda8a fix(responses): leave namespace unset on non-namespace tool calls 2026-08-12 00:28:15 -07:00
mateo-berri
0f6e5abd49 test(router): reference _model_name_has_plain_deployments directly for the router coverage gate 2026-08-12 00:28:13 -07:00
Mateo Wang
9cc5a818c3
Merge pull request #36154 from BerriAI/devin_ai_sse_keepalive_openai_routes
feat(proxy): global SSE keepalive ping interval for OpenAI-shaped streaming routes
2026-08-12 00:19:13 -07:00
Mateo Wang
a64a83bf36 fix(responses): keep custom_tool_call echoes on their advertised short name 2026-08-12 00:19:08 -07:00
mateo-berri
b7136243c7 test(router): cover the non-mapping litellm_params marker guard and drop redundant docstrings 2026-08-12 00:17:52 -07:00
Mateo Wang
9bfe593241
Merge pull request #35880 from BerriAI/devin_ai_fix_cost_estimate_onprem_provider_35210
fix(proxy): forward resolved provider and deployment pricing in /cost/estimate
2026-08-12 00:08:40 -07:00
Mateo Wang
23b805d5a4
Merge pull request #36447 from BerriAI/litellm_anthropic_fast_mode_speed_usage
fix(anthropic): preserve speed=fast in usage for /v1/messages and pass-through
2026-08-12 00:05:59 -07:00
Mateo Wang
ca14e52b08 fix(responses): requalify echoed namespace tool calls with their flattened name 2026-08-12 00:01:34 -07:00
mateo-berri
bff10db90f fix(router): consume router-selecting tags on litellm_metadata-shaped requests too
/v1/messages and other litellm_metadata endpoints store proxy metadata,
including x-litellm-tags header tags, under litellm_metadata instead of
metadata. The pre-routing hook read request tags with a hardcoded
metadata bucket, so it never saw the tags that selected the marker and
cleared the consumed-tags stamp, and tag filtering then 401'd the routed
tier. Resolve the bucket from the request kwargs instead, matching how
the stamp write and the tag-filter read already resolve it.
2026-08-11 23:46:38 -07:00
mateo-berri
bcba392b21 fix(router): exclude strategy marker deployments from selection when plain siblings exist 2026-08-11 23:44:13 -07:00
yuneng-jiang
5e620af405
Merge pull request #36600 from BerriAI/litellm_/bedrock-retired-sonnet-test-model
test(bedrock): repoint live Claude tests off the retired Claude 3 Sonnet
2026-08-11 23:42:03 -07:00
mateo-berri
1d7c23a424 Merge branch 'litellm_internal_staging' into fix/nvidia-nim-ranking-image-passages-top-n 2026-08-11 23:41:54 -07:00
yuneng-jiang
e5e6728c8e
Merge pull request #36597 from BerriAI/litellm_/litellm-test-failures-debug-b4200d
fix(model_prices): advertise native structured output on every Bedrock DeepSeek V3.2 and GLM 5 id
2026-08-11 23:41:46 -07:00
Mateo Wang
397fcd0e6b fix(responses): serialize flattened namespace tools and keep tool results adjacent to tool_calls 2026-08-11 23:41:44 -07:00
mateo-berri
e53f044d20 fix(proxy): resolve the global SSE keepalive interval through the per-deployment engine
The outer wrap_sse_stream_with_keepalive_pings layer duplicated the
keepalive engine that PR #34423 already runs inside async_data_generator
for chat completions and responses streams, and it kept pinging
deployments whose operator set keepalive_seconds: 0 as a hard disable.
sse_keepalive_ping_interval_seconds is now the global fallback inside
_resolve_keepalive_seconds, so deployment and request values keep
precedence, an explicit 0 still disables, the [1, 300]s clamp applies,
and router-less proxies arm the wrap when the global default is set.
2026-08-11 23:37:43 -07:00