Commit graph

4669 commits

Author SHA1 Message Date
mateo-berri
6a9662a5a8 fix(passthrough): recognize CR-only SSE frame delimiters when minting streamed managed ids 2026-08-26 01:36:18 -07:00
mateo-berri
e0c101b4da fix(passthrough): record ownership of streamed responses under managed ids 2026-08-26 01:05:15 -07:00
Devin AI
055b6f6f69 chore: merge litellm_internal_staging into rolling techdebt branch
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 07:44:58 +00:00
Yuneng Jiang
0c87bf5de9 test(cost-estimate): keep the new tests inside the test-quality budgets
Two of the new lines tripped the ratcheting gate.

TQ005 flagged restoring litellm.model_cost by assignment. Dropped the
save/restore pair for monkeypatch.setitem, which adds the one model the
test needs and takes it back out at teardown, so the module global is
never reassigned.

TQ008 flagged patching litellm.proxy.proxy_server.llm_router. The
endpoint imports the router from that module inside the function body,
so there is no seam to inject through without changing the endpoint.
Suppressed with the reason already used elsewhere in the suite for the
same module global, on the single helper the new tests share.
2026-08-26 00:32:25 -07:00
Yuneng Jiang
430fb71933 test(cost-estimate): pin the prices and period totals /cost/estimate returns
The endpoint already had tests for deployments that set both an input and
an output price, and for litellm_params winning over model_info. Nothing
covered a deployment that prices only one of the two sides, the daily and
monthly totals, or the price and provider read from the public cost map.

Found by changing one line of cost_tracking_settings.py at a time and
running the mapped test file against each change. Nine of eleven one-line
changes went unnoticed: dropping custom pricing entirely when only one
side is priced, billing the unpriced side at something other than zero,
skipping the model lookup so the reported price and provider go empty,
turning zero requests a day into a cost of zero rather than no estimate,
and scaling a period total by one request instead of the real count.

The seven tests added here kill all eleven. The cost math is real; only
the router is faked, matching the fixtures already in this file.
2026-08-26 00:20:35 -07:00
mateo-berri
7abd82f5e8 test(passthrough): cover opaque and jwt credential stripping under custom auth 2026-08-26 00:19:24 -07:00
Yuneng Jiang
2d1e3a1c80
feat(proxy): hide unhealthy models from model listings, opt-in
Adds `general_settings.model_list_healthy_only`, which makes `/models`,
`/v1/models/{id}` and `/model/info` hide models whose backing deployments are
all marked unhealthy by background health checks, for every caller, without
each client having to pass `healthy_only=true`. `/model/info` also gains the
per-request `healthy_only` parameter that `/v1/models` already had.

Everything here is opt-in. With the setting absent, the endpoints take the same
code path they do today and no health lookup runs at all.

The listing filter reads the deployment health cache, which until now was only
populated when `enable_health_check_routing` was on, so `healthy_only=true`
silently did nothing in a plain `background_health_checks` setup. The setting
now also keeps that cache filled. That is a pure write: every routing-time
reader is itself gated on `enable_health_check_routing`, and the cooldown and
failure bookkeeping stays behind that flag, so routing is untouched.

Filtering stays presentation-only and fails open. A hidden model is still
callable, and missing, stale or empty health state hides nothing.
2026-08-26 00:13:52 -07:00
Yuneng Jiang
e475c3268b
test(proxy): pin the request-validation contracts in proxy/_types.py
proxy/_types.py is 4,965 lines holding 202 request and auth models with 27
validators, and its mapped test file was 32 lines covering one of them. The
validators decide what a caller is allowed to send, so a silent change here
reaches customers as a request that should have been refused and wasn't, or
the reverse.

Pins the contracts that carry real consequence:

- the server-only MCP markers and via_virtual_key are stripped from any
  caller-supplied input, so they cannot be forged through the constructor or
  model_validate, while the server can still set them by assignment
- a virtual key is hashed out of the auth object, and Bearer-prefixed and
  bare keys hash alike
