Commit graph

46529 commits

Author SHA1 Message Date
mateo-berri
b97d5e77eb fix(model_prices): raise bedrock_mantle gpt-5.5 and gpt-5.4 max_input_tokens to Mantle's enforced 1050000 2026-08-26 10:42:00 -07:00
Mateo Wang
95285c3433
Merge pull request #38211 from eugene-yao-zocdoc/litellm_anthropic_responses_strictness_pr
fix(anthropic-responses): preserve structured output strictness
2026-08-26 10:39:45 -07:00
devin-ai-integration[bot]
c11c654b8e
fix(proxy): honor DATABASE_DISABLE_PREPARED_STATEMENTS in componentized entrypoints (#38363)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 10:39:04 -07:00
mateo-berri
0bfc733278 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_techdebt_20260822 2026-08-26 10:35:55 -07:00
mateo-berri
c449f11451 fix(anthropic): detect client credential headers case-insensitively on /v1/messages 2026-08-26 10:35:39 -07:00
yuneng
e1dcb6c76b style(ui): format the add model mapping column defs
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 17:25:04 +00:00
Devin AI
07c9812739 fix(model_prices): carry anthropic behavior flags on deepinfra claude entries, move retired together models to deprecated list
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 17:18:51 +00:00
yuneng
3f25e5b9f6 fix(ui): keep focus in the add model public name input while typing
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 17:17:32 +00:00
Devin AI
d3ede97189 fix(model_prices): keep novita gpt-oss-120b vision flag per provider catalog
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 16:57:26 +00:00
Devin AI
d266326a42 fix(model_prices): azure gpt-4.1-nano retirement date, together deprecations, novita gpt-oss-120b vision flag, fireworks deepseek-v4-pro-0813
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 16:56:53 +00:00
Devin AI
9300018414 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_registry_audit_bedrock_sol_anthropic_1hr 2026-08-26 16:46:44 +00:00
Mateo Wang
cdb60af024
Merge pull request #38269 from BerriAI/litellm_together_structured_outputs
fix(together_ai): fail open on response_format instead of dropping it for unregistered models
2026-08-26 09:34:56 -07:00
Mateo Wang
c81ceba431
Merge pull request #38232 from BerriAI/litellm_e2e_bedrock_customer_matrix
test(e2e): cover the Bedrock provider-feature cells customers run
2026-08-26 09:34:07 -07:00
ryan-crabbe-berri
597b4bb239
Merge pull request #38327 from BerriAI/litellm_tiered_pricing_rate_fallbacks
test(cost-calc): pin the rate fallbacks inside a tiered-pricing tier
2026-08-26 09:20:56 -07:00
Yuneng Jiang
3fdeb79737
fix(test): keep the mutmut sentinel out of the cleared environment
test_google_login_only_threads_user_code_when_enabled cleared the whole
process environment for the duration of the call. mutmut's trampoline reads
os.environ['MUTANT_UNDER_TEST'] with a bare subscript, so the first
trampolined callee inside the block, _get_cli_sso_flow_or_raise, raised
KeyError. The bare `except Exception: pass` swallowed it and the assertion
then read call_args on a mock that was never called, which is where
"'NoneType' object has no attribute 'kwargs'" came from.

The test only needs the SSO provider variables unset, so it now preserves the
rest of the environment instead of clearing everything. google_login does not
raise here, so the try/except is gone and any future exception propagates; the
added assert turns a silent early return into a readable failure instead of an
AttributeError.

Root cause measured in a trampolined copy of the mutated folder with
MUTANT_UNDER_TEST=stats: the old test fails there with
KeyError: 'MUTANT_UNDER_TEST' inside _mutmut_trampoline, the new one passes.
That was the only test the mutation run could not execute, so the --deselect
comes back out and it counts toward the score again.
2026-08-26 08:19:56 -07:00
ksk2023
a76e224a5f fix(cost_calculator): resolve real cost key when model_name alias contains '/'
When a router-facing model_name alias contains a '/' whose leading segment
is not a registered provider (e.g. 'vertex/claude-opus-5' for deployment
'vertex_ai/claude-opus-5'), _select_model_name_for_cost_calc re-prefixed
it into a non-existent key ('vertex_ai/vertex/claude-opus-5'), so cost
lookup silently priced every streamed request at $0 - token counts were
recorded, no error raised, budgets never tripped.

After prefixing, walk the alias tail and return the first assembly that
exists in litellm.model_cost ('vertex_ai/claude-opus-5'). Provider/region
segments in the head are preserved, and an alias that resolves to no
known key keeps the previous behavior (no crash, legacy double-prefix).

Fixes #38069
2026-08-26 22:06:06 +08:00
Yuneng Jiang
5bbbbff9dd
fix(ci): unblock the mutation run's stats phase
With the coverage fix in place the run generates mutants, then dies before
testing any of them: "1 failed ... failed to collect stats. runner returned 1".

The offender is one test. google_login is called inside a bare
`except Exception: pass` and the assertion then reads the mock's call_args, so
an early raise inside mutmut's mutants/ sandbox surfaces as
"'NoneType' object has no attribute 'kwargs'" rather than as the real error.

Deselected rather than ignored, so the other 248 tests in test_ui_sso.py still
contribute to the score.

This is measured rather than guessed. mutmut's stats phase hardcodes -x, so a
failing run only ever names its first offender, which is why deselecting looked
like whack-a-mole before. pytest_add_cli_args is appended after -x, and a later
--maxfail wins, so overriding it once let the whole folder run inside the
sandbox: 1 failed, 2901 passed. That one test is the only one that cannot run
there.

What is still not known is why it raises early in the sandbox. It is not the
suite and not the copied tree: the same folder passes outside mutants/ on the
runner image (2930), passes on a copied tree put first on PYTHONPATH (2902),
and passes with and without the test_saml_sso.py ignore. What is left is
mutmut's trampolines.
2026-08-26 05:10:43 -07:00
Mateo Wang
40423e6ec0
Merge pull request #38325 from BerriAI/litellm_fix_responses_id_stream_route
fix(proxy): encrypt streamed responses ids on /openai/v1/responses and /responses aliases
2026-08-26 02:38:24 -07:00
Mateo Wang
4185c8af07
Merge pull request #38320 from BerriAI/litellm_passthrough_object_ownership
fix(passthrough): record ownership of streamed responses under managed ids
2026-08-26 02:15:12 -07:00
mateo-berri
95f8373e3c test(responses): drive streamed-id regression via production ResponseCompletedEvent shape
The streamed-id regression test built a bare BaseLiteLLMOpenAIResponseObject with a
top-level id, hitting the wrong _encrypt_response_id branch. A real streamed create
emits ResponseCompletedEvent, whose client-visible id lives on event.response.id, so
the test now drives that production event shape and reads collected[0].response.id.
Mutating the alias route gate or disabling the .response.id encryption branch both
fail the test.
2026-08-26 01:52:01 -07:00
Yuneng Jiang
54cbc44705
test(cost-calc): pin the rate fallbacks inside a tiered-pricing tier
_get_tiered_base_costs documents that tiered pricing is all-or-nothing: a
tier is picked from the request's input tokens, and any rate that tier does
not declare falls back to the tier's own input rate so one request is never
priced from two tiers.

Nothing checked that. Every existing tiered test supplies a fully populated
tier, so the fallbacks were never reached: deleting them from the source
left the whole suite green. The fallbacks are not hypothetical either. Of
the 66 tiered rows shipped in model_prices_and_context_window.json, 54
declare no cache-creation rate and 44 declare no cache-read rate, so the
fallback is what prices their cached tokens today.

Adds three tests on the generic path:
  - a tier with no cache rates bills cached and cache-creation tokens at
    that tier's input rate, ignoring the model's top-level cache rates
  - a tier with no above-1hr rate bills 1h cache writes at the tier's
    cache-creation rate rather than zero
  - a tier with no input rate is not a priced tier at all, so the model's
    flat rates still apply instead of billing input at zero

Test-only change, no source touched.
2026-08-26 01:46:29 -07:00
Mateo Wang
5640e7c9dd
Merge pull request #38318 from BerriAI/litellm_fix_branchless_provider_status_mapping
fix(exceptions): map upstream status codes for providers with no exception_type branch
2026-08-26 01:46:25 -07:00
mateo-berri
498ba9dd62 fix(proxy): encrypt streamed responses ids on /openai/v1/responses and /responses aliases
The streaming security hook only encrypted response ids when request_route
matched "/v1/responses" exactly, so streamed creates on the /openai/v1/responses
and /responses aliases leaked the plain managed id. A second virtual key could
GET, continue, and DELETE another key's response. Normalize the route (strip the
provider prefix, accept the /responses alias) before gating, mirroring the
non-streaming hook which has no route gate.
2026-08-26 01:38:13 -07:00
mateo-berri
6a9662a5a8 fix(passthrough): recognize CR-only SSE frame delimiters when minting streamed managed ids 2026-08-26 01:36:18 -07:00
mateo-berri
6386a68c9c fix(router): fail fast on PermissionDeniedError with a single deployment 2026-08-26 01:09:14 -07:00
mateo-berri
e0c101b4da fix(passthrough): record ownership of streamed responses under managed ids 2026-08-26 01:05:15 -07:00
Devin AI
494fcf94a0 style: apply ruff format to prometheus caller identity validation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 08:03:43 +00:00
Devin AI
64c8931077 fix: resolve type gate regressions in prometheus caller identity validation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 08:01:48 +00:00
mateo-berri
910c41f154 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_branchless_provider_status_mapping
# Conflicts:
#	tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py
2026-08-26 00:55:42 -07:00
Devin AI
d675b904e0 chore(typing): clear fresh tech debt from the Aug 25 window
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 07:55:16 +00:00
Devin AI
2eedcb62ce Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_techdebt_20260822 2026-08-26 07:54:51 +00:00
Mateo Wang
137311ffd6
Merge pull request #38310 from BerriAI/litellm_fix_anthropic_messages_error_mapping
fix(otel): map /v1/messages provider errors before failure logging
2026-08-26 00:49:17 -07:00
mateo-berri
53037c34ed fix(exceptions): map upstream status codes for providers with no exception_type branch 2026-08-26 00:47:28 -07:00
Devin AI
055b6f6f69 chore: merge litellm_internal_staging into rolling techdebt branch
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 07:44:58 +00:00
mateo-berri
be0cac1ca7 fix(proxy): keep sk-shaped secrets stripped on no-master-key proxies 2026-08-26 00:37:17 -07:00
Yuneng Jiang
0c87bf5de9 test(cost-estimate): keep the new tests inside the test-quality budgets
Two of the new lines tripped the ratcheting gate.

