Commit graph

310 commits

Author SHA1 Message Date
mateo-berri
043c954aa9 fix(proxy): keep a caller's own Anthropic key when the proxy has no master key
Without a master key the auth layer echoes whatever key the caller presented as the authenticated key, so the passthrough's strip-by-value matched the caller's own Anthropic key and dropped it: a bring-your-own-key request that returned 200 on main answered 401 telling the caller to send the key they had just sent. Only the auth module's own no-auth dev-mode definition, shared through is_no_auth_dev_mode, decides that nothing was authenticated, and only when no custom auth is installed; JWTs, OAuth2 tokens, and custom-auth credentials are still stripped there. The sk- prefix heuristic goes with it.

The Vertex credential-less test now sets a master key, since a virtual key can only authenticate under one: the auth layer returns before any key lookup when the master key is unset.
2026-09-16 12:52:37 -07:00
mateo-berri
774fc6021b chore(proxy): drop restating docstrings on the passthrough header helpers and refresh the lazy OpenAPI snapshot 2026-09-16 11:35:43 -07:00
mateo-berri
938702a3a7 Merge branch 'main' of https://github.com/BerriAI/litellm into litellm_anthropic_passthrough_strip_virtual_key 2026-09-16 11:28:09 -07:00
mateo-berri
3a8679d3d8 fix(proxy): never forward the LiteLLM virtual key to Anthropic on the /anthropic passthrough
The /anthropic/{endpoint} route forwarded every incoming header upstream, so
the header carrying the caller's LiteLLM virtual key (Authorization, x-api-key,
x-litellm-api-key, or the operator-configured key header) reached Anthropic and
was rejected there as an invalid credential, with or without a proxy-side
Anthropic key layered on top.

Share the Vertex credential-less header filter: drop the proxy-only credential
headers by name, drop the value that authenticated the caller (virtual key,
master key, or JWT) from Authorization / x-api-key, keep a caller's own
Anthropic credential, layer the proxy's Anthropic credential on top, and fail
with a clean 401 when neither the proxy nor the caller supplied one.