- a JWT issuer must name an audience or opt out of one, never both and never
  neither
- a boolean spend reset is refused rather than silently read as 1.0 or 0.0
- a key or user update must say which key or user it updates
- a key lookup naming nothing is refused rather than matching everything
- an organization member cannot be given a role that lives outside an
  organization
- an audit log stores the key it recorded a change to only masked, and keeps
  the non-secret fields intact

Every case asserts the observed value rather than that a call happened, and
nothing is patched. Verified by mutating the source: dropping the marker
strip, flipping the audience rule's and to or, letting booleans through the
spend reset, treating an empty key list as naming a key, and disabling the
role check each turn the suite red.

Moves the file to the path that mirrors litellm/proxy/_types.py, which the
old file's own first line already said it should have been at, and carries
its two tests over.
2026-08-25 23:29:26 -07:00
Yuneng Jiang
d8ad578045
fix(proxy): stop cache eviction errors from failing /key/update
`_delete_cache_key_object` awaited the Redis delete unguarded, so any cache
backend error surfaced as a failure on an operation that had already been
committed. A Redis ACL that denies DEL on LiteLLM's unprefixed token-hash keys
turned a persisted `/key/update` into `400 Authentication Error, No permissions
to access a key`, and `/key/block` and `/key/regenerate` into 500s

Make the helper best-effort, the way `delete_cache_team_object` and
`delete_cache_key_objects` on either side of it already are: log the failure and
carry on. Nothing ends up staler for it, since the in-memory entry is dropped
before the Redis round trip and the write has already committed, so raising only
misreported a success
2026-08-25 23:23:29 -07:00
mateo-berri
728b73d1b3 fix(proxy): strip only the authenticating secret on credential-less Vertex passthrough
PR #38114 dropped whichever header user_api_key_auth would read the caller's
key from, by precedence. Under custom_auth, JWT auth, or no master key that
header is the caller's own Google token, so the bring-your-own-credentials
Vertex branch answered 401 to every valid request.

A header value is now dropped only when it is the master key or when its
hash is the api_key that authenticated the request, so a Google token that
auth never consumed keeps flowing while a LiteLLM key still never reaches
Google.

test_passthrough_post_call_guardrails.py no longer plants a MagicMock
proxy_server module in sys.modules at import, which poisoned sibling tests
that read module globals at call time.
2026-08-25 21:04:27 -07:00
Daniel Meismer
c74a8df52d chore(mcp): satisfy test quality lint
Document the intentional internal seams used by the DCR bridge admission tests and normalize import ordering.\n\nGenerated with AI\n\nCo-Authored-By: Codex
2026-08-25 22:48:22 -04:00
Daniel Meismer
da036ad0f0 fix(mcp): harden DCR bridge admission
Preserve standard Authorization key validation while preventing client MCP credentials from receiving anonymous bridge admission.

Generated with AI

Co-Authored-By: Codex
2026-08-25 22:33:07 -04:00
Daniel Meismer
a66e091cd7 fix(mcp): complete DCR bridge OAuth challenges 2026-08-25 22:33:07 -04:00
Mateo Wang
d0c527f3bb
Merge pull request #36245 from BerriAI/litellm_fix_headroom_stream_leak
fix(anthropic): buffer streamed responses carrying server-fulfilled tools so retrieval tool calls never reach the client
2026-08-25 17:48:31 -07:00
mateo-berri
7965cfdd2b fix(proxy): keep listing a keyless user's own models when their user row is missing
Keys minted by /key/generate get no LiteLLM_UserTable row, so /v2/model/info?user_models_only=true for such a user hit the new None guard and returned 400 where the merge base returned the user's own models. Skip the team-model merge for a missing row instead of raising, since a user with no row belongs to no team
2026-08-25 16:51:03 -07:00
mateo-berri
98b04de455 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_decrease_anys_opus5_round2
# Conflicts:
#	litellm/responses/litellm_completion_transformation/session_handler.py
2026-08-25 16:48:41 -07:00
mateo-berri
1b37397633 fix(router): forward the hold-back keepalive ping live and carry the withheld-output flag through the stream wrappers
The router's pre-content ping filter dropped AgenticAnthropicStreamingIterator's
hold-back keepalive, so a held-back turn sent the client nothing until the buffer
settled. A ping that no lifecycle frame precedes is now forwarded live, since a
fallback's message_start can still follow it without overlapping lifecycles

