Commit graph

382 commits

Author SHA1 Message Date
mateo-berri
a49fbc6272 fix(proxy): keep the raw client model out of the stored request body when a spend row is placeholdered
With store_prompts_in_spend_logs on, the persisted request body kept the client's model string even when the row's model, model_group, and error text had been replaced by the unknown-model placeholder. The body's model now takes the same placeholder on those rows. Also annotates the new test locals with Final and wraps the four test lines that ran past 120 characters.
2026-09-19 03:20:31 -07:00
mateo-berri
2e3667b270 fix(proxy): keep the raw client model out of spend logs for rejections outside the router 2026-09-19 02:01:28 -07:00
yucheng-berri
8e93031c19
Merge pull request #41786 from BerriAI/litellm_passthrough_xpass_trace
Pass-through requests inject the proxy span into upstream headers since #40669, which
replaced an explicit x-pass-traceparent with an unrelated trace and dropped its
x-pass-tracestate. Keep the caller's context when the carrier already names a
different trace, and keep the proxy child span for same-trace or missing headers.

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:07:28 -07:00
Yassin Kortam
47209d37f2
Merge pull request #41882 from BerriAI/litellm_azure_speech_api_base_prefix
fix(proxy): classify Azure Speech short audio behind a prefixed api base
2026-09-18 14:50:28 -07:00
Yassin Kortam
52d6aab421
Merge pull request #41554 from BerriAI/litellm_deepgram_listen_websocket_passthrough
feat(passthrough): deepgram streaming /v1/listen WebSocket passthrough with duration-based cost tracking
2026-09-18 14:48:37 -07:00
yassin
0b5b69ea3a fix(deepgram): forward only the first model and language values to /listen
Authorization and pricing read the first model and language query value, but the raw query was forwarded, so Deepgram (which honours the last repeated value) could be sent a model the key was never allowed. Later duplicates of those two keys are now dropped before the upstream URL is built

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 21:29:29 +00:00
yassin
f1b9642c41 fix(proxy): classify Azure Speech short audio behind a prefixed api base
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 21:15:01 +00:00
yassin
93d61abfa5 fix(deepgram): refuse /listen sessions that have no streaming price
A caller could pick a model with only a pre-recorded registry row, or no row at all, and the session would be billed at the pre-recorded rate or logged at zero cost, so budgets did not apply. The route now closes the WebSocket with 1008 before dialing Deepgram unless deepgram/streaming/<model> (or the -multilingual row for language=multi) is an exact registry hit, and the logging handler applies the same check so a registry change under a live session records the duration with no cost instead of a substitute rate