TQ005 flagged restoring litellm.model_cost by assignment. Dropped the
save/restore pair for monkeypatch.setitem, which adds the one model the
test needs and takes it back out at teardown, so the module global is
never reassigned.

TQ008 flagged patching litellm.proxy.proxy_server.llm_router. The
endpoint imports the router from that module inside the function body,
so there is no seam to inject through without changing the endpoint.
Suppressed with the reason already used elsewhere in the suite for the
same module global, on the single helper the new tests share.
2026-08-26 00:32:25 -07:00
mateo-berri
5cfc1608f9 fix(anthropic): map only provider failures on the /v1/messages boundary 2026-08-26 00:31:18 -07:00
Mateo Wang
ef8eb98724
Merge pull request #38314 from BerriAI/litellm_together_e2e_replay_hardening
test(e2e): let the Together replayed-reasoning case survive a single provider miss
2026-08-26 00:30:01 -07:00
Yuneng Jiang
430fb71933 test(cost-estimate): pin the prices and period totals /cost/estimate returns
The endpoint already had tests for deployments that set both an input and
an output price, and for litellm_params winning over model_info. Nothing
covered a deployment that prices only one of the two sides, the daily and
monthly totals, or the price and provider read from the public cost map.

Found by changing one line of cost_tracking_settings.py at a time and
running the mapped test file against each change. Nine of eleven one-line
changes went unnoticed: dropping custom pricing entirely when only one
side is priced, billing the unpriced side at something other than zero,
skipping the model lookup so the reported price and provider go empty,
turning zero requests a day into a cost of zero rather than no estimate,
and scaling a period total by one request instead of the real count.

