Commit graph

17801 commits

Author SHA1 Message Date
kerry
d68e20e586 Merge remote-tracking branch 'origin/litellm_e2e_cost_calculation_scripted_provider' into litellm_e2e_cost_calculation_scripted_provider
Some checks are pending
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
2026-09-18 18:05:20 +00:00
kerry
e87830feba Merge remote-tracking branch 'origin/main' into litellm_e2e_cost_calculation_scripted_provider 2026-09-18 17:56:49 +00:00
kerry-berri
fd58c31cc7
Merge pull request #41832 from BerriAI/litellm_lit_8111_cache_read_missing_rate
fix(cost): bill cache-read tokens at the input rate when the map has no cache-read rate
2026-09-18 10:56:25 -07:00
yuneng-jiang
c4ab1d98e9
Merge pull request #41779 from BerriAI/litellm_settings_store_precedence
refactor(proxy): make the config file win over the database
2026-09-18 09:52:09 -07:00
kerry
af61d9247c test(cost_tracking): expect cache reads without a map rate to estimate at the input rate
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:33:17 +00:00
kerry
88d1eb3b35 fix(cost): bill cache-read tokens at the input rate when the map has no cache-read rate
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:22:14 +00:00
Mateo Wang
861f79797f
Merge pull request #41663 from BerriAI/litellm_remove_legacy_interactions_schema_flag
refactor(interactions): remove expired use_legacy_interactions_schema shim
2026-09-18 08:48:30 -07:00
ryan-crabbe-berri
4f70b88a1f
Merge pull request #41707 from BerriAI/litellm_jwt_mapping_cache_evict_on_bulk_key_delete
fix(proxy): evict jwt key mapping cache on user, team, org, and bulk key deletion
2026-09-18 08:41:31 -07:00
kerry
5f3a86aee5 test(e2e): use TypeAlias over 3.12 type statements in e2e models
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 13:27:14 +00:00
kerry
dda7776346 test(e2e): make cost-calculation cases MECE by rate-key ownership with realistic fixtures
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 13:15:29 +00:00
Yuneng Jiang
0d9c5159df
test(e2e): assert config ownership instead of absence from the stored row
/config/field/info used to answer from the LiteLLM_Config row, so "the field
400s" proved the row did not carry it. It now answers from the resolved
settings, and the CI stack config declares general_settings.max_parallel_requests,
so the endpoint returns that value and the old assertion could never hold.

The check that /add/allowed_ip writes only what the caller changed moves to
/config/list, which still reports stored_in_db off the row, and the field/info
call now asserts the ownership the endpoint reports: the config file owns the
key, so it reads back as source=config and editable=false.

Verified against a live proxy on an isolated Postgres rather than in CI, where
this check has never run: it waits on protected-environment approval.
2026-09-18 02:53:37 -07:00
Yuneng Jiang
2f3e4fa724
test(proxy): pass the new pass-through argument to the side-effect dispatcher
_apply_general_settings_side_effects grew a fourth argument when the reload
started comparing the resolved pass-through list, and this dispatch test calls
it positionally, so it failed with a TypeError.
2026-09-18 02:17:52 -07:00
Yuneng Jiang
460f336d8a
fix(proxy): rebuild pass-through routes whenever the resolved list changes
The reload only re-registered pass-through endpoints when the stored row
still carried the key, so deleting the row left the deleted routes serving
traffic until the process restarted.

It now compares the resolved list before and after the row is applied and
rebuilds on any difference, including a deletion that resolves back to the
config file's list or to nothing.

This matches what _apply_retention_settings already does with the retention
values, so the two reload effects no longer disagree about what counts as a
change.

The tests assert the proxy's registry of live pass-through routes, which is
what decides whether a request is routed upstream or falls through to the
auth error, rather than that the registration helper was called.
2026-09-18 02:05:09 -07:00
Yuneng Jiang
23ee8ad1c3
fix(proxy): close the last three surfaces that bypassed config ownership
The two dashboard toggles under litellm_settings wrote through save_config,
so the refusal applied, but they mutated the litellm module global first: a
refused write still took effect in the running process until the next reload.
Both now check before they mutate.