Resolves LIT-3550
2026-09-15 17:54:58 -07:00
Yassin Kortam
7eeba69016
Merge pull request #41316 from BerriAI/litellm_nvidia_nim_infer_passthrough
feat(proxy): add /nvidia_nim passthrough route for NIM object detection and OCR /v1/infer
2026-09-15 17:28:25 -07:00
Yassin Kortam
636313e9fc
Merge pull request #41322 from BerriAI/litellm_gcp_video_usage
fix(vertex_ai): bill Gemini Omni Interactions usage and Veo sampleCount on passthrough
2026-09-15 17:21:15 -07:00
yassin
65f9d9bbbd test(proxy): use the module-level HTTPException import in the /nvidia_nim route tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 23:57:16 +00:00
yassin
74a2c2eab1 refactor(vertex_ai): tighten Interactions route match and type the usage boundary
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 23:34:31 +00:00
yassin
771b2509d1 fix(proxy): reject mixed NIM model groups and strip the deployment model before the group in /nvidia_nim URLs
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 23:34:22 +00:00
yassin
b50a22b370 fix(proxy): restrict /nvidia_nim route to NIM-backed model groups and inject router in tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 22:55:14 +00:00
yassin
b13cf21fd1 fix(vertex_ai): bill Gemini Omni Interactions usage and Veo sampleCount on passthrough
Route Vertex and Gemini /interactions create responses through the shared
Interactions usage transformation so SpendLogs carry per-modality tokens and
cost instead of zeros, and carry parameters.sampleCount into Veo usage as
video_count so duration-based video cost is multiplied by the number of
generated videos

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 22:42:27 +00:00
yassin
9261a72eb7 test(passthrough): cover Vertex router resolution without a matching deployment
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 22:14:44 +00:00
yassin
69be041da7 feat(proxy): add /nvidia_nim passthrough route for NIM object detection and OCR /v1/infer
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 22:11:08 +00:00
yassin
96bffb1290 fix(passthrough): attribute Vertex passthrough successes to the resolved router deployment
The Vertex passthrough route resolved a router deployment only to rewrite the
upstream URL and dropped its model_info, so the standard logging payload and
the Prometheus litellm_deployment_success_responses_total counter carried
model_id="". Carry the deployment's model_info through request.state into the
passthrough logging metadata, where it overrides any client-supplied model_info.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 21:39:39 +00:00
Yassin Kortam
cdb4eaac59
Merge pull request #40262 from BerriAI/litellm_lit_7190_inline_token_counts
fix(proxy): run the remaining inline token counts off the event loop
2026-09-15 13:38:00 -07:00
mateo-berri
4f27573424 merge: origin/main into litellm_internal_copy_37075 2026-09-15 00:34:18 -07:00
Mateo Wang
fe5ff9d3b0
Merge pull request #40771 from BerriAI/litellm_regression_coverage_followup
test: tighten regression tests added in #37974
2026-09-12 15:29:36 -07:00
Mateo Wang
c040061f15
Merge pull request #40766 from BerriAI/litellm_fix_v1_messages_disconnect_partial_cost
fix(anthropic): price recovered tokens when a /v1/messages client disconnects mid-stream
2026-09-12 15:21:43 -07:00
devin-ai-integration[bot]
729ea6b832
perf(proxy): lazy-load provider passthrough routes (#40691)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 09:48:53 -07:00
mateo-berri
966a58d1c8 Merge branch 'litellm_internal_staging' into litellm_lit_7022_azure_ai_passthrough_config 2026-09-09 19:42:28 -07:00
mateo-berri
d644e4970f Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_bedrock_sign_request_off_loop
# Conflicts:
#	tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py
2026-09-09 15:43:48 -07:00
Marty Sullivan
9580b89bb1 fix(vertex-live): resolve a Live setup model before logging reads it
A client that named a bare gateway alias logged the session as "unknown" and billed nothing,
because the model was read off the raw setup frame and the extractor only yields a name when the
string already contains "/models/". The rewriter qualifies that same model a few lines later for
the upstream, so the supported client form, an alias, was the one that went unbilled.

Resolving through the rewriter first means the real model reaches the logging object, and from
there the cost map. A route with no rewriter, which is every non-Live passthrough, hands the frame
over untouched.

(cherry picked from commit 573982803df612fd94144e2e06dd647f8530d4e8)
2026-09-08 23:28:47 -04:00
mateo-berri
4fa34accb8 Merge remote-tracking branch 'origin/litellm_internal_staging' into HEAD
# Conflicts:
#	litellm/litellm_core_utils/litellm_logging.py
2026-09-08 17:17:31 -07:00
mateo-berri
5e056a264e refactor(azure): move the passthrough deployment-segment helpers under llms
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Waiting to run
Terraform Modules / fmt, validate, test (gcp) (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-09-08 12:45:05 -07:00
mateo-berri
8b89c909a9 fix(proxy): keep a ProxyException's status and label 408s in the OpenAI error payload
error_status_code only read status_code, so a ProxyException raised
before routing (which stores its status as the string code) answered
500 with its 4xx type through the rerank, images, realtime, files, and
pass-through tails. It now falls back to a decimal code. A 408 maps to
timeout_error instead of invalid_request_error.

Tail regressions for rerank, images, realtime calls, and the chat
pass-through fail at the merge base with ('None', 'None'); the new
files-test helpers are fully typed.
2026-09-08 12:16:08 -07:00
mateo-berri
89c3a8216b fix(bedrock): sign requests off the event loop on every async path
SigV4 signing resolves AWS credentials, and botocore refreshes expiring
credentials inside that signing with a blocking HTTP call. Every async
Bedrock path that still signed on the event loop (/v1/messages, Converse,
count tokens, the agent-runtime and Comprehend Medical pass-throughs,
async-invoke status polling, realtime, AgentCore, SQS, S3) now signs on a
worker thread, so one Bedrock request no longer stalls the whole worker.

Fixes #40165
2026-09-08 11:54:43 -07:00
yassin
b5a7032eb4 fix(proxy): run the remaining inline token counts off the event loop
Wrap the context-management editors, the end-of-stream chunk builder,
acount_tokens, the compression interception hook, the passthrough
interrupted-stream recovery, the A2A usage counters, and the semantic
cache embedding truncation in asyncify so a multi-megabyte payload no
longer stalls the worker's event loop while it is tokenized

The pass-through suite now drains the process-global logging worker
from an autouse conftest fixture so work queued on one test's loop
cannot fire against the next test's callbacks

Resolves LIT-7190

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-08 18:42:38 +00:00
mateo-berri
1de369a1a4 fix(passthrough): bound azure relays to the key's model group and reject foreign deployment segments 2026-09-07 21:54:37 -07:00
mateo-berri
02b18b4fc6 fix(passthrough): match image URL schemes case-insensitively when counting relayed prompt tokens 2026-09-07 19:10:06 -07:00
mateo-berri
22d7616fe7 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_7022_azure_ai_passthrough_config
# Conflicts:
#	tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py
2026-09-07 17:44:44 -07:00
yucheng-berri
1009976c49
fix(bedrock): keep x-amzn-RequestId on chat error responses (#40089)
* fix(bedrock): keep x-amzn-RequestId on chat error responses

Bedrock chat error paths built BedrockError from only a status code and a
message, so the provider response headers were gone before exception mapping
ran and the proxy had nothing to forward. AWS support needs x-amzn-RequestId
to investigate a server-side error.

- converse and invoke chat handlers pass the real headers and response when
  they turn an httpx.HTTPStatusError into a BedrockError, and read the body
  through error_response_text so a streamed body nobody read does not throw
- every bedrock chat get_error_class honors the headers it is already handed:
  invoke, moonshot, bedrock-hosted openai, agentcore and the invoke agent
- BedrockError carries those headers into the response it synthesizes when a
  caller has headers but no response, skipping values httpx cannot carry
- the bedrock 500 mapping forwards the provider response like its 4xx and 503
  siblings instead of fabricating a blank one

The proxy now returns llm_provider-x-amzn-requestid on Bedrock chat errors.

* fix(bedrock): keep request-id on text-classified errors

The context-window and image branches of _map_bedrock_exception built their
litellm exception without the provider response, so a Bedrock 400 classified
by its body text lost x-amzn-RequestId while the sibling branches kept it.

Also narrows the new BedrockError types and trims its docstrings.

* chore(bedrock): drop the docstrings on the new error helpers

* fix(bedrock): keep request-id on every error path that has one

The ticket's root cause is that every BedrockError raise site under
litellm/llms/bedrock/ was built from status and message alone. The first
commits covered the chat and invoke handlers; this covers the rest.

Embeddings, rerank, image generation, image edit, count tokens, search and
the transformation layers now hand on the provider response or its headers,
and both bedrock_mantle configs return a BedrockError instead of the
OpenAI error that drops them.

Two blockers surfaced while verifying the streaming path. The trailing
`except Exception` in make_call and make_sync_call swallowed the BedrockError
raised a few lines above, relabelling a provider status as a 500, and the
non-200 branch read an unread streamed body, which throws.

The raise sites left alone have no provider response to carry: timeouts,
credential and config errors, and mid-stream event frames.

* fix(bedrock): forward provider headers from the count tokens route

The count tokens route converts BedrockError into an HTTPException, and dropped
the headers the handler had just kept, so that route still lost the request id.

get_response_headers now takes a Mapping so an httpx.Headers can be handed to it
without a copy.

* fix(bedrock): classify every bedrock surface through BedrockError

Eleven bedrock configs still inherited a provider-agnostic get_error_class
that builds a blank response, so the request id was gone before the proxy
read it. Claude platform, bedrock anthropic-messages, both image edit
configs, passthrough, realtime, vector stores and agentcore search now
return BedrockError, and a parametrized audit drives all 36 configs.

* fix(proxy): keep provider headers on the httpx status error branch

_handle_llm_api_exception forwards safe_headers on every branch except the
httpx.HTTPStatusError one, which the bedrock passthrough route reaches, so
the request id was dropped before the client saw the response.

* fix(bedrock): keep the request id on the timeout mappings

Timeout takes no response argument, so the three bedrock timeout branches
dropped the provider headers even when the upstream answered 408 or 504
with an x-amzn-RequestId. They now ride on the exception, already
llm_provider-prefixed, which is the form the proxy emits.

* fix(bedrock): keep the provider response on mapped timeouts

The previous round attached llm_provider-prefixed headers directly to the
Timeout. That shadowed the raw upstream headers for _get_response_headers,
so router cooldown and fallback cooldown stopped honouring retry-after on
bedrock 408/504 replies.

Give Timeout an optional response instead, the way every other mapped
bedrock exception already carries one. Retry logic reads the raw
retry-after off the response, and the proxy prefixes those headers on the
way out, so clients still see llm_provider-x-amzn-requestid.

* chore(bedrock): drop the explanatory comment on Timeout.response
2026-09-07 17:16:47 -07:00
mateo-berri
2dabac186a fix(anthropic): clear the placeholder cost once and let logging price recovered tokens
Drop the second pricing pass on interrupted /v1/messages streams: clearing
the stale usage.cost and hidden response_cost is enough for the existing
success and failure logging to price the recovered usage. Add an iterator
test for the upstream-close path the proxy takes on a client disconnect.
2026-09-05 03:53:40 -07:00
mateo-berri
4706acef95 fix(passthrough): charge remote high-detail images at the high-detail upper bound 2026-09-05 03:48:06 -07:00
mateo-berri
eb9beced71 fix(anthropic): price recovered tokens when a /v1/messages client disconnects mid-stream 2026-09-05 02:49:09 -07:00
mateo-berri
2f981d14e4 refactor(passthrough): inject the streaming prompt-token counter instead of a default-image flag 2026-09-05 02:09:26 -07:00
mateo-berri
cd25eb9189 fix(azure_ai): cost streaming relays and return upstream errors from router relays
Streaming chat relays on Azure and azure_ai deployments rebuild the response from
the SSE chunks through the OpenAI passthrough assembler, so the spend log carries
usage. The router relays keep the JSON body when the Content-Type carries a
charset, return the upstream status and body instead of a 500 when the deployment
rejects the call, and fall back to the caller's api-version when the deployment
sets none. Lint budgets ratcheted to the measured totals
2026-09-04 23:32:27 -07:00
Mateo Wang
44b1cc7b0f
Merge pull request #39589 from BerriAI/litellm_fix_v1_messages_midstream_timeout_failure_logging
fix(proxy): log mid-stream /v1/messages failures as failures with partial usage
2026-09-04 13:20:30 -07:00
mateo-berri
6c27754455 fix(anthropic): bill an uncostable partial pass-through stream at zero cost instead of dropping its usage 2026-09-04 09:26:51 -07:00
mateo-berri
2f3d1ca575 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_fix_v1_messages_midstream_timeout_failure_logging
# Conflicts:
#	litellm/litellm_core_utils/litellm_logging.py
2026-09-03 18:27:43 -07:00
devin-ai-integration[bot]
4a537e2c19
fix(proxy): emit SSE keepalives on queue, rag, azure passthrough, usage chat and policy enrich streams (#39273)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 18:23:37 -07:00
mateo-berri
a330bc98a6 test(vertex-passthrough): inject the forwarder into the short-route regression helper 2026-09-03 16:39:03 -07:00
mateo-berri
1125307b1a Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_lit6905_vertex_passthrough_default_location 2026-09-03 16:25:50 -07:00
mateo-berri
f15bdfb669 chore: merge litellm_internal_staging into the mid-stream failure logging branch
Keeps staging's response-id keying next to the pass-through failure-path helpers
and types the read-only raw_bytes parameters as Sequence[bytes] so the merged tree
stays inside the lint budgets
2026-09-03 15:51:51 -07:00
mateo-berri
57da95a77c fix(proxy): apply default_vertex_config location before building the Vertex passthrough base URL
Routes without /projects/<project>/locations/<location>/ built the upstream host from the URL's
still-empty location and 500ed even with default_vertex_config set. Build the base URL once after
the configured project and location are applied, drop the hook that re-derived it afterwards, and
answer 400 with a fix-it message when no location is available at all.

Resolves LIT-6905
2026-09-03 15:27:47 -07:00
mateo-berri
0f759c56f0 fix(proxy): bill partial usage on failed Vertex and Gemini pass-through streams 2026-09-03 15:00:08 -07:00
Mateo Wang
f5f2781c6f
Merge pull request #39543 from BerriAI/litellm_fix_guardrail_mapping_test_order_dependence
fix(guardrails): remove the module-global translation mapping that leaked between tests
2026-09-03 14:36:03 -07:00
Mateo Wang
8cf3bc87a2
Merge pull request #39597 from BerriAI/litellm_lit6642_vertex_count_tokens_anthropic_beta
fix(proxy): drop anthropic-beta on the Vertex passthrough count-tokens route
2026-09-03 13:13:11 -07:00
devin-ai-integration[bot]
e046aee3d5
fix(spend_tracking): add missing_session_id: omit to leave SpendLogs.session_id null without a client session (#39458)
* fix(spend_tracking): leave SpendLogs.session_id null when no client session id was established

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

* chore(lint): ratchet basedpyright budget after session_id fix

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

* fix(spend_tracking): ignore trace ids as session ids

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

* fix(spend_tracking): gate null SpendLogs.session_id behind missing_session_id: omit

Unset, generate and reject keep the legacy trace id fallback. omit records only
metadata.session_id, the key Langfuse reads, so a trace id copied into
litellm_session_id by get_litellm_params never becomes a session.

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

* fix(spend_tracking): stamp the omit decision on the request so a config reload cannot fabricate a session

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

* fix(spend_tracking): keep omit covering requests the pre-call stamp never reaches

Router-model provider pass-through calls allm_passthrough_route directly and
skips add_litellm_data_to_request, so those requests never run the pre-call
helper and carry no omit stamp. Reading only the stamp made POST
/anthropic/v1/messages write a fabricated uuid into SpendLogs.session_id under
missing_session_id: omit while its Langfuse trace had no session, the exact
divergence the policy exists to remove.

The stamp now only pins omit on, and an unstamped request falls back to the
configured policy, so a config reload still cannot fabricate a session for a
request that was decided pre-call.

* fix(spend_tracking): make the session-omission marker proxy-owned so clients cannot forge it

* fix(spend_tracking): strip the client-sent omission marker from both metadata buckets before they merge

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

* fix(spend_tracking): strip the session-omission marker from both metadata buckets

The pre-call policy ran before litellm_metadata is merged into metadata, so a
client that planted the marker in litellm_metadata had it copied back into the
route's own bucket after the strip and still got a null SpendLogs.session_id.

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-09-03 11:57:56 -07:00
mateo-berri
501f47ba2f fix(proxy): run the failure hook when a pass-through stream dies mid-body 2026-09-03 11:31:35 -07:00
mateo-berri
aaef5d219a fix(proxy): drop anthropic-beta on the Vertex passthrough count-tokens route 2026-09-03 11:07:29 -07:00