The seven tests added here kill all eleven. The cost math is real; only
the router is faked, matching the fixtures already in this file.
2026-08-26 00:20:35 -07:00
mateo-berri
7abd82f5e8 test(passthrough): cover opaque and jwt credential stripping under custom auth 2026-08-26 00:19:24 -07:00
Yuneng Jiang
b989f65b11
refactor(proxy): flatten the model/info health filter expression 2026-08-26 00:19:19 -07:00
mateo-berri
be9c170156 test(e2e): let the Together replayed-reasoning case survive a single provider miss 2026-08-26 00:14:32 -07:00
Yuneng Jiang
2d1e3a1c80
feat(proxy): hide unhealthy models from model listings, opt-in
Adds `general_settings.model_list_healthy_only`, which makes `/models`,
`/v1/models/{id}` and `/model/info` hide models whose backing deployments are
all marked unhealthy by background health checks, for every caller, without
each client having to pass `healthy_only=true`. `/model/info` also gains the
per-request `healthy_only` parameter that `/v1/models` already had.

Everything here is opt-in. With the setting absent, the endpoints take the same
code path they do today and no health lookup runs at all.

The listing filter reads the deployment health cache, which until now was only
populated when `enable_health_check_routing` was on, so `healthy_only=true`
silently did nothing in a plain `background_health_checks` setup. The setting
now also keeps that cache filled. That is a pure write: every routing-time
reader is itself gated on `enable_health_check_routing`, and the cooldown and
failure bookkeeping stays behind that flag, so routing is untouched.

