Commit graph

15348 commits

Author SHA1 Message Date
Yujong Lee
5707bd420e fix(native): preserve global OCR enablement contract
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
2026-09-07 10:43:50 -07:00
Yujong Lee
29e810170a refactor(native): wrap endpoint execution in a shared harness 2026-09-07 10:43:50 -07:00
Yujong Lee
3c7bb5131e refactor(native): separate attempts from declarative dispatch 2026-09-07 10:43:50 -07:00
Yujong Lee
77bf16b847 test(native): cover shared dispatch contracts 2026-09-07 10:43:50 -07:00
Yujong Lee
c363987dad test(ocr): keep bridge config double compatible 2026-09-07 10:43:50 -07:00
Yujong Lee
6b3a9af0e3 refactor(native): run acceptance checks inside shared dispatch 2026-09-07 10:43:50 -07:00
Yujong Lee
f9a1fd42af refactor(ocr): dispatch complete operations through shared runtime 2026-09-07 10:43:50 -07:00
Yujong Lee
20de0ce755 refactor(native): share dispatch lifecycle across existing bridges 2026-09-07 10:43:50 -07:00
yujonglee
217cb12623
refactor(rust): remove per-request enablement arguments (#39928)
* refactor(rust): remove per-request enablement arguments

* fix(rust): remove ignored transcription enablement

* refactor(rust): remove OCR-specific bridge controls
2026-09-07 10:43:45 -07:00
mubashir1osmani
a1e7293fa9
fix(mcp): apply key and team guardrails to MCP tool calls (#39629)
* fix(mcp): apply key and team guardrails to MCP tool calls

Guardrails attached to a virtual key or team were only enforced on LLM
routes. The synthetic request built for MCP tool call guardrail hooks
carried no guardrails in its metadata, so a guardrail with default_on
false never ran on tools/call even when the key explicitly listed it.
Resolve key, team, and project guardrails onto the synthetic request
with the same helper the chat path uses.

* fix(mcp): pass project metadata through without a mutable default

* fix(mcp): mark the request dict parameter mutable-ok with a reason

* test(mcp): explain the premium_user patch and tighten the helper docstring
2026-09-07 16:45:16 +00:00
Mateo Wang
1c7b13bdbf
Merge pull request #38755 from BerriAI/litellm_mistral_voxtral_tts_speech
feat(mistral): add text-to-speech support for /v1/audio/speech
2026-09-07 09:38:57 -07:00
Mateo Wang
50f54b9c7e
Merge pull request #40014 from BerriAI/litellm_lit_7036_retry_policy_400s
fix(router): skip the refusing deployment when retrying a non-transient error
2026-09-07 09:38:43 -07:00
devin-ai-integration[bot]
168a0055a2
chore(lint): stop ratcheting *-budget.json on PR branches (#39937)
Some checks failed
Unit Tests / Vertex AI (push) Has been cancelled
Unit Tests / proxy-auth (push) Has been cancelled
Unit Tests / proxy-endpoints (push) Has been cancelled
Unit Tests / proxy-extras (push) Has been cancelled
Unit Tests / proxy-infra (push) Has been cancelled
Unit Tests / proxy-server (push) Has been cancelled
Unit Tests / responses-caching-types (push) Has been cancelled
Postgres Tests / proxy-security (push) Has been cancelled
Postgres Tests / schema-migration (push) Has been cancelled
Postgres Tests / proxy-behavior (push) Has been cancelled
Unit Tests: Documentation Validation / documentation (push) Has been cancelled
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests / caching-local (push) Has been cancelled
Unit Tests / core-utils (push) Has been cancelled
Unit Tests / enterprise-package (push) Has been cancelled
Unit Tests / enterprise-routing (push) Has been cancelled
Unit Tests / integrations (push) Has been cancelled
Unit Tests / All Other Providers (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
2026-09-06 12:44:19 -07:00
mateo-berri
5825cc7593 chore: merge litellm_internal_staging into litellm_mistral_voxtral_tts_speech
Some checks failed
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
LiteLLM Rust / release wheel (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
2026-09-06 03:24:55 -07:00
Mateo Wang
02522a5441
Merge pull request #39983 from BerriAI/litellm_lit_7081_azure_ai_gpt_6_astra_pricing
feat(cost-map): add azure_ai/gpt-6-astra Foundry pricing
2026-09-06 01:27:22 -07:00
Mateo Wang
b09b7d3eb8
Merge pull request #39980 from BerriAI/litellm_lit_7048_batch_cost_row_once
fix(batches): account a batch's cost once, from the first retrieve that sees it final
2026-09-06 01:27:10 -07:00
mateo-berri
a72041b757 test(router): pin the retry skip list across attempts in a model group 2026-09-06 01:03:32 -07:00
mateo-berri
d664ca139e chore(router): suppress the retry-skip kwargs writes and correct the filter docstring
The two writes that hand the skip list to the next attempt now carry a
`# rebind-ok` reason, which is the sanctioned escape hatch for an unavoidable
parameter mutation and matches how `log_retry` already writes into the same
kwargs dict a few lines above

`get_excluded_filtered_deployments`'s docstring said returning the unfiltered
list would re-include the deployment that just failed. The retry skip does
exactly that on purpose, so the docstring now says each caller decides what an
empty result means

The reliability registry cell the new e2e test claims is marked
`fail_before_fix: proven`: the same config returns 400 at the merge base and
200 off a sibling deployment at the tip
2026-09-06 00:53:24 -07:00
mateo-berri
0fcf0fe06c test(router): cover the retry skip-list narrowing helper
The router code coverage gate reads every function defined in router.py
and fails when no test file names it. _as_retry_skipped_deployment_ids
was only reached indirectly through the retry path, so the gate went red
on this PR's tip.

Test it directly instead: a tuple of strings survives, non-string items
inside the tuple are dropped, and every other shape a caller could send
narrows to an empty skip list.
2026-09-06 00:06:35 -07:00
mateo-berri
defd8661f4 refactor(spend): stop queueing a batch's claim row for a writer the proxy never builds
SPEND_LOGS_URL only diverts spend logs when db_writer_client is set, and nothing in the proxy ever assigns that global, so the queued copy was only ever skipped as a duplicate by the local insert.
2026-09-06 00:04:24 -07:00
Mateo Wang
4104868458
Merge pull request #39723 from Atharva-Kanherkar/fix/anthropic-responses-refusal-translation
Some checks failed
Unit Tests / misc (push) Waiting to run
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests / caching-local (push) Waiting to run
Unit Tests / core-utils (push) Waiting to run
Unit Tests / enterprise-package (push) Waiting to run
Unit Tests / enterprise-routing (push) Waiting to run
Unit Tests / integrations (push) Waiting to run
Unit Tests / All Other Providers (push) Waiting to run
Unit Tests / Vertex AI (push) Waiting to run
Unit Tests / proxy-endpoints (push) Waiting to run
Unit Tests / proxy-extras (push) Waiting to run
Unit Tests / proxy-infra (push) Waiting to run
Unit Tests / proxy-auth (push) Waiting to run
Unit Tests / proxy-server (push) Waiting to run
Unit Tests / responses-caching-types (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
LiteLLM Rust / release wheel (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
fix(anthropic_responses): preserve Responses refusal blocks in Anthropic messages translation
2026-09-06 00:03:06 -07:00
mateo-berri
6866eac96f fix(router): ignore a retry skip list the caller sent itself
The retry skip travels as a request kwarg, and the router forwards keys it
does not recognize, so a client can put _retry_skipped_deployment_ids in its
own request body. The value went straight into a pydantic TypeAdapter and
then into a set(), so an int or an object raised TypeError and a string, a
list, or a dict raised a ValidationError, each of them replacing the 400 the
provider had actually returned.

Every read now goes through one narrowing function that keeps a tuple of
strings and skips nothing otherwise, so a forged value costs the caller
nothing beyond the retry landing on the same deployment again.
2026-09-05 23:53:36 -07:00
mateo-berri
fcb6d2267c fix(spend): keep a batch's claim row out of the logs a proxy was told not to write
disable_spend_logs has to keep meaning that no request gets logged, and the row
that makes a batch chargeable exactly once is the one row it cannot drop, so with
logging off that row now carries only what tells the retrieves apart. SPEND_LOGS_URL
deployments get their copy back too: the claim writes straight to this table, so the
row is queued as well when an external writer is the one that takes the spend logs.
2026-09-05 23:51:44 -07:00
mateo-berri
05cba21763 fix(anthropic): split refusal off a combined finish_reason chunk
A fake-streamed provider hands the adapter one chunk carrying both the
delta payload and the finish_reason, which is exactly what the combined
chunk splitter exists for, but its content check never listed the refusal.
The translation short-circuits on finish_reason, so that refusal text was
dropped and the client got `stop_reason: refusal` over an empty content
array, the symptom this PR set out to fix.

Both refusal accumulators also drop their `mutable-ok` lists for a plain
string attribute
2026-09-05 23:40:20 -07:00
mateo-berri
ecf7e4e766 fix(router): keep the provider's error when the retry skip empties the group
Before excluding the deployment that just refused, the retry-skip guard asked
whether another one could still answer. It asked by re-running a single routing
filter, the order filter, while deployment selection also applies cooldowns, the
context-window pre-call check, tag routing, and routing plugins.

Any filter the guard did not replicate made it answer yes while the real pick was
left with nothing. A group narrowed to one deployment by tag routing turned the
provider's own 400 into a no-deployments 429.

The skip now runs where every filter has already been applied, and it keeps the
deployments untouched when skipping would leave none. The caller gets the
provider's error either way, and a group with one eligible deployment retries in
place as it did before.
2026-09-05 23:36:28 -07:00
mateo-berri
c09d34fc4b fix(anthropic): stream refusals parked in provider_specific_fields
The first-delta guard read `delta.refusal` directly, while the translation
three lines later goes through `openai_chat_refusal_text`, which also reads
the `provider_specific_fields` LiteLLM parks unrecognized fields in. A
provider that sends the refusal that way had its only refusal delta skipped
as blank, so the client got `stop_reason: refusal` over an empty content
array, which is the symptom this PR set out to fix
2026-09-05 23:20:19 -07:00
mateo-berri
fffe0bb0dc test(azure_ai): pin the tier the messages bridge sends when astra refuses max
The /v1/messages adapter lowers a tier the entry does not accept, so dropping max from the astra
rows moves that path from Foundry's 400 to a request at xhigh. Nothing pinned that, and the guard
test's docstring named gpt-6-astra as the only gpt-5 name with an azure_ai row, which 11 rows
contradict.
2026-09-05 23:18:25 -07:00
Mateo Wang
0318b4acdc
Merge pull request #30856 from emerzon/litellm_vertex_lyria_models
feat(vertex): add Lyria model support
2026-09-05 23:12:25 -07:00
mateo-berri
a2b21b323a Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_7048_batch_cost_row_once 2026-09-05 23:03:41 -07:00
mateo-berri
cb1ec76e46 fix(router): keep retry skips on the active order-fallback target
The retry-skip guard checks that some other deployment could still answer
before it excludes the one that just refused, so a single-deployment group
keeps the old retry-in-place behavior. It asked that question at the group's
minimum order, but the router picks the retry's deployment at the order the
request has already escalated to.

So a group with a primary at order 1 and a backup at order 2 answered "yes,
order 1 still has a candidate" while the retry was pinned to order 2, and the
exclusion left order 2 with nothing. The caller got a no-deployments error in
place of the provider's own 400.

The helper now takes the active target order and filters by it, which is the
same value async_get_healthy_deployments reads off the request.
2026-09-05 23:02:44 -07:00
mateo-berri
11e45ad953 fix(vertex_ai): mark the Lyria 3 catalog entries text-only
`vertex_ai/lyria-3-clip-preview` and `vertex_ai/lyria-3-pro-preview` were
registered with `supports_vision`, `supports_image_input`, and an `image`
modality, which contradicts their `gemini/lyria-3-*` siblings and makes
/model/info advertise image input on text-to-music models.
2026-09-05 23:00:01 -07:00
mateo-berri
02b44820c4 test(vertex_ai): keep imagen predict passthrough off the Lyria audio path
The new Lyria passthrough branch runs before the image-generation branch
and keys on the same `predictions[0].bytesBase64Encoded` shape imagen
returns, so only the cost-map lookup separates them. Cover an imagen
predict response end to end so a future change that drops that lookup
fails here instead of misbilling images as audio.
2026-09-05 22:59:57 -07:00
mateo-berri
24f0be8021 fix(spend): leave a batch uncharged when the database refuses the takeover
The takeover of a $0 row an older proxy left behind used to charge the batch when
the update could not reach the database. That leaves the row still reading $0, so
every later retrieve finds the same row and charges the batch again, which is the
repeat charging this PR exists to stop. The retrieve that does take the row over
is the one that charges, and a batch nobody retrieves again after that failure is
never charged, the same as one whose proxy died inside the write window.
2026-09-05 22:47:41 -07:00
mateo-berri
7c7810df42 fix(router): ignore non-integer status codes when picking retry skips
CI's router_code_coverage gate wants every function in router.py called by
name from a test file with "router" in its name, and the new helper had no
direct caller, so the check-quality job failed on the first tip.

Covering it directly also turned up a hole. litellm._should_retry compares
the status code to 500, so a provider exception carrying a string status code
raises TypeError instead of answering. should_retry_this_error has the same
call, but the retry policy path skips it, which is exactly the path this
change enables, so the helper was the first to touch that value. Narrowing to
int leaves those exceptions on the old retry-in-place behavior.
2026-09-05 22:43:15 -07:00
mateo-berri
6be78fa850 fix(vertex_ai): bill Lyria per generation, not per audio second
Google prices Lyria per generated clip, so every Vertex Lyria entry in the
price map now carries a single output_cost_per_image and both the speech
and the passthrough cost paths read that one field. The old
output_cost_per_second and audio_seconds_per_prediction pair assumed a
30 second clip, which does not match the 32.768 second WAV Vertex returns,
and no other model in the map priced audio that way

Drops max_audio_length_hours and max_audio_per_prompt from the price map,
its schema, the generator, and ModelInfo, since nothing reads them, and
drops the audio_mime_type hidden param for the same reason: the response
already carries the resolved content type on its own header

Folds the per-model bundled catalog lookups into one cached parse of the
local cost map, validated with a TypeAdapter over a ReadOnly TypedDict
2026-09-05 22:34:31 -07:00
mateo-berri
0fb3951b2c fix(spend): charge a batch once when an older proxy left its cost row at $0
A proxy without this fix wrote the batch's cost row on every poll while the batch
was still running, so that row reads $0 and the insert that claims the charge has
nowhere to land. The retrieve that charges the batch now writes its own payload
over that row under a where clause that still names spend 0.0, so exactly one
retrieve takes it over and every later one reads the charge and charges nothing
2026-09-05 22:32:06 -07:00
mateo-berri
fa2b64878b fix(azure_ai): redirect a gpt-5 capability lookup only when the map has a foundry row
gpt-6-astra is the only gpt-5-family name with an azure_ai row. Prefixing the rest
cost them every effort flag, since get_llm_provider sends an azure_ai name down the
azure provider when a global AZURE_AI_API_BASE points at an openai.azure.com host and
azure/<model> is not a key either, which turned temperature, top_p and logprobs on
azure_ai/gpt-5.1-chat-latest from accepted into an UnsupportedParamsError.
2026-09-05 22:31:33 -07:00
mateo-berri
e79f3ec520 fix(cost-map): stop advertising reasoning_effort max on the azure gpt-6-astra rows
Both Azure routes refuse it. A live call to the same deployment through
openai/deployments/gpt-6-astra/chat/completions on api-version 2025-04-01-preview
answers reasoning_effort max with a 400 unsupported_value naming none, low, medium,
high and xhigh as the values it takes, and xhigh returns 200, so azure/gpt-6-astra
and azure/us/gpt-6-astra now match the azure_ai row.
2026-09-05 22:31:32 -07:00
mateo-berri
2e2fce5e58 fix(router): skip the refusing deployment when retrying a non-transient error
BadRequestErrorRetries and ContentPolicyViolationErrorRetries did let a retry
happen, but the retry re-picked the deployment that had just refused, since a
400 never puts a deployment in cooldown. On a weighted model group the caller
got the same 400 back after every configured retry, and the existing 401/403
"retry on another deployment" rule broke the same way

A retry after a non-transient status now carries the deployments that already
answered this request in the per-request exclusion list weighted failover
already honors, so the next attempt lands on a sibling. Single-deployment
groups still retry in place, and 408/429/5xx retries are untouched

Adds live e2e coverage for reliability.retry.context_window.succeeds_within_retries
and renames the two litellm.utils deployment filters that are now called from
outside the module
2026-09-05 22:25:13 -07:00
Emerson Gomes
fd24cce2c3
test(vertex): isolate Lyria fallback from the remote catalog 2026-09-06 00:15:08 -05:00
Emerson Gomes
82edb9e901
fix(vertex): preserve Lyria pricing fallback and audio MIME 2026-09-06 00:09:27 -05:00
mateo-berri
061c25b5ca fix(spend): let a batch's charge survive an older proxy's $0 poll row
A proxy running the old code wrote <batch id>_batch_cost at $0 every time it polled a batch that was still running, so after an upgrade the claim found that row and read it as proof the batch had already been charged. Only a row that recorded a charge counts now, which leaves those $0 rows, and any row a client planted under the batch id, to be charged over

disable_spend_logs skipped the claim entirely, so under that setting every retrieve of a finished batch charged again. The claim now runs either way and writes the one row per batch that makes the charge exactly once, while the per-request logs stay off
2026-09-05 21:25:03 -07:00
yuneng-jiang
2b3a82d223
Merge pull request #39416 from BerriAI/litellm_/e2e-test-performance-7d53be
ci(e2e): run a PR's changed e2e tests three times behind a human-approved environment
2026-09-05 21:13:57 -07:00
Mateo Wang
54af2ec411
Merge pull request #39970 from BerriAI/litellm_fix_latency_routing_empty_latency_list
fix(router): treat a routing entry with no latency samples as zero latency
2026-09-05 21:07:36 -07:00
mateo-berri
116f88b023 fix(e2e-changed): keep the gate off suites the stack cannot run
The selector picked up two suites that can never pass in this stack, so
editing either one turned the check permanently red: the presidio masking
suite calls pytest.fail without an analyzer and anonymizer that up.sh
never starts, and the pipecat audio suite skips itself at import time
unless the NLTK punkt_tab data is present, which nothing installs.

tests/e2e/coverage_registry/test_collector.py had the same problem for a
different reason. Its nested pytest.main autoloads pytest-retry from the
ci group the workflow installs and dies with "INTERNALERROR: no option
named 'filtered_exceptions'", so the collect-only pass now disables that
plugin. The plugin's entry point is pytest-retry, not retry, so the same
one-word fix lands on mutmut's pytest_add_cli_args, where "-p no:retry"
was disabling nothing.

Two smaller holes in the harness: a canary argument the shell never
expanded used to select nothing and let the gate pass green, and a secret
that cannot be represented in both bash and dotenv was rejected without
naming the key.
2026-09-05 21:03:50 -07:00
mateo-berri
79d47788d9 fix(anthropic): stream the refusal text on bridged /v1/messages calls
Both bridges opened an empty text block on a refused streaming turn and
closed it without a single delta, so a client replaying that assistant
turn got HTTP 400 "text content blocks must be non-empty" from Anthropic.
The safeguard-refusal fallback that motivated withholding the text only
runs on the awaited non-streaming response, so nothing needed it withheld

Move the refusal readers into the shared messages/utils helpers so the
adapters stop reaching into each other's private statics, which is also
what put reportPrivateUsage over its budget
2026-09-05 20:56:39 -07:00
mateo-berri
e8f311429e fix(cost-map): stop advertising reasoning_effort max on azure_ai/gpt-6-astra
Foundry rejects reasoning_effort max on the gpt-6-astra deployment with a 400 that
names none, low, medium, high, and xhigh as the supported values, so the card no
longer lists max. The request path never gated max (only xhigh is opt-in), so this
only changes /model_group/info and router capability gating. The azure/ twin stays
as is because it was not verified on an Azure OpenAI host
2026-09-05 19:42:15 -07:00
tin-berri
9fd60e4f95
feat(router): gate heuristic v1 tuning (#39952) 2026-09-05 19:24:00 -07:00
mateo-berri
a17fcecf70 refactor(azure_ai): type the Foundry param mapping override and drop test docstrings
The AzureAIStudioConfig.map_openai_params override now carries dict[str, object]
annotations instead of bare dict, and the docstrings added to the new tests go away
since the test names already say what they cover. No behavior change
2026-09-05 19:21:34 -07:00
mateo-berri
15372967c6 fix(azure_ai): read the azure_ai card for gpt-5 series reasoning effort gates
Foundry deployments of gpt-6-astra reached through azure_ai used the bare OpenAI card
for the reasoning_effort none gates, so temperature and top_p were refused while the
azure_ai card says none is supported. AzureAIStudioConfig now dispatches gpt-5 series
params through AzureAIGPT5Config, which looks capabilities up under the azure_ai/
prefix the way the azure route does

Also carries the search_context_cost_per_query block azure/gpt-6-astra has, adds a
flex service tier cost test that fails at the merge base, and keeps the wildcard test
from stripping azure_ai/gpt-6-astra out of the provider set
2026-09-05 19:06:38 -07:00