Regression tests cover the route refusal, an operator-supplied streaming row for another model being accepted, and the handler never substituting the pre-recorded rate

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 21:08:14 +00:00
Yassin Kortam
6759f28e73
Merge pull request #41557 from BerriAI/litellm_azure_speech_passthrough
feat(proxy): add Azure AI Speech pass-through route
2026-09-18 14:05:55 -07:00
Mateo Wang
c553bc92bd
Merge pull request #41875 from BerriAI/litellm_passthrough_stream_timeout
fix(router): honor stream_timeout on the SDK-native passthrough route (/v1/messages, /converse)
2026-09-18 13:59:02 -07:00
mateo-berri
74e9fb2323 fix(passthrough): validate only the winning timeout value in the resolver 2026-09-18 13:07:33 -07:00
mateo-berri
58beea2275 fix(passthrough): read the stream flag by truthiness in the timeout resolver 2026-09-18 12:45:54 -07:00
yassin
1e7c5400fd fix(proxy): canonicalize azure speech paths and bill uploaded short audio
Resolve dot segments in the /azure_speech endpoint path before the endpoint family and the admin-only batch guard are decided, so the guard and the forwarded upstream path agree. Bill short-audio requests for the longer of the uploaded audio duration and the recognized duration, so a NoMatch or silence response still charges for the audio Azure processed

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:27:25 +00:00
yassin
84f7adec2e fix(passthrough): match deepgram listen routes served under a path prefix
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 19:06:24 +00:00
mateo-berri
73fddb999e fix(router): resolve stream_timeout before generic timeouts on the passthrough route 2026-09-18 11:53:26 -07:00
yassin
1e6b33ffab Merge remote-tracking branch 'origin/main' into litellm_azure_speech_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	helm/litellm/templates/ingress.yaml
#	litellm/proxy/_types.py
#	litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
#	litellm/proxy/pass_through_endpoints/success_handler.py
#	terraform/litellm/aws/locals.tf
#	terraform/litellm/gcp/locals.tf
#	tests/test_litellm/proxy/middleware/test_billable_request_metrics_middleware.py
2026-09-18 18:42:22 +00:00
yassin
aeca6ed7ba chore: merge main into litellm_deepgram_listen_websocket_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 18:39:39 +00:00
yassin
fa70e49b81 chore: merge main into litellm_transcribe_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 17:54:59 +00:00
yucheng
318b027782 fix(otel): keep caller traceparent and tracestate on pass-through relays
Pass-through requests inject the proxy span into upstream headers since #40669, which
replaced an explicit x-pass-traceparent with an unrelated trace and dropped its
x-pass-tracestate. Keep the caller's context when the carrier already names a
different trace, and keep the proxy child span for same-trace or missing headers.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 08:26:39 +00:00
yassin
3f824793e5 fix(proxy): price a Transcribe job from the parsed start response instead of the relayed httpx body
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:48:09 +00:00
yassin
2decf3761a fix(proxy): price a deleted Transcribe job from the start response the proxy relayed
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:08:21 +00:00
Yuneng Jiang
349e8b9358
test(proxy): forward each method through the typesafe route to a mocked upstream
The route test only resolved route names. It now sends every method through
the proxy with a virtual key and asserts the upstream receives that method,
the proxy's TypeSafe key and the caller's body
2026-09-17 18:19:42 -07:00
yassin
393d084db7 feat(proxy): restrict Transcribe media and output buckets per operator allowlist
Non-admin keys may only start transcription jobs whose media and transcript output live in the S3 buckets listed in general_settings.transcribe_media_buckets, and may not supply DataAccessRoleArn or JobExecutionSettings. The setting is editable from the Admin UI general settings table (new List editor) and DB values load into the running proxy when config.yaml does not set it

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 01:18:07 +00:00
yassin
659b85ea80 Merge remote-tracking branch 'origin/main' into litellm_deepgram_listen_websocket_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/pass_through_endpoints/pass_through_endpoints.py
2026-09-18 01:17:36 +00:00
Yuneng Jiang
e8c1fe884e
Merge remote-tracking branch 'origin/main' into litellm_/circleci-specific-sha-0cf414 2026-09-17 18:05:39 -07:00
Yuneng Jiang
acc375a2a9
fix(proxy): forward every method on the typesafe pass-through route
#41607 registered the typesafe pass-through with a route that only accepted
GET and POST, so a PUT, DELETE or PATCH to /typesafe/... came back 405
before reaching the upstream. CircleCI's pass-through method test caught it,
but that lane does not run on the PR gate, so the mapped unit test now
covers the same invariant for typesafe