Filtering stays presentation-only and fails open. A hidden model is still
callable, and missing, stale or empty health state hides nothing.
2026-08-26 00:13:52 -07:00
mateo-berri
3fe65029bf fix(exceptions): map anthropic 403 to PermissionDeniedError
Now that /v1/messages routes provider failures through exception_type, an
Anthropic permission_error fell through the anthropic branch to the generic
APIConnectionError and reached the client as a 500 where the raw exception
used to answer 403. Map 403 to PermissionDeniedError so the status survives
on every route.
2026-08-26 00:09:10 -07:00
mateo-berri
d17c501e98 Merge origin/litellm_fix_anthropic_messages_error_mapping, keeping exception_type unchanged
Bugbot Autofix pushed e2e16d7e2d to split 403 out of the shared 401/403 branch in _map_openai_like_exception. That premise was the BaseLLMException fallback, which d2e4e74685 already removed, and remapping 403 for every openai-like provider is a separate contract change, so this merge resolves both files back to the base branch versions
2026-08-25 23:56:20 -07:00
Yuneng Jiang
ac1eb1029a fix(ci): name the file mutmut actually writes partial results to
The step-timeout comment claimed mutmut streams each mutant's result into
mutants/mutmut-stats.json. It does not. That file holds the pre-run test
timings and coverage map (tests_by_mangled_function_name, duration_by_test,
stats_time) written once by save_stats() before mutation starts.

Per-mutant results live in mutants/<source path>.meta. Verified against
mutmut 3.5.0: SourceFileMutationData.register_result() calls save() after
every single result, and export-cicd-stats walks those .meta files to build
mutmut-cicd-stats.json. So the reason the step deadline exists is still
right, an interrupted run keeps the mutants it already scored, but the
comment pointed at the wrong file.

Also upload the .meta files, since they are the partial results the comment
relies on and the artifact could not otherwise show them.
2026-08-25 23:54:48 -07:00
mateo-berri
d2e4e74685 fix(otel): drop the generic BaseLLMException fallback from exception_type
The fallback mapped every unbranched provider error by status code on every route, which changed the exception class and HTTP status for those providers and failed four provider test suites in CI. The /v1/messages handler change alone covers the ticket, since the anthropic branch already maps its errors
2026-08-25 23:52:30 -07:00
Cursor Agent
e2e16d7e2d
fix(exceptions): map 403 to PermissionDeniedError in openai-like mapper 2026-08-26 06:51:33 +00:00
Yuneng Jiang
be4a79710d
test(azure-ai): match the exact route the provider is called on
The fixture was matched by host alone, so it answered any method and path and
the tests would have stayed green if the request went somewhere else. It now
matches POST on the Foundry route, and asserts the route was called.

Worth pinning on its own: the real path carries a /models prefix, which the
first attempt at this got wrong, so the match now also holds the routing in
place rather than only the retry.
2026-08-25 23:43:11 -07:00
Yuneng Jiang
8a61263ab4
test(azure-ai): build each retry test its own tool payload
The source drops the rejected field in place, so a payload shared across
tests could in principle be consumed by whichever case ran first. It does
not happen today, because the request is copied before the transform runs,
and the cases pass in reverse and async-first order alike. Building the
payload per call costs nothing and keeps that true if the copy ever goes.
2026-08-25 23:38:22 -07:00