The proxy's cancel-refund guard checked isinstance against the iterator, but the
proxy only ever sees it behind FallbackAwareAnthropicMessagesStream and
AnthropicMessagesStreamingResponse, so a disconnect during hold-back refunded the
budget reservation anyway. Both wrappers now forward a duck-typed
has_buffered_provider_output flag, and the router wrapper follows a fallback
source so the flag tracks the stream actually being consumed
2026-08-25 16:39:14 -07:00
yucheng-berri
75bf9f9452
fix(router): persist attempted_fallbacks and original_model_group into spend logs metadata (#38107) 2026-08-25 16:22:01 -07:00
mateo-berri
d16afd2027 fix(memory): return 404 when the memory row vanishes before delete
prisma-client-py delete() returns None when the row is already gone, so the
handler reported deleted: true for a row this caller never removed. Surface
the same 404 the read path uses instead
2026-08-25 16:20:17 -07:00
mateo-berri
4582496c8a Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_decrease_anys_opus5_round2
# Conflicts:
#	basedpyright-code-budget.json
#	litellm/proxy/auth/user_api_key_auth.py
#	litellm/proxy/management_endpoints/team_endpoints.py
#	litellm/proxy/management_helpers/utils.py
#	ruff-strict-budget.json
#	tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py
#	type-discipline-budget.json
2026-08-25 16:17:29 -07:00
mateo-berri
4ab654cbc5 merge: litellm_internal_staging into litellm_fix_headroom_stream_leak 2026-08-25 16:04:33 -07:00
devin-ai-integration[bot]
bb22742025
fix(rerank): emit latency and cost headers on /rerank (#35419)
* fix(rerank): emit latency and cost headers on /rerank

Thread the logging object into the rerank httpx calls and pass hidden_params through to get_custom_headers, so x-litellm-overhead-duration-ms, x-litellm-response-duration-ms, x-litellm-response-cost, x-litellm-call-id and the LITELLM_DETAILED_TIMING x-litellm-timing-* headers show up on rerank like they do on chat completions

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

* fix(rerank): keep zero response cost in the /rerank cost header

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

* ci: assign the new rerank endpoint tests to the proxy-endpoints shard

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

* test: suppress TQ008 on the rerank header tests with reasons

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

---------

Co-authored-by: milan <milan@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yassin <yassin@berri.ai>
2026-08-25 15:54:25 -07:00
Daniel Meismer
c673bcd970 fix(mcp): preserve provider access token lifetime
Co-Authored-By: Codex
2026-08-25 18:41:16 -04:00
Yassin Kortam
6c0c91c5ad
fix(team): serialize member_add, member_delete, and delete under the team's advisory lock (#37969)
* fix(proxy): make /team/member_delete's four cleanups atomic

The team roster update, the user.teams update, the team membership
delete, and the team-scoped verification token delete ran as four
sequential writes with no transaction around them, so a failure
between any two left the removal half applied. Thread a single
prisma transaction through all four writes, following the same
tx.<table> pattern /team/member_add and /team/member_update already
use, so either all four land or none do.

* fix(team): serialize member_add, member_delete, and delete under the team's advisory lock

/team/member_add validated a team exists and then wrote the user's teams array and
a membership row without holding anything across that gap, so a /team/delete could
commit its reference sweeps in between and leave a member pointing at a team id that
no longer exists. The write path already re-read members_with_roles under a row lock
before this change, but SELECT ... FOR UPDATE can deadlock with the access-group
endpoints, which lock an access group and then a team.

member_add now takes pg_advisory_xact_lock(hashtext(team_id)) before re-reading the
team and only writes if it is still there, so a delete that already committed is
visible before any write happens. delete_team takes the same lock around its own
row delete and reference sweep, so the two requests can never interleave: whichever
acquires the lock first runs to completion before the other's read can proceed.

Dropping the row lock from member_add's read also dropped the incidental protection
it gave against a concurrent member_delete, which still wrote from the snapshot it
validated against, unlocked, and could silently overwrite whatever member_add had
just committed. member_delete now takes the same advisory lock and re-reads the
roster under it before computing its own write, so it can never resurrect a member
by overwriting from stale data.

Resolves LIT-5544

* fix(team): run member writes on the advisory lock's transaction

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

* fix(team): keep member writes on the lock holder's connection after merge

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

* fix(team): keep the transactional member create an upsert on user_id

The transaction path was creating the email-identified user row outright, where the
regular client path upserts on user_id. Share one upsert helper between both member
paths so the create stays idempotent on the lock holder's connection.

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

* fix(team): read member_delete's user and key rows on the lock-holding transaction

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 21:55:11 +00:00
ryan-crabbe-berri
02cba40bb6
Merge pull request #37756 from BerriAI/litellm_jwt_team_allowed_routes_wildcard
fix(auth): support wildcard prefixes in jwt team_allowed_routes
2026-08-25 13:49:31 -07:00
devin-ai-integration[bot]
c96245d7bf
fix(scim): preserve existing team memberships when POST /Users adoption carries no groups (#38166)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 13:39:34 -07:00
devin-ai-integration[bot]
6d0cc1423e
fix(scim): return user_id as Group members[].value on transformed group responses (#38161)
* fix(scim): return user_id as Group members[].value on transformed group responses

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

* style: format scim transformation tests per ruff

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

* test: drop redundant assertion comment

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>

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 13:37:45 -07:00
ryan-crabbe-berri
b1f903743c Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_jwt_team_allowed_routes_wildcard 2026-08-25 13:33:29 -07:00
Yassin Kortam
104fe73113
fix(dashboard): don't show a stale provider prompt-cache chip on a response-cache hit (#37951)
* fix(dashboard): don't show a stale provider prompt-cache chip on a response-cache hit

The playground's non-streaming chat completion and responses paths replayed a cache hit's original usage payload verbatim, so ResponseMetrics kept rendering the provider's prompt-cache-write/read chips using token counts from the original request. Detect the hit via the x-litellm-cache-key response header and render a Response Cache indicator instead.

* fix(dashboard): expose x-litellm-cache-key through CORS for the playground cache-hit indicator
2026-08-25 13:07:09 -07:00
devin-ai-integration[bot]
0458accaa0
perf(auth): drop guaranteed-miss internal-cache Redis read from team object lookup (#38073)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 12:47:37 -07:00
devin-ai-integration[bot]
e25ce85dbc
fix(proxy): stop expected 4xx responses from saturating worker CPU on failure logging (#38102)
* fix(proxy): stop expected 4xx responses from saturating worker CPU on failure logging

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

* fix(proxy): keep threaded sync failure handler so CustomLogger sync callbacks still run

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

* chore: remove redundant comments per review

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-25 12:44:16 -07:00
milan
6cd1fcdcf0 test: drop unneeded proxy_server patches and ratchet lint budgets
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 18:37:26 +00:00
milan
6f24d85490 fix(proxy): grant users with empty models list direct access in model listing
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 18:26:57 +00:00
ryan-crabbe-berri
cbc931da54 test: assert the poll call shape after the timeout refactor 2026-08-25 11:00:29 -07:00
ryan-crabbe-berri
c04b5dba32 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_model_router_spend_log_model
# Conflicts:
#	tests/test_litellm/litellm_core_utils/test_litellm_logging.py
#	tests/test_litellm/llms/azure_ai/chat/test_azure_ai_transformation.py
2026-08-25 10:55:53 -07:00
ryan-crabbe-berri
0ea6f5e159 fix(azure_ai): stamp the model router's selected model instead of matching on the model name
The model Azure Model Router served was recovered by checking whether the text
"model_router" or "model-router" appeared in a model string. Spend logs applied that
check to the litellm model path, where the route prefix guarantees a match, but the
proxy applied it to the client's model group alias, which carries no prefix. A model
group named anything else therefore lost the selected model in both the response and
the spend row.

AzureModelRouterConfig now stamps the served model onto _hidden_params, and the spend
log payload and the proxy's response restamping read that stamp. The name heuristic
survives as a fallback for callers with no response in hand, routed through
get_azure_ai_route so it lives in one place.
2026-08-25 10:50:11 -07:00
ryan-crabbe-berri
3ebf09464a fix(proxy): give every requests call a timeout so a silent server cannot hang the caller
`requests` has no default timeout, so a host that accepts the connection and
never answers blocks the calling thread forever.

The one on the request path is the HiddenLayer guardrail's `_get_jwt`. It runs
synchronously inside `_call_hiddenlayer` whenever the hour-long JWT expires and
the API answers 401, so a stalled auth host parked the worker's whole event
loop, not just the guarded request. The other eight are the teams and users CLI
clients, which pin the operator's terminal instead.

`TeamsManagementClient` and `UsersManagementClient` now take the same
`timeout: int = 30` their `HTTPClient` sibling already had, and `Client` threads
its own timeout down to teams. `_poll_for_ready_data` already passed a timeout
through a TypedDict that ruff could not see into; passing the argument directly
retires both the TypedDict and the suppression it would have needed.

Graduate S113 into ruff.toml so the next `requests` call without a timeout fails
the lint step.
2026-08-25 10:12:33 -07:00
Mateo Wang
92fe35854b
Merge pull request #38222 from BerriAI/litellm_fix_mantle_reasoning_effort_max
fix(completion_extras): forward reasoning_effort=max through the Responses API bridge
2026-08-25 10:06:08 -07:00
Yassin Kortam
1d695a714b
fix(proxy): reset a stuck team member's budget (#37971)
* fix(proxy): reset a stuck team member's budget

A per-team-member budget check reads a cross-pod spend counter that
nothing ever invalidates. Once a member exceeds their per-member
budget, resetting the key's spend, raising the user's or the team's
own budget, or issuing a new key all leave the member stuck, because
none of them touch this counter or its cached membership object.

Add POST /team/{team_id}/member/{user_id}/reset_spend to reset a
member's tracked spend, and invalidate the same cached state from
/team/member_update when it raises a member's own budget, so that
path also takes effect immediately instead of waiting on the
membership cache's TTL. Name the entity in the check's error message
so a stuck member is diagnosable from the 429 body alone.

* fix(proxy): close reset-vs-floor-read race and surface double Redis write failure on member spend reset

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

* fix(proxy): broadcast spend reset as a SET so the handler's self-delivered message cannot erase the reset guard

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

* fix(proxy): omit null fields from the invalidation message so plain evictions keep the old wire format

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 09:50:09 -07:00
mateo-berri
a73f11ae9c fix(completion_extras): forward reasoning_effort=max through the Responses API bridge 2026-08-25 09:40:23 -07:00
mateo-berri
9dabd72f2d refactor(repositories): type prisma table access with one generic protocol
Every repository handed its `.table` back untyped, so a dozen modules had
each grown a private `_PrismaTableActions` Protocol to paper over it. They
had drifted: some declared `update` as returning the row, others the row or
None, and none agreed on whether `find_many` was covariant

Replace all of them with a single `TableActions[RowT_co]` in
`litellm/repositories/prisma_protocols.py`, keyed to the prisma row each
repository is bound to. Query inputs stay `Mapping[str, object]` so callers
keep passing plain dicts, and `find_many` returns `Sequence` so the row type
stays covariant

Typing the nullable returns honestly surfaced paths that were already
crashing. A team admin could never edit or delete a memory entry owned by
their team: the write-auth check fed a raw prisma row to a helper that
expects the domain model, so `members_with_roles` arrived as plain dicts and
the request died as a 500 instead of applying the edit. Non-admin members hit
the same 500 in place of the 403 they were owed, so refusal and breakage were
indistinguishable. `/v2/model/info?user_models_only=true` dereferenced a
missing user row rather than returning the 400 the route already had, three
team routes dereferenced a team deleted between the read and the write, and
the agent registry dereferenced a missing agent instead of naming it

basedpyright drops 2,132 errors, 1,454 of them reportAny and 73
reportExplicitAny. The dashboard's generated types pick up `string[]` where
they had `unknown[]` for a team's members, admins and models
2026-08-25 12:14:17 +00:00
Devin AI
2dfe564479 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_deflake_20260821 2026-08-25 09:45:52 +00:00
Devin AI
41acaf4183 merge: bring litellm_internal_staging into the rolling techdebt branch 2026-08-25 07:44:33 +00:00
ryan-crabbe-berri
891b23d680
Merge pull request #38156 from BerriAI/litellm_caching_error_drilldown
feat(ui): add error-code drilldown for failed requests on caching page
2026-08-24 18:30:41 -07:00
Mateo Wang
82b0069f83
Merge pull request #38155 from BerriAI/litellm_forward_video_edit_source_file
fix(videos): forward uploaded source file on /v1/videos/edits to the provider
2026-08-24 18:00:29 -07:00
mubashir1osmani
65af77c43b merge(litellm_internal_staging): reconcile batch observability with per-line resilience
Staging split batch output-line costing into _safe_output_line_stats /
_compute_output_line_stats / _output_line_cost so one uncostable line can no
longer zero a whole batch, and added _provider_output_file_id so model-encoded
output file ids decode before the fetch. This branch's pass/fail counting was
written against the pre-split shape, where every None line meant a provider
failure.

Keep staging's structure and layer the counts on a three-way classification: a
provider-reported failure yields PROVIDER_FAILED, a provider-successful line
litellm cannot price yields UNCOSTABLE and stays in successful_requests billed
at $0. Without that split a litellm-side pricing gap would be reported to the
customer as a failed request and the counts would stop reconciling with the
provider's own request_counts.

Route the error-file fetch through _provider_output_file_id too, and carry the
new dataclass return through the callers staging added after this branch
forked.
2026-08-24 19:08:40 -04:00
ryan-crabbe-berri
0de0e1cef6 feat(ui): add error-code drilldown for failed requests on caching page
/global/activity/cache_hits now returns an error_breakdown: failed spend
logs bucketed per call_type by error code and error class, read from
metadata->error_information. Clicking a red failed-requests segment on
the cache activity chart opens a per-code bar chart; hovering a bar
lists the error classes behind that code.
2026-08-24 15:45:45 -07:00
mateo-berri
7d0df4a062 fix(videos): forward uploaded source file on /v1/videos/edits to the provider
The video edit endpoint parsed the multipart body but dropped the uploaded
source video, only normalizing it to an id. When a raw file is uploaded it now
flows through videos.main -> the http handler -> the provider transform, which
emits multipart/form-data with the source video as a file part, matching the
official OpenAI SDK's videos.edit wire format. Edit-by-id still egresses JSON.
2026-08-24 15:34:21 -07:00
Mateo Wang
46c2328641
Merge pull request #38114 from BerriAI/litellm_fix_5997_vertex_pt_key_leak
fix(passthrough): stop leaking the caller's virtual key on credential-less Vertex passthrough
2026-08-24 14:54:53 -07:00
mateo-berri
16a81c9ceb fix(vertex-passthrough): cover the mapped-route litellm_user_api_key header
On mapped pass-through routes, of which /vertex_ai is one,
user_api_key_auth accepts the caller key from a header literally named
litellm_user_api_key and applies it last, so it overrides every other source.
The credential-less filter neither dropped it nor resolved the caller key from
it, so a virtual key there reached Google past a real x-goog-api-key, and a
bring-your-own Authorization could be stripped when auth actually came from that
header. Drop it by name and resolve it at highest precedence.
2026-08-24 14:30:44 -07:00