The same CircleCI run also failed test_models_by_provider because typesafe
is not a key of models_by_provider. Registering it there would satisfy the
assertion without changing behaviour: typesafe has no LlmProviders member,
so a typesafe/* deployment never loads and get_valid_models returns nothing,
and its spend is priced straight from model_cost. The test already skips
search-mode providers for that reason, so it now skips evaluation mode too
2026-09-17 18:04:58 -07:00
Mateo Wang
3424390101
Merge pull request #41448 from BerriAI/litellm_fix_passthrough_empty_query_params_drop_url_query
fix(passthrough): keep target URL query when client sends no query params
2026-09-17 18:02:48 -07:00
yassin
4f8a5b5e12 Merge remote-tracking branch 'origin/main' into litellm_transcribe_passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/_lazy_features.py
#	litellm/proxy/_lazy_openapi_snapshot.json
#	litellm/proxy/pass_through_endpoints/success_handler.py
#	ui/litellm-dashboard/src/lib/http/schema.d.ts
2026-09-18 00:17:47 +00:00
yassin
ea1fd5f288 fix(proxy): price deleted Transcribe jobs from their start response and read media length without loading it
Restrict signed media fetches to https URLs, treat a job AWS no longer knows as
priceable from the media named in its StartTranscriptionJob response instead of
polling to the eight hour maximum, and read the media length with libsndfile
headers instead of decoding the whole file into memory

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 00:16:18 +00:00
yassin
d0591665b5 Merge remote-tracking branch 'origin/main' into HEAD
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/pass_through_endpoints/success_handler.py
#	tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py
2026-09-17 23:44:13 +00:00
yassin
ce735f586c fix(proxy): scope Transcribe jobs to the key that started them and charge rewritten media the maximum
Standard jobs are tagged litellm-owner on StartTranscriptionJob so GetTranscriptionJob
and DeleteTranscriptionJob only work for the owner or a proxy admin, and account-wide
operations need a proxy admin. Media rewritten after job creation is charged the eight
hour maximum, and the success handler takes an injected log dispatch instead of tests
patching its private method

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 22:58:15 +00:00
mateo-berri
1feaa48705 fix(proxy): log TypeSafe calls that name no model as unknown 2026-09-17 15:37:00 -07:00
yassin
4885594a1e fix(proxy): use path-style S3 URLs for dotted Transcribe media buckets
Virtual-hosted URLs for bucket names containing dots fail TLS verification, so the
media duration fetch failed and completed jobs were charged the eight hour maximum

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 21:55:32 +00:00
yassin
16500bdf07 fix(proxy): cap Transcribe pricing media downloads by size and concurrency
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 21:41:33 +00:00
yassin
6e7c3f68a1 test(deepgram): pin litellm.max_budget to zero in the model authorization route test
Under xdist the per-test litellm reload is skipped, so a leaked max_budget from another proxy test sent the real
key auth path into the global spend lookup, which the MagicMock prisma client cannot await

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 21:06:07 +00:00
yassin
2801614878 fix(proxy): bill Transcribe jobs by media length and refuse media LiteLLM cannot measure
Amazon Transcribe bills every second of the media file, silence included, while the
transcript's last end_time stops at the last word, so pricing from the transcript
undercharged. After a job completes, download Media.MediaFileUri from S3 with the
proxy's credentials and read its length with libsndfile. Formats libsndfile cannot
read (mp4, m4a, webm, amr) and custom language models under LanguageIdSettings are
refused before signing. The S3 signature is only sent to hosts in the AWS partition

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:52:10 +00:00
yassin
11ec157d71 fix(deepgram): authorize the effective model and price /listen sessions at streaming rates
Key auth on the Deepgram WebSocket route now sees the same model the upstream target will carry, so a key restricted to other models can no longer reach nova-3 by leaving model out of the query. user_api_key_auth_websocket keeps its signature and delegates to user_api_key_auth_websocket_for_model, which the Deepgram route calls with deepgram_listen_requested_model

Sessions are priced from new deepgram/streaming/* registry rows (nova-3, nova-3-multilingual for language=multi) plus per-minute add-on rows for redact, keyterm, detect_entities and diarize, all read from Deepgram's pricing page on 2026-09-17. Models without a streaming row fall back to their pre-recorded row as before

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:36:03 +00:00
yassin
25846d1341 fix(proxy): limit the whole Azure Speech batch API to proxy admin keys
Ordinary keys could read, patch and delete batch transcription jobs that other keys created with the proxy's shared Azure subscription, so every /speechtotext/v3.2 method is now admin only while fast transcription stays open. Also clears SERVER_ROOT_PATH in the real-auth test helper because test_custom_proxy leaves it set at import time and the shared app then 404s pass-through routes

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:29:51 +00:00
yassin
784fe5bfd8 fix(proxy): price Amazon Transcribe jobs at completion so budgets apply
StartTranscriptionJob was logged with response_cost 0.0, so key, team and proxy
budgets never stopped repeated jobs on the proxy's AWS credentials. The success
handler now polls GetTranscriptionJob to completion, reads the audio duration
from the transcript artifact and charges whole seconds at the cost map rate,
charging the longest media AWS accepts when the duration cannot be read. The
route refuses job classes and surcharge features the cost map does not price

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 19:11:45 +00:00
yassin
db560ca652 fix(passthrough): bill every Deepgram channel, not just wall-clock duration
Deepgram charges for the total processed audio across channels, so a stereo /listen session with multichannel=true costs twice its duration. The handler now multiplies the session duration by a validated channel count taken from Metadata.channels, then the widest Results channel_index, then the channels query parameter, defaulting to one. Booleans, floats, strings, zero and negative values are ignored

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 19:02:44 +00:00
yassin
849859001f fix(deepgram): refuse callback delivery on the /listen passthrough so sessions cannot go unbilled
With callback or callback_method in the query, Deepgram sends every Results and Metadata frame to the caller's URL and only a request id down this socket, so the proxy would meter zero seconds of audio while its own Deepgram credential paid for the transcription. The route now closes such connections with 1008 before contacting Deepgram, naming the offending parameters in the close reason. Adds helper and route tests for both parameters and a nine mutation sweep, all killed

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 18:18:40 +00:00
yassin
5f64dfd8dd fix(proxy): price Azure Speech fast transcription and limit unpriced batch writes to admins
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 18:14:04 +00:00
mateo
b2ef8daee8 fix(proxy): stop duplicating query params on the TypeSafe passthrough
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 18:03:08 +00:00
mateo
78eb92ca55 refactor(proxy): simplify TypeSafe passthrough pricing lookup and route
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 15:55:57 +00:00
mateo
2dc9697381 feat(proxy): add TypeSafe Jev passthrough spend tracking
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 15:53:19 +00:00
yassin
1d8f19e4fd fix(proxy): keep Azure Speech multipart bodies intact through auth
user_api_key_auth called request.form() on multipart Azure Speech batch uploads, consuming the Starlette stream before the pass-through handler could read the raw bytes. The opaque body predicate now covers multipart on the whole /azure_speech prefix so auth caches an empty parsed body and the upload is forwarded byte for byte

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 04:14:26 +00:00
yassin
6e56ba86c5 test(proxy): clear leaked auth dependency override before Azure Speech real-auth tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 04:03:22 +00:00
yassin
585c32d3f5 refactor(deepgram): move listen frame parsing into llms/deepgram and drop routine docstrings
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 03:53:28 +00:00
yassin
f2305879d0 feat(proxy): price Azure Speech short audio pass-through from the recognized duration
Short audio responses carry Offset and Duration in 100ns ticks; convert their sum to seconds and price it with the existing azure/speech/azure-stt entry through transcription_cost. Batch calls and responses without an integer duration stay at zero cost

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 03:31:29 +00:00
yassin
6e1b4959d1 feat(passthrough): deepgram streaming /v1/listen WebSocket passthrough with duration-based cost tracking
Adds authenticated /deepgram/v1/listen and /deepgram/listen WebSocket routes that resolve the Deepgram
credential through the pass-through router, inject Authorization: Token upstream, default the model to
nova-3 when the client passes none, and relay audio and transcript frames unchanged. The shared WebSocket
relay no longer assumes the first upstream frame is JSON and forwards every frame as received, keeping
the Vertex AI Live setup handling on Vertex routes only. A Deepgram logging handler bills the call on
Metadata.duration, falling back to the furthest Results start + duration, at the deepgram/<model>
per-second rate from the model cost map

Resolves LIT-7937

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 03:14:13 +00:00