/config/field/delete drops the stored key without touching the store, so a
deleted key kept reading back from the process. It now refreshes the store
like the other write paths.

/config/list reported source and editable for the general_settings rows but
not for the litellm_settings ones, so the dashboard would have shown a
config-declared toggle as editable.
2026-09-18 01:53:54 -07:00
Yuneng Jiang
8e67a33fc3
fix(proxy): refuse config-owned writes at one choke point and refresh the store
Both write paths now go through the same refusal, so /config/field/update and
/config/update answer identically instead of each phrasing its own rule.

A successful write now applies to the SettingsStore, so the next read sees it.
Without this, /config/field/info reported a key the dashboard had just stored
as "not set" until the process reloaded from the database.

resolve() no longer takes a KeyRule it never reads; the store picks the row.
The matrix tests resolve through SettingsStore instead of calling resolve
directly, so the section and key in each case actually route a lookup.

ConfigFieldInfo and ConfigList type `source` as the FieldSource literal, and
the dashboard API types are regenerated for the two new fields.
2026-09-18 01:49:30 -07:00
Yuneng Jiang
afa4a6fe78
refactor(proxy): make the config file win over the database
The precedence used to vary per key: some keys let a stored row win, some
let the file win, some merged the two. That meant an operator could not
answer "which value is live?" without knowing the key.

Now file presence decides ownership. A key the config file declares is
config-owned, whatever the database holds, and a key the file omits falls
back to the stored row. KeyRule no longer carries a RuleKind, only which
row the stored value lives in.

Writes to a config-owned key are refused at the two surfaces that reach
the database instead of being stored and silently ignored: save_config
and /config/field/update both 400 naming the key and the config file path.

Both read endpoints now report source and editable off the same
SettingsStore, so /config/field/info and /config/list can no longer
disagree inside one process.

Replaces the 786-case checked-in JSON fixture with cases generated from
the rule table, so the matrix tests no longer assert that resolve() agrees
with a snapshot of resolve().

BREAKING CHANGE: a dashboard or /config/field/update write to a setting
the config file declares now returns 400 instead of being stored. Remove
the key from the config file to let the database own it.
2026-09-18 01:28:21 -07:00
Yuneng Jiang
a7d4f7c521
Merge remote-tracking branch 'origin/main' into litellm_settings_store_precedence 2026-09-18 00:51:38 -07:00
yucheng-berri
8fc9c46d1a
Merge pull request #41541 from BerriAI/litellm_prompt_injection_async_llm_check
fix(proxy): run prompt injection heuristics off the event loop
2026-09-18 00:40:25 -07:00
Yuneng Jiang
0524745510
fix(proxy): preserve config pass-through and retention reloads 2026-09-18 00:23:24 -07:00
Yuneng Jiang
d1cd869012
refactor(proxy): resolve config and DB settings precedence in one SettingsStore 2026-09-17 23:36:27 -07:00
Yuneng Jiang
1be40e304d
refactor(proxy): add settings precedence primitives 2026-09-17 23:35:38 -07:00
kerry
aac1456e07 refactor(e2e): inline literal expected costs into cases.json
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 06:11:00 +00:00
yuneng-jiang
fc49ad7042
Merge pull request #41748 from BerriAI/litellm_wt_0917_7e12
fix(proxy): persist only the keys a caller changed in save_config
2026-09-17 22:57:06 -07:00
kerry-berri
68c4c82ac9
Merge pull request #41767 from BerriAI/litellm_bedrock_titan_batch_usage
fix(batches): bill Bedrock Titan embedding batch lines from inputTextTokenCount
2026-09-17 22:49:52 -07:00
yuneng-jiang
ea95631181
Merge pull request #41756 from BerriAI/litellm_/regression-analysis-deep-dive-dd13e8
test(e2e/ui): wait for the filtered budget list before clicking a row action
2026-09-17 22:40:20 -07:00
kerry
0247e9b634 fix(batches): bill Titan binary embedding batch lines that only carry embeddingsByType
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 05:32:25 +00:00
kerry
e50fc8ba75 fix(batches): bill Bedrock Titan embedding batch lines from inputTextTokenCount
Titan embedding batch output carries the token count as a top-level inputTextTokenCount with no usage block, so the Bedrock batch cost parser recorded 0 tokens and 0 spend for every Titan embedding batch. Parse that field for embedding lines only and leave Converse and Anthropic shaped lines on their existing paths

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 05:25:52 +00:00
kerry-berri
e8d30efe99
Merge pull request #41763 from BerriAI/litellm_drop_cost_map_pinning_tests 2026-09-17 22:11:32 -07:00
kerry
bb768573cf test: restore synthetic behavior tests dropped as catalog pins
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 05:00:04 +00:00
kerry
eff323682e test: drop the fireworks vision flag pin that reads the shipped cost map
get_provider_info is a passthrough over the cost map entry, so asserting supports_vision on named fireworks models pins a vendor capability rather than litellm behavior

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 04:52:58 +00:00
kerry
7975987107 test: keep behavior tests that read the cost map for a later fixture rewrite
Fifty six of the deleted tests turn out to assert the output of litellm code rather than the catalog lookup itself, things like map_openai_params, get_supported_openai_params, should_fake_stream, transform_request bodies, cost_per_token arithmetic, get_llm_provider routing, and provider config dispatch. They only happen to read shipped entries as inputs, so they belong in the later rewrite that injects a local model_cost, not in this deletion

