Commit graph

47377 commits

Author SHA1 Message Date
mateo-berri
f66663cc8b chore: merge litellm_internal_staging into fix/batch-retrieve-model-group 2026-09-06 02:36:39 -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
63ea197433 fix(router): keep batch retrieves out of routing strategy state
Stamping model_group on a batch retrieve routed the whole batch job's token usage
into the per-model-group counters that usage-based, latency-based, cost-based and
least-busy routing read, so polling a finished batch could exhaust a group's TPM or
RPM window and lock live chat traffic out with RouterRateLimitError. Polling also
drove the least-busy in-flight counts negative once per poll per deployment, which
pinned chat to whichever deployment had been polled most.

The strategy callbacks now skip batch retrieve call types, so a retrieve still lands
in spend logs under its model group while the numbers that pick a deployment for the
next chat request stay driven by live traffic only.
2026-09-06 01:02:53 -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: 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 / misc (push) Waiting to run
Unit Tests / proxy-auth (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-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 / 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
fix(anthropic_responses): preserve Responses refusal blocks in Anthropic messages translation
2026-09-06 00:03:06 -07:00
yujonglee
b9f5cd6036
ci: run unit tests on Python 3.12 (#39989) 2026-09-05 23:56:09 -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
ad2afe5e65 style(router): drop the inline comment on the batch retrieve stamp 2026-09-05 23:41:07 -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
58c3d04733 test: cover is_batch_retrieve_call_type in router batch utils 2026-09-05 23:24:47 -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-berri
3dea1ebb32 fix(cost-map): keep the prompt cache breakpoint flag on the foundry gpt-6-astra row
The openai gpt-6-astra card carries supports_prompt_cache_breakpoint, so a Foundry deployment
reported it as true until the azure_ai row took over the lookup. The cache control hook still
honours breakpoints for that deployment through the bare name, so /model/info was the only thing
that changed, and it now agrees with the hook again.
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
9acf09f60d fix(router): keep batch retrieves out of the per-minute tpm/rpm counters
Stamping model_group let both router deployment callbacks past their
`model_group is None` early return for batch retrieves. A batch reports the
whole job's token total on retrieve and reports it again on every poll of the
finished batch, so those tokens are not load in the current minute: three polls
of one completed 1,200 token batch pushed a tpm:1000 deployment to 3,600. The
fan-out also probed unrelated deployments, adding an rpm tick to each.
2026-09-05 23:07:55 -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
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
b21315fe75 refactor(anthropic): type the responses refusal stream iterator state
Types the cached sync upstream iterator instead of holding it as Any, so the
Responses to Anthropic streaming wrapper carries no untyped state.
2026-09-05 22:36:43 -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
01bdfb34aa chore: drop redundant comments in aretrieve_batch router tests 2026-09-05 22:24:20 -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
33d89c9814 style: drop redundant comments per repo comment policy 2026-09-05 20:55:11 -07:00
mateo-berri
128cb114bd style: trim comments on batch retrieve model group stamp 2026-09-05 20:45:47 -07:00
yuneng-jiang
11a0c0abf0
Merge pull request #39995 from BerriAI/litellm_mongodb_extra_docker_images
fix(docker): ship pymongo in the proxy images for the MongoDB vector store
2026-09-05 20:08:01 -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
mateo-berri
b067e836f8 fix(batches): claim the batch cost spend row in the database before charging
The cost callback used to look for an existing `<batch id>_batch_cost` row before charging a
completed batch, which left a window where concurrent retrieves on any instance all charged the
key, and it would honor a row any request had written under that id. The spend update writer now
inserts the batch cost row itself with `create_many(skip_duplicates=True)` and only the retrieve
whose insert lands charges the key, team, and user. An existing row only takes the charge when it
is a successful `aretrieve_batch` row, so a client-chosen `x-litellm-call-id` on another endpoint
cannot suppress billing. Batch cost rows no longer get their own immediate flush path

`batch_cost_is_final` now treats the proxy's normalized `complete` status like `completed`, which
the enterprise batch cost poller relies on when it decides whether a completed batch is safe to
retire. Tests build that status with `model_copy` since the OpenAI `Batch` model rejects it

The `test-quality-ok` markers sit on the `patch(` lines the gate keys on, and the logging tests no
longer wrap the priced retrieve in `contextlib.suppress`
2026-09-05 18:58:11 -07:00
mateo-berri
b2e93ba99f ci(e2e): declare the embedding model the access_control canary calls
The first canary run failed pass 1 because the stage-mirror config had no
openai-text-embedding-3-small while test_llm_api_routes_group_grants_every_llm_endpoint
calls /embeddings with it; the public log named the test, which is the
behavior the previous commit added
2026-09-05 18:58:07 -07:00
mateo-berri
a9e918577b ci(e2e): run the access_control canary on harness changes and name failed tests
A harness-only change (proxy_client.py, conftest.py, pytest.ini, the gateway
config, .github/e2e-stack, or the workflow) selected nothing, so the stack was
never exercised by the change that touched it. select_tests.py keeps the
changed-file rule and adds the access_control suite whenever a harness file
changes. The run step now reports the pytest exit code before the evidence
check, prints pytest's summary line per pass so the rerun count is visible,
and assert_tests_ran.py names each failed or errored test as classname::name
2026-09-05 18:46:51 -07:00
Yuneng Jiang
37e8b99a9f
fix(docker): ship pymongo in the proxy images for the MongoDB vector store
The MongoDB Atlas vector store provider imports pymongo lazily from the
opt-in `mongodb` extra, but none of the shipped images installed that
extra. Any image-based deployment that configured a MongoDB vector store
failed at search time with "requires the 'pymongo' package", which the
user cannot fix without extending the image

Adds `--extra mongodb` to every uv sync in the root Dockerfile,
Dockerfile.database, Dockerfile.non_root, and the gateway component
image. The backend component does not serve /vector_stores so it is left
as is. The extra resolves from the existing uv.lock to pymongo 4.17.0
plus dnspython 2.8.0, no lock change needed

(cherry picked from commit 16fd14f537)
2026-09-05 18:14:58 -07:00
ryan-crabbe-berri
09e9fd5f60
Merge pull request #39991 from BerriAI/litellm_remove_legacy_user_dashboard
refactor(ui): render the Virtual Keys page without the legacy user dashboard
2026-09-05 18:11:12 -07:00
mynkyu
df6990c712 test: move the batch model_group regression into test_router.py
CLAUDE.md asks bug fixes to extend the existing mapped test file rather than
add a new one, and tests/test_litellm/test_router.py already covers
Router.aretrieve_batch. Fold the two cases in next to that coverage and drop
the standalone file.

The helpers are prefixed so they read unambiguously in a shared file, and the
respx context stays open while the payload is awaited, since the usage
accounting reads the batch's output file from the success handler.

Signed-off-by: mynkyu <mynkyu@bucketplace.net>
2026-09-06 10:10:07 +09:00
mynkyu
e630f21d16 test: fake the provider at the HTTP boundary in the batch model_group test
The test-quality gate flagged the first version for patching an SDK internal
(litellm.batches.main.vertex_ai_batches_instance) and for writing
litellm.callbacks directly.

Drive an openai-compatible deployment through respx instead, so the retrieve
call and the usage accounting that reads the completed batch's output file both
run for real, and install the collector with monkeypatch so nothing leaks into
the next test.

Signed-off-by: mynkyu <mynkyu@bucketplace.net>
2026-09-06 10:04:38 +09:00
mynkyu
2286bf3eca fix(router): stamp model_group when retrieving a batch
Batch token usage is accounted on the retrieve call, not on create: a provider
only reports token counts once the job finishes, so the usage arrives on
aretrieve_batch and that is the spend log row the tokens land on.

Router.acreate_batch stamps the requested model group into its metadata, but
Router.aretrieve_batch never did. A batch is retrieved by id, so the request
carries no model, and the router fans the lookup out over its deployments -
leaving model_group unset on the one record that carries the tokens.
/global/activity/model groups the spend logs by model_group, so every batch's
tokens were bucketed under an empty group.

Stamp the model group inside the per-deployment retrieve attempt, preferring an
explicitly requested group and otherwise using the model_name of the deployment
that answered, which is unambiguous even when the request named no model. An
existing model_group in the metadata is left untouched, so nothing that already
resolves a group changes.

Scope is limited to aretrieve_batch: acompletion, aresponses and acreate_batch
logging are untouched, and cost/spend attribution by model is unchanged.

Signed-off-by: mynkyu <mynkyu@bucketplace.net>
2026-09-06 10:04:38 +09:00
tin-berri
60440ee4d3
feat(mcp): add opt-in per-server oauth relay discovery (#39936)
Resolves LIT-7074
2026-09-05 18:04:35 -07:00
moe-berri
91ae13d07d
Merge pull request #39955 from BerriAI/litellm_fix_adaptive_router_bandit_prior
fix(adaptive_router): add the persisted delta to the cold-start prior on load
2026-09-05 18:02:04 -07:00