Each one is restored verbatim from origin/main along with the fixtures, helpers, constants and imports it needs, and tests/test_litellm/test_sambanova_model_metadata.py is restored wholesale

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 04:52:06 +00:00
Yuneng Jiang
593fa5921a
test(e2e/ui): wait for the filtered budget list before clicking a row action
All three budget specs searched by typing into the search box and moved on
immediately. The search is debounced 300ms, and while the filtered query is in
flight react-query serves the previous page as placeholder data, which the list
hook reports as isLoading, which makes the table swap its whole body for
skeleton rows.

So the row assertion passed against the pre-search rows, and roughly 300ms later
the skeleton swap unmounted the row the spec had just opened the action menu on.
Playwright logged "element is not stable" twice and then "element was detached
from the DOM", and since the menu never reopened the click burned the full 15s
action timeout on all three attempts. Losing that race was pure timing: build
386 and build 387 of the UI suite ran the same commit 4b368bf066, and 386
passed where 387 failed on this spec plus "Delete a budget"

searchForBudget now waits for the GET that carries q=<budget id>, matching what
projectDetachment.spec.ts already does for a key search. That also gives the row
assertion something real to assert, since until now it could pass without the
search having filtered anything
2026-09-17 21:39:22 -07:00
Yujong Lee
1d88ca1cd2 test(ocr): restore public-boundary OCR coverage the Rust move cannot replace
The Python/Rust parity cases behind the ocr_backend fixture are back as they were on main:
the malformed-document matrix, Azure invalid options, native format for every provider and
the unknown Reducto model. They are the only check that the Python opt-out path and the
native path agree

test_native_failures_raise_the_public_exception_class drives every native failure kind
through litellm.ocr and litellm.aocr and pins the exception class callers catch. That class
is chosen in Python by route_host.map_failure, so no Rust test can cover it; bypassing the
mapping fails all 26 cases. The nested document edit and metadata failure tests run sync
again, since the sync path skips deployment hooks and dispatches success on the executor

legacy_callbacks.callbacks_needed now takes a Literal phase and ends its match with
assert_never, and setup imports from litellm.utils instead of mixing import styles
2026-09-17 21:36:48 -07:00
kerry
d2ac51893b test: keep the pinning-test removal free of unrelated reformatting
Regenerated every touched file from origin/main applying only the B1 test deletions and the unused import and helper cleanup they leave behind, without running the formatter across untouched code. CI only checks ruff format under litellm/, so the earlier reflows of test files were pure diff noise for reviewers

Also drops the tests/local_testing/test_prompt_caching.py entry from the caching-local shard in test-unit.yml since that file is deleted

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 04:27:28 +00:00
Yujong Lee
63d994ade4 refactor(rust): run OCR through a route-neutral callback contract and a legacy Logging adapter
Extracted from #41733 without the router loop, the cache machine layer, streaming, or the
error, timeout and route-pruning work that moved to #41745

litellm-callbacks holds the contract a native call and its host share: Machine, HostOp,
CallEvent, the in-process run loop, and Passthrough, which is built only by comparing the
caller's inputs with the body the route sends, so a route can never mark a key it rewrote.
litellm-host-python (formerly python-interop) owns the CPython driver and the Execution
handle, and litellm-callbacks-legacy is the @client wrapper as the native call sees it:
function_setup, the deployment hooks, pre_call and post_call, the success and failure fan-out
and the deferred proxy release. OCR is the one route on it, and the old core and bridge
lifecycles are gone

The passthrough rule is the structural fix for the bug #41719 patched in core and #41716
reworks: an inlined remote document no longer counts as the caller's value, so the legacy
adapter never hands the caller's URL back into the body. core/tests/ocr/passthrough.rs pins
it for every route and document source, including that unchanged values stay passthrough,
and callbacks-legacy/tests/payload.rs pins the adapter side with a real pre_call callback

Python OCR integration tests that only exercised core behavior now live as Rust tests, so
tests/test_litellm_rust keeps the cases that need the full Python stack
2026-09-17 21:13:16 -07:00
Yuneng Jiang
77a5e2cb64
test(proxy): isolate environment variable encryption 2026-09-17 21:07:02 -07:00
kerry
8ecbf3dbc1 test: drop tests that pin provider-owned cost map values
The repo rule is that a test must only fail when litellm code changes, never when a vendor updates a price, renames a field, or drops a model. These tests asserted shipped catalog entries directly, comparing lookup results to literals copied from model_prices_and_context_window.json or requiring named entries to exist or be absent, so every cost map sync could break them without any litellm code changing

Tests that exercise real litellm behavior with an injected local model_cost, invariants like backup parity, and assertions on non-lookup code paths are untouched

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 03:55:51 +00:00
Yuneng Jiang
463ece762a
fix(proxy): preserve opted-in environment variable saves 2026-09-17 20:53:21 -07:00
Yuneng Jiang
af17691389
fix(proxy): persist only the keys a caller changed in save_config 2026-09-17 20:32:04 -07:00
kerry-berri
db37977307
Merge pull request #41736 from BerriAI/litellm_mistral_cache_read_pricing
fix(model_prices): add cache-read pricing to Mistral chat models missing it
2026-09-17 19:35:26 -07:00
kerry
1bc4509bf0 test(model_prices): type the Mistral cache-read helpers and check the backup registry too
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:22:06 +00:00
kerry
6933ca2337 fix(model_prices): add cache-read pricing to Mistral chat models missing it
Mistral bills cached prompt tokens at 10% of the input price for every model, but twelve
active mistral/ chat rows had no cache_read_input_token_cost, so the cost calculator billed
their cache hits at zero. Adds the derived rate to those rows in both registry copies and a
registry invariant test that fails when an active priced Mistral chat row drops the field or
drifts from the 10% ratio

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:13:44 +00:00
kerry
38bc855235 test(cost_map): stop pinning supports_reasoning absent on the openrouter o1 entry
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 02:09:04 +00:00
yuneng-jiang
34718f0da6
Merge pull request #41723 from BerriAI/litellm_/circleci-specific-sha-0cf414
fix(proxy): forward every method on the typesafe pass-through route
2026-09-17 18:57:06 -07:00
Yuneng Jiang
471eff8421
test(logging): add azure_spillover to the GCS pub/sub spend-log golden
#41569 made SpendLogsMetadata always carry azure_spillover, null unless
Azure reported a spillover, and updated the unit tests that run on the PR
gate. The GCS pub/sub golden only runs on CircleCI's logging lane, so it
kept the old key set and test_async_gcs_pub_sub_v1 has failed on every
run since that merge with an extra metadata.azure_spillover key
2026-09-17 18:33:42 -07: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
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
c25c098bc1
Merge pull request #41469 from BerriAI/litellm_bedrock_mantle_responses_drop_top_p
fix(responses): drop top_p for gpt-5 reasoning models when drop_params is set
2026-09-17 18:04:19 -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