Skip to content

fix(router): keep custom model_info across a price data reload - #35491

Merged
yassin-berriai merged 1 commit into
litellm_internal_stagingfrom
litellm_lit4675_model_cost_reload
Aug 5, 2026
Merged

fix(router): keep custom model_info across a price data reload#35491
yassin-berriai merged 1 commit into
litellm_internal_stagingfrom
litellm_lit4675_model_cost_reload

Conversation

@yassin-berriai

@yassin-berriai yassin-berriai commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Price data reload wiped custom model_info from every model group
  • Model Hub showed - / - for token limits after the reload
  • Operator model_info overrides silently reverted to catalog values
  • With pre-call checks on, alias requests died with "LLM Provider NOT provided"
  • Once registrations survived reloads, dead ones were replayed forever

How it solves it:

  • Runtime model metadata is re-applied onto the freshly fetched catalog
  • Both reload paths (scheduled job and manual endpoint) share one helper
  • Pre-call checks resolve the deployment model before the lookup that can raise
  • An unresolvable provider skips the filter instead of failing the request
  • A reload rebuilds deployment metadata from the routers that are alive then

Relevant issues

Linear ticket

Resolves LIT-4675

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Live proxy on a real Postgres, hitting the real OpenAI API and costing real money. Two deployments, both carrying model_info token limits:

model_list:
  - model_name: polyu-chat
    litellm_params:
      model: hosted_vllm/gpt-5.6            # OpenAI-compatible, absent from the built-in cost map
      api_base: https://api.openai.com/v1
      api_key: os.environ/OPENAI_API_KEY
    model_info:
      max_input_tokens: 128000
      max_output_tokens: 16384
  - model_name: dashscope-qwen
    litellm_params:
      model: dashscope/qwen-max             # present in the cost map, so the override is what is at stake
      api_key: os.environ/OPENAI_API_KEY
    model_info:
      max_input_tokens: 12345
      max_output_tokens: 678

router_settings:
  enable_pre_call_checks: true
general_settings:
  store_model_in_db: true

A reload interval was armed the way the Admin UI arms it:

$ curl -sX POST "http://127.0.0.1:4676/schedule/model_cost_map_reload?hours=24" -H "Authorization: Bearer $KEY"
{"message":"Model cost map reload scheduled for every 24 hours","status":"success","interval_hours":24,...}

Before, at 2a9843e

That is the current litellm_internal_staging tip, so this is the bug as it stands today. The Admin UI's Price Data Reload button, and nothing else:

$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=128000.0   max_output_tokens=16384.0
  dashscope-qwen   max_input_tokens=12345.0    max_output_tokens=678.0

$ curl -sX POST http://127.0.0.1:4676/reload/model_cost_map -H "Authorization: Bearer $KEY"
{"message":"Price data reloaded successfully! 2987 models updated.",...}

$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=None       max_output_tokens=None
  dashscope-qwen   max_input_tokens=30720.0    max_output_tokens=8192.0

$ curl -sX POST http://127.0.0.1:4676/v1/chat/completions -H "Authorization: Bearer $KEY" \
    -d '{"model":"polyu-chat","messages":[{"role":"user","content":"Reply with the single word OK"}]}'
  ERROR: litellm.BadRequestError: LLM Provider NOT provided. Pass in the LLM provider you are
  trying to call. You passed model=polyu-chat

polyu-chat lost its limits outright; dashscope-qwen is the quieter half of the same bug, its configured 12345/678 replaced by the catalog's 30720/8192.

It does not take a click on the pod in question either. One admin's click publishes a revision that every other pod picks up on its own poll, so a pod that was never touched wipes its own metadata a few seconds after boot:

12:38:27 - LiteLLM Proxy:INFO: periodic_reload_schedule.py:154 - Model cost map reload triggered by manual reload request
12:38:27 - LiteLLM Proxy:INFO: proxy_server.py:6588 - Model cost map reloaded successfully. Models count: 2987

$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=None       max_output_tokens=None
  dashscope-qwen   max_input_tokens=30720.0    max_output_tokens=8192.0

After, at 43d7ba0

The head moved to 856d2f7 after this was captured; the only difference is one debug log rewritten from an f-string to %-style arguments for the lazy-logging gate, which touches nothing shown here.

Same config, same database, same endpoints, and a second reload to show it is idempotent:

$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=128000.0   max_output_tokens=16384.0
  dashscope-qwen   max_input_tokens=12345.0    max_output_tokens=678.0

$ curl -sX POST http://127.0.0.1:4676/reload/model_cost_map -H "Authorization: Bearer $KEY"
{"message":"Price data reloaded successfully! 2987 models updated.",...}
$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=128000.0   max_output_tokens=16384.0
  dashscope-qwen   max_input_tokens=12345.0    max_output_tokens=678.0

$ curl -sX POST http://127.0.0.1:4676/reload/model_cost_map -H "Authorization: Bearer $KEY"   # again
$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=128000.0   max_output_tokens=16384.0
  dashscope-qwen   max_input_tokens=12345.0    max_output_tokens=678.0

$ curl -sX POST http://127.0.0.1:4676/v1/chat/completions -H "Authorization: Bearer $KEY" \
    -d '{"model":"polyu-chat","messages":[{"role":"user","content":"Reply with the single word OK"}]}'
  content=OK  total_tokens=16

The same pod-poll path, which is what a pod that nobody clicked on runs, keeps the metadata and still serves the request:

12:39:28 - LiteLLM Proxy:INFO: periodic_reload_schedule.py:154 - Model cost map reload triggered by manual reload request
12:39:29 - LiteLLM Proxy:INFO: proxy_server.py:6595 - Model cost map reloaded successfully. Models count: 2987

$ curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:4676/model_group/info
  polyu-chat       max_input_tokens=128000.0   max_output_tokens=16384.0
  dashscope-qwen   max_input_tokens=12345.0    max_output_tokens=678.0

$ curl -sX POST http://127.0.0.1:4676/v1/chat/completions -H "Authorization: Bearer $KEY" \
    -d '{"model":"polyu-chat","messages":[{"role":"user","content":"Reply with the single word OK"}]}'
  content=OK  total_tokens=16

The same thing on the Model Hub

This is the surface the report was filed against. Admin UI, AI Hub, Model Hub tab. These two shots were taken on an earlier head of this branch, before the rebase onto current staging; the transcripts above are the same claim re-run against the head as it stands.

Before. polyu-chat reads - / -, and dashscope-qwen reads the catalog's 30.7K / 8.2K rather than the configured 12.3K / 678:

Model Hub before the fix

After. Both rows carry what the config asked for:

Model Hub after the fix

The Mode column moving from chat to - for polyu-chat is the fix working, not a regression. Booting the base branch with the reload schedule cleared, so no reload ever runs, gives mode=None, max_input_tokens=128000, max_output_tokens=16384 for polyu-chat and mode='chat', 12345, 678 for dashscope-qwen, which is exactly the fixed post-reload state. The chat in the before shot came from the wipe falling back to the catalog, not from the deployment.

Deleting a model, on the same live proxy

Restoring registrations onto every catalog is only correct while whatever registered them still exists. GET /model/cost_map/source reports len(litellm.model_cost), which makes the accumulation directly observable: 25 create-then-delete cycles through the same endpoints the Admin UI's Models page uses, then a price data reload.

for i in $(seq 1 25); do
  ID=$(curl -s -X POST $B/model/new -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
    -d "{\"model_name\":\"leak-probe-$i\",\"litellm_params\":{\"model\":\"hosted_vllm/leak-probe-$i\",\"api_base\":\"http://127.0.0.1:9/v1\",\"api_key\":\"sk-x\"},\"model_info\":{\"max_input_tokens\":1234}}" | jq -r '.model_info.id')
  curl -s -X POST $B/model/delete -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d "{\"id\":\"$ID\"}" >/dev/null
done
curl -s -X POST $B/reload/model_cost_map -H "Authorization: Bearer $KEY"
curl -s -H "Authorization: Bearer $KEY" $B/model/cost_map/source | jq -r .model_count

An earlier head of this branch recorded every registration and replayed it, so the reload put the deleted models straight back and the count never came down. At 43d7ba0 the reload is the garbage collector again, exactly as it was before this PR; the difference is that it no longer takes the live deployments with it:

baseline model_count:              2990
after 25 create+delete cycles:     3040
Price data reloaded successfully! 2987 models updated.
after a price data reload:         2990

The catalog itself is 2987 models, so 2990 is the baseline plus the config-declared deployments, and 3040 is 50 entries that no longer describe anything the proxy can route to.

At the unit level the same thing holds for the paths a curl cannot reach. Twenty per-request routers built from a caller-supplied user_config and then discarded, and twenty clientside-credential deployments, each left 40 and 20 entries behind on the previously reviewed commit; both leave nothing now, while an operator's own register_model override is still recorded and still survives a reload.

Type

🐛 Bug Fix

Changes

litellm.model_cost carries two kinds of entry: the catalog fetched from the model cost map, and whatever is registered at runtime through litellm.register_model. The Router registers every deployment's model_info there under its model id and its backend model name, and per-request custom pricing lands there too. A price data reload replaced the whole dict, so everything in the second category vanished. /model_group/info reads litellm.model_cost through get_deployment_model_info, which is why a model absent from the catalog came back with null token limits, and why a model present in it silently reverted to the catalog's numbers.

The reload now re-applies both on top of the freshly fetched catalog. That ordering matters: the catalog supplies fresh pricing and the re-applied entries re-assert the operator's intent over it, which is the same precedence a fresh boot produces. How each is restored is the subject of the next section, and it is the part the security review changed. Both reload paths already adopt a fetched catalog through the one _swap_in_model_cost_map helper, so the re-apply hangs off its tail and neither path can forget it; the count it returns is taken before anything is re-applied, so the endpoint keeps reporting on the price data alone.

The second defect is independent and outlives the first. Router._pre_call_checks resolved the per-deployment model name on the line after get_router_model_info, so any deployment that model-info lookup cannot map left the name unset, and the supported-params check then ran get_llm_provider on the bare model group name. That call sits outside the surrounding try, so a filter turned into a 400 for the whole request. The name is now resolved first, and provider resolution failing there skips the check for that deployment rather than escaping deployment selection, matching what the block already does when a provider reports no supported params.

Not everything registered at runtime is durable, and the difference matters here. Config-declared deployment metadata and explicit register_model overrides are lasting operator intent, so they are restored. Per-request custom pricing describes one call, so _register_custom_pricing_for_request opts out via persist_across_reloads=False; carrying it forward would let a one-off price beat fresh upstream pricing for the rest of the process.

Restoring a registration is only right while whatever registered it still exists, and that is what the security review caught. Before this PR a reload replaced litellm.model_cost wholesale, so a deleted deployment's entries were swept away as a side effect of the wipe. Preserving registrations removes that sweep, and a recording that only ever grows brings the deletion question with it.

So deployment metadata is not recorded at all. A reload asks the routers that are alive at that moment to re-assert the deployments they are serving right then, which is what _replay_model_cost_registrations does, over a weakref.WeakSet of live routers. Everything the accumulation question was about answers itself: a deleted deployment is not in model_list, a repointed one is there under its new backend, a deployment dropped for an inactive environment or an invalid tag_regex was never added, and a router built per request from a caller-supplied user_config is unreferenced the moment the request ends. None of them need withdrawing, so none of them can be missed, and there is no unregister path to keep in step with future removal paths.

The one thing with no owner to ask is an explicit litellm.register_model override, so those are still recorded and replayed. They come from an operator rather than from request traffic, and a registration describing a single request opts out via persist_across_reloads=False.

Registration itself moved into _register_deployment_in_model_cost, shared by _create_deployment, add_deployment and the rebuild, so a reload writes what a boot writes rather than a second implementation of it. That also settles a divergence between the two registration sites: add_deployment previously skipped the shared-backend mode preservation that _create_deployment applies, and now both get it.

Tests cover both halves and each was checked against a mutant: reverting the assignment order, dropping the provider guard, dropping the replay, dropping the recording, recording request-scoped registrations anyway, taking the reported model count after the re-apply rather than before, and reintroducing the stale variable reference in the scheduled path each turn the relevant test red. The rebuild was mutation-checked the same way: dropping the rebuild entirely, letting a discarded router keep contributing, leaving a deleted deployment in model_list so the rebuild still sees it, and recording router registrations globally again all turn the relevant test red. One mutation survives and is reported rather than counted as a kill: dropping the litellm_params custom-pricing fold from the payload changes nothing today, because both registration sites already carry that pricing on the stored model_info. It is kept so the payload stays derivable from a deployment alone, which is the property the rebuild rests on

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR preserves operator model metadata across model-cost catalog reloads while preventing request-scoped and deleted deployment registrations from being replayed.

  • Centralizes scheduled and manual catalog refreshes in refresh_model_cost_map.
  • Rebuilds deployment metadata from currently live routers and preserves explicit operator registrations.
  • Excludes per-request pricing registrations from durable replay.
  • Resolves deployment models before supported-parameter pre-call filtering and tolerates unresolved providers.

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported request-pricing persistence issue is fixed because all current request-scoped pricing registrations opt out of durable replay, while router-owned deployment entries are rebuilt only from live router state.

Important Files Changed

Filename Overview
litellm/utils.py Adds the centralized catalog-refresh and explicit-registration replay mechanism while allowing request-scoped registrations to opt out.
litellm/router.py Rebuilds cost-map metadata from live router deployments and adjusts provider resolution during pre-call checks.
litellm/main.py Marks completion and embedding request-scoped custom pricing as nonpersistent across catalog reloads.
litellm/proxy/proxy_server.py Routes both scheduled and manual model-cost reloads through the shared refresh helper.
tests/test_litellm/test_router_model_cost_isolation.py Covers live-router replay, discarded routers, deployment deletion and repointing, and request-pricing isolation.
tests/test_litellm/test_utils.py Covers durable operator registration replay and removal of nonpersistent request registrations on refresh.

Reviews (4): Last reviewed commit: "fix(router): keep custom model_info acro..." | Re-trigger Greptile

Comment thread litellm/utils.py Outdated
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.87179% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/router.py 96.55% 2 Missing ⚠️
litellm/utils.py 86.66% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@yassin-berriai
yassin-berriai force-pushed the litellm_lit4675_model_cost_reload branch from 77ba68a to 478a554 Compare August 1, 2026 20:25
@yassin-berriai

Copy link
Copy Markdown
Contributor Author

@greptileai please review the current head 478a554

Good catch on the registration lifetime; the finding was right and is fixed rather than rebutted. register_model now takes persist_across_reloads (default True, so an explicit override still survives a refresh) and _register_custom_pricing_for_request in litellm/main.py passes False, so per-request pricing is applied to the current catalog and never replayed onto later ones. A regression test pins both directions in one refresh: the durable override wins over fresh catalog pricing, the request-scoped one does not.

The push also fixes a real bug your review prompted me to look harder for. The scheduled reload path still referenced the local I had removed, inside a block wrapped by a broad except, so the reload worked while its success log silently raised. There is now a test that drives _check_and_reload_model_cost_map and asserts nothing was logged as an exception, mutation-checked against exactly that regression.

Comment thread litellm/utils.py
@veria-ai

veria-ai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@codspeed-hq

codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_lit4675_model_cost_reload (856d2f7) with litellm_internal_staging (2792887)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (09dd167) during the generation of this report, so 2792887 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@yassin-berriai
yassin-berriai force-pushed the litellm_lit4675_model_cost_reload branch 2 times, most recently from 57edabd to 56d4aad Compare August 3, 2026 22:02
@yassin-berriai

Copy link
Copy Markdown
Contributor Author

@greptileai please review the current head 56d4aad

The security review's finding was right and is fixed rather than rebutted. Before this PR a reload replaced litellm.model_cost wholesale, so a deleted deployment's entries were swept away as a side effect of the wipe; once the replay preserves registrations, nothing sweeps them, and repeated create/delete grows the registry for the life of the process. Router.delete_deployment now withdraws what the deployment registered, and upsert_deployment does the same for the outgoing registration so repointing a deployment at a different backend model does not strand the old key.

The two key kinds are handled differently on purpose. A deployment id was only ever written by that one deployment, so it is dropped from litellm.model_cost outright. A backend key is shared with the catalog and with other deployments on the same backend model, so it is withdrawn from the replay registry only, and only once no remaining deployment registers under it; dropping it from litellm.model_cost would take real upstream pricing down with it. A test pins that by deleting a deployment sitting on gemini/gemini-2.5-pro and asserting the catalog price survives.

Proof of fix in the description is re-captured on a live proxy against this head: 25 create-then-delete cycles plus a price data reload leave len(litellm.model_cost) at 3036 on the previously reviewed commit and back at its 2986 baseline here. Five mutants were checked, one per branch of the withdrawal logic. The branch is also rebased onto current staging, which resolves the conflict.

@yassin-berriai

Copy link
Copy Markdown
Contributor Author

osv-scan is red for a reason that is not in this diff. It flags `cryptography 48.0.1` in `uv.lock` (GHSA-g6cj-pr64-35w5, GHSA-jwv3-5hgf-82ww, GHSA-m2h6-j472-rp4c; fixed in 49.0.0 / 50.0.0), and `git diff litellm_internal_staging...HEAD -- uv.lock pyproject.toml` is empty here. Every open PR is failing the same scan right now, so the pin is on the base branch and the bump belongs in its own PR rather than this one.

@yassin-berriai
yassin-berriai force-pushed the litellm_lit4675_model_cost_reload branch 2 times, most recently from 9c5f776 to ca31fac Compare August 3, 2026 23:33
@yassin-berriai

Copy link
Copy Markdown
Contributor Author

@greptileai please review the current head ca31fac

This replaces the withdrawal hooks from the previous round with a change of mechanism, because the hooks were the wrong shape. The security review's finding was one instance of a class: a reload was re-asserting everything ever registered, so anything whose owner had gone kept coming back. Each hook I added closed one path and left the next one open, and one of them could not be closed at all, since _route_user_config_request builds a Router per request from caller-supplied config and discards it, and no unregister call can reach a router nothing references. Measured on the previously reviewed commit, 20 such requests left 40 entries behind and 20 clientside-credential deployments left 20.

Deployment metadata is now not recorded at all. A reload asks the routers alive at that moment to re-assert what they are serving right then, over a weakref.WeakSet. A deleted deployment is not in model_list, a repointed one is there under its new backend, one dropped for an inactive environment or an invalid tag_regex was never added, and a discarded router is gone. All of it falls out of the design rather than out of a hook, and unregister_model plus both call sites are deleted. Explicit register_model overrides have no owner to ask, so those are still recorded; they come from an operator rather than from request traffic.

Registration moved into one _register_deployment_in_model_cost used by _create_deployment, add_deployment and the rebuild, so a reload writes what a boot writes. Worth flagging that this also gives add_deployment the shared-backend mode preservation _create_deployment already had, which was an existing divergence between two near-copies.

Proof of fix is re-captured live against this head, and a test asserts the rebuilt entry contains every field the boot registration wrote, including the cache pricing that is derived rather than stored. Four mutants are killed; one survives and is reported in the description as equivalent rather than counted as a kill.

@yassin-berriai
yassin-berriai force-pushed the litellm_lit4675_model_cost_reload branch from ca31fac to 43d7ba0 Compare August 5, 2026 19:30
A price data reload replaced litellm.model_cost wholesale, discarding every
runtime registration: the deployment model_info the Router registers from
model_list, and pricing overrides passed to litellm.register_model. Custom
model groups lost max_input_tokens / max_output_tokens in /model_group/info,
and a deployment whose backend model is in the catalog silently reverted to
upstream values. Runtime registrations are now recorded and replayed on top of
the freshly fetched catalog.

Router._pre_call_checks resolved the per-deployment model name only after the
model-info lookup, so an unregistered model left it unset and the supported
params check ran against the bare model group name, raising "LLM Provider NOT
provided" out of deployment selection. The name is now resolved first, and an
unresolvable provider skips that check rather than failing the request.

Resolves LIT-4675
@yassin-berriai
yassin-berriai force-pushed the litellm_lit4675_model_cost_reload branch from 43d7ba0 to 856d2f7 Compare August 5, 2026 19:42
@yassin-berriai
yassin-berriai enabled auto-merge (squash) August 5, 2026 19:55
@yassin-berriai
yassin-berriai merged commit 347798b into litellm_internal_staging Aug 5, 2026
79 checks passed
@yassin-berriai
yassin-berriai deleted the litellm_lit4675_model_cost_reload branch August 5, 2026 19:56
sgran pushed a commit to junohq/litellm that referenced this pull request Aug 6, 2026
Since BerriAI#35491, register_model records every registration in the process-global
_runtime_registered_model_cost ledger, and every cost map swap replays that
ledger on top of the freshly adopted map. Under pytest-xdist, any earlier test
in the same worker that registered gpt-3.5-turbo leaked into
TestPriceDataReloadIntegration::test_distributed_reload_check_function: the
replay ballooned its sparse mocked entry into a full ModelInfo dict and failed
the exact-equality assert, breaking the proxy-infra shard whenever loadscope
happened to co-schedule such a test first (reruns cannot help since the
pollution is process-wide)

The autouse isolate_litellm_state fixture now snapshots the ledger before each
test and restores it in place on teardown, so no test's registrations outlive
it. A regression pair in test_conftest_isolation.py asserts the rollback
mateo-berri added a commit that referenced this pull request Aug 8, 2026
Since #35491, every Router joins the module-global _live_routers weak set at
construction, and every model cost map swap replays the deployments of every
member on top of the freshly adopted map. #36039 isolated the register_model
ledger half of that replay but not this half: under pytest-xdist, a Router
created by an earlier test in the same worker that was still referenced (or
simply not yet garbage collected) re-registered its deployments during
TestPriceDataReloadIntegration::test_distributed_reload_check_function, and
register_model hydrated the sparse mocked gpt-3.5-turbo entry into a full
ModelInfo dict, failing the exact-equality assert (reruns cannot help since
the polluting router survives in the worker process)

The autouse isolate_litellm_state fixture now snapshots _live_routers before
each test and restores its membership on teardown, so a test's routers stop
contributing to cost map rebuilds once the test ends. A canary pair in
test_conftest_isolation.py asserts the rollback
doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Aug 17, 2026
…7.0) (#336)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/berriai/litellm](https://images.chainguard.dev/directory/image/wolfi-base/overview) ([source](https://github.com/BerriAI/litellm)) | minor | `v1.96.2` → `v1.97.0` |

---

### Release Notes

<details>
<summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary>

### [`v1.97.0`](https://github.com/BerriAI/litellm/releases/tag/v1.97.0)

[Compare Source](https://github.com/BerriAI/litellm/compare/v1.97.0...v1.97.0)

##### Verify Docker Image Signature

All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](https://github.com/BerriAI/litellm/commit/0112e53046018d726492c814b3644b7d376029d0).

**Verify using the pinned commit hash (recommended):**

A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key:

```bash
cosign verify \
  --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \
  ghcr.io/berriai/litellm:v1.97.0
```

**Verify using the release tag (convenience):**

Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules:

```bash
cosign verify \
  --key https://raw.githubusercontent.com/BerriAI/litellm/v1.97.0/cosign.pub \
  ghcr.io/berriai/litellm:v1.97.0
```

Expected output:

```
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key
```

***

##### What's Changed

- feat(proxy): resolve Cursor thinking/fast model-name suffixes on /cursor/chat/completions by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35554](https://github.com/BerriAI/litellm/pull/35554)
- fix(team-callbacks): actually stop logging when disable\_logging is called by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35520](https://github.com/BerriAI/litellm/pull/35520)
- refactor(lint): drop redundant !s f-string conversion flags and fix displaced import-group comments by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35546](https://github.com/BerriAI/litellm/pull/35546)
- fix(proxy): backfill null user\_email on existing users during JWT auth by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34588](https://github.com/BerriAI/litellm/pull/34588)
- feat(playground): add non-streaming response toggle by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35560](https://github.com/BerriAI/litellm/pull/35560)
- feat(teams): apply default organization to new teams from default team settings by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35540](https://github.com/BerriAI/litellm/pull/35540)
- fix(ui): block Playground page for viewer roles on direct URL access by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35676](https://github.com/BerriAI/litellm/pull/35676)
- fix(caching): close evicted LLM clients so their connections are reclaimed by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35492](https://github.com/BerriAI/litellm/pull/35492)
- chore(deps): update brace-expansion, postcss, and gitpython to current patch releases by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35692](https://github.com/BerriAI/litellm/pull/35692)
- refactor(ui): rename the create MCP server component to PascalCase by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35686](https://github.com/BerriAI/litellm/pull/35686)
- fix(openai): drop undefined Union from owns\_wrapped\_http\_client annotation by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35706](https://github.com/BerriAI/litellm/pull/35706)
- fix(openai): drop the undefined Union from owns\_wrapped\_http\_client by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35704](https://github.com/BerriAI/litellm/pull/35704)
- chore(ui): note Google's Agent Platform rename in vector store setup by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;28076](https://github.com/BerriAI/litellm/pull/28076)
- fix(proxy): apply key/team router\_settings.model\_group\_alias by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35486](https://github.com/BerriAI/litellm/pull/35486)
- feat(complexity\_router): default session affinity off and expose it in the UI by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35714](https://github.com/BerriAI/litellm/pull/35714)
- fix(datadog): read team callback dd\_\* params from kwargs instead of blocked dynamic params ([#&#8203;35115](https://github.com/BerriAI/litellm/issues/35115) port) by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35687](https://github.com/BerriAI/litellm/pull/35687)
- refactor(ui): extract the MCP create form's logic and field groups by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35694](https://github.com/BerriAI/litellm/pull/35694)
- test(ui): tier the MCP create tests into unit and integration by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35697](https://github.com/BerriAI/litellm/pull/35697)
- fix(proxy): redact credential headers from request logging copies by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35678](https://github.com/BerriAI/litellm/pull/35678)
- feat(guardrails/rubrik): prompt moderation, response-text blocking, streaming buffer, failure logging by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35722](https://github.com/BerriAI/litellm/pull/35722)
- fix(ui): render Responses API request and response in the logs drawer by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35718](https://github.com/BerriAI/litellm/pull/35718)
- fix(ui): hide guardrail review buttons from non-admin users by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;27535](https://github.com/BerriAI/litellm/pull/27535)
- feat(team): custom metadata validation hook for team create and update by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;33353](https://github.com/BerriAI/litellm/pull/33353)
- ci(circleci): install a pinned Rust toolchain on the Linux jobs by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35519](https://github.com/BerriAI/litellm/pull/35519)
- fix(bedrock): stop forwarding no-op toolSpec.strict to Converse by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35688](https://github.com/BerriAI/litellm/pull/35688)
- fix(ui): reject an auto-router keyword rule left empty instead of dropping it by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35705](https://github.com/BerriAI/litellm/pull/35705)
- fix(guardrails/rubrik): attribute blocked requests to the caller that made them by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35734](https://github.com/BerriAI/litellm/pull/35734)
- fix(responses): forward client headers to the provider on /v1/responses by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34531](https://github.com/BerriAI/litellm/pull/34531)
- feat(spend): add net auto-router savings to the cost-optimization dashboard by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35521](https://github.com/BerriAI/litellm/pull/35521)
- chore(typing): clear basedpyright Any errors in budget reset, access groups, and cache settings by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35719](https://github.com/BerriAI/litellm/pull/35719)
- fix(spend): read what a request cost from the record instead of pricing it again by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35736](https://github.com/BerriAI/litellm/pull/35736)
- perf: install hiredis so redis-py parses replies with its C parser by [@&#8203;Classic298](https://github.com/Classic298) in [#&#8203;35709](https://github.com/BerriAI/litellm/pull/35709)
- feat(ui): show auto-router savings on the cost-optimization dashboard by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35522](https://github.com/BerriAI/litellm/pull/35522)
- perf: build log messages lazily so filtered-out log records cost nothing by [@&#8203;Classic298](https://github.com/Classic298) in [#&#8203;35703](https://github.com/BerriAI/litellm/pull/35703)
- fix(proxy): retry model cost map fetch with Retry-After-aware backoff and keep current map on reload failure by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35739](https://github.com/BerriAI/litellm/pull/35739)
- feat(otel): stamp service tier attributes on inference spans by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35679](https://github.com/BerriAI/litellm/pull/35679)
- fix(proxy): log the model cost map reload failure lazily by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35750](https://github.com/BerriAI/litellm/pull/35750)
- fix(groq): translate web\_search\_options to the browser\_search tool by [@&#8203;hMED22](https://github.com/hMED22) in [#&#8203;34971](https://github.com/BerriAI/litellm/pull/34971)
- feat(ui): add admin-configurable user banner by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35729](https://github.com/BerriAI/litellm/pull/35729)
- fix(e2e): make spend-counter redis connection env-driven for non-cluster deployments by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35732](https://github.com/BerriAI/litellm/pull/35732)
- fix(proxy): make /cursor/chat/completions work with Cursor agent mode by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;34029](https://github.com/BerriAI/litellm/pull/34029)
- fix(proxy): propagate user\_email and bind api\_key on JWT auth attribution paths by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34331](https://github.com/BerriAI/litellm/pull/34331)
- chore(build): move the Admin UI toolchain to Node 24 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35801](https://github.com/BerriAI/litellm/pull/35801)
- test(e2e): vendor API strategy coverage across endpoints by [@&#8203;mubashir1osmani](https://github.com/mubashir1osmani) in [#&#8203;34649](https://github.com/BerriAI/litellm/pull/34649)
- chore(deps): upgrade cryptography to 50.0.0 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35803](https://github.com/BerriAI/litellm/pull/35803)
- test(e2e): cover legacy text /completions endpoint by [@&#8203;mubashir1osmani](https://github.com/mubashir1osmani) in [#&#8203;34431](https://github.com/BerriAI/litellm/pull/34431)
- feat(gemini): add gemini-robotics-er-2-preview and gemini-robotics-er-1.6-preview by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35555](https://github.com/BerriAI/litellm/pull/35555)
- test(e2e): move load/perf testing out of the main suite and drop the vllm passthrough test by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35820](https://github.com/BerriAI/litellm/pull/35820)
- feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35807](https://github.com/BerriAI/litellm/pull/35807)
- chore: bump litellm-proxy-extras 0.4.81 -> 0.4.82, litellm 1.96.0 -> 1.97.0 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35810](https://github.com/BerriAI/litellm/pull/35810)
- fix(bedrock): drop conflicting tool\_choice.type when toolConfig.toolChoice is set by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35738](https://github.com/BerriAI/litellm/pull/35738)
- docs(CLAUDE.md): prefer commas over semicolons when replacing em dashes by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35825](https://github.com/BerriAI/litellm/pull/35825)
- chore(lint): zero out basedpyright headroom for purely local rules by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35828](https://github.com/BerriAI/litellm/pull/35828)
- test(e2e): retry provider-transient statuses at the transport with bounded backoff by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35824](https://github.com/BerriAI/litellm/pull/35824)
- chore(ci): promote internal staging to main by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35836](https://github.com/BerriAI/litellm/pull/35836)
- refactor(ui): route MCP session tokens through the shared storage helper by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35835](https://github.com/BerriAI/litellm/pull/35835)
- docs(helm): replace the classic chart's 128Mi resource example with the documented 4Gi sizing by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35830](https://github.com/BerriAI/litellm/pull/35830)
- fix(proxy): persist periodic reload schedule state so status survives restarts and fires without store\_model\_in\_db by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35165](https://github.com/BerriAI/litellm/pull/35165)
- fix(router): eagerly fetch Vertex AI deferred stream to surface HTTP errors in \_acompletion fallback path by [@&#8203;deepanshululla](https://github.com/deepanshululla) in [#&#8203;34627](https://github.com/BerriAI/litellm/pull/34627)
- fix(azure\_storage): honor AZURE\_STORAGE\_ENDPOINT\_SUFFIX for sovereign clouds by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35806](https://github.com/BerriAI/litellm/pull/35806)
- fix(proxy): apply key\_alias/key\_hash filters to all /key/list visibility branches by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35840](https://github.com/BerriAI/litellm/pull/35840)
- fix(proxy): enforce per-model budgets against resolved cursor model variants by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35834](https://github.com/BerriAI/litellm/pull/35834)
- feat(ui): reorder Add Auto Router into name + template, with a collapsible detailed config by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35746](https://github.com/BerriAI/litellm/pull/35746)
- test: repair three failing suites on litellm\_internal\_staging by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35845](https://github.com/BerriAI/litellm/pull/35845)
- fix(guardrails): scan model output on the /openai/v1/responses alias by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35818](https://github.com/BerriAI/litellm/pull/35818)
- ci: pin Node on the Playwright UI lanes so npm ci meets the engines floor by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35848](https://github.com/BerriAI/litellm/pull/35848)
- fix(pricing): apply OpenAI's gpt-5.6 terra/luna cut to Azure cost map by [@&#8203;mubashir1osmani](https://github.com/mubashir1osmani) in [#&#8203;35481](https://github.com/BerriAI/litellm/pull/35481)
- feat(spend): add caller-scoped key/user/team/organization spend report endpoints by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35725](https://github.com/BerriAI/litellm/pull/35725)
- revert: "fix(caching): close evicted LLM clients so their connections are reclaimed ([#&#8203;35492](https://github.com/BerriAI/litellm/issues/35492))" by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35856](https://github.com/BerriAI/litellm/pull/35856)
- refactor(repositories): add prisma protocol seams and a spend-reset unit of work by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35748](https://github.com/BerriAI/litellm/pull/35748)
- perf(streaming): assemble streamed tool-call arguments in linear time by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35826](https://github.com/BerriAI/litellm/pull/35826)
- fix(s3\_v2): sign S3 object URLs with S3SigV4Auth so encoded paths verify by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35726](https://github.com/BerriAI/litellm/pull/35726)
- test(e2e): self-seed the ui suite's password-login users in global setup by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35863](https://github.com/BerriAI/litellm/pull/35863)
- fix(claude-code): create-only skill registration with a PUT update route (LIT-4110) by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;31752](https://github.com/BerriAI/litellm/pull/31752)
- fix(proxy):  fix zguard  httpcode when block input by [@&#8203;jwang-gif](https://github.com/jwang-gif) in [#&#8203;31948](https://github.com/BerriAI/litellm/pull/31948)
- fix(lint): pick the merge-aware base so in-progress merges are not blamed for base drift by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35868](https://github.com/BerriAI/litellm/pull/35868)
- chore: bump litellm-proxy-extras 0.4.82 -> 0.4.83 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35877](https://github.com/BerriAI/litellm/pull/35877)
- feat(ui): add Test Routing to the auto router create form by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35859](https://github.com/BerriAI/litellm/pull/35859)
- fix(ui): derive auto-router preset tests from the bundled preset JSON by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35882](https://github.com/BerriAI/litellm/pull/35882)
- revert: "test(e2e): vendor API strategy coverage across endpoints" ([#&#8203;34649](https://github.com/BerriAI/litellm/issues/34649)) by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35881](https://github.com/BerriAI/litellm/pull/35881)
- chore(deps): bump grpc and golang.org/x modules in the terraform provider by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35844](https://github.com/BerriAI/litellm/pull/35844)
- test(e2e): skip view-backed global spend probes pending LIT-5211 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35875](https://github.com/BerriAI/litellm/pull/35875)
- fix(lint): move the basedpyright heap flag into the type check gate by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35869](https://github.com/BerriAI/litellm/pull/35869)
- chore(ci): promote internal staging to main by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35876](https://github.com/BerriAI/litellm/pull/35876)
- feat(ui): add role capability gating, migrate Tool Policies route by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35812](https://github.com/BerriAI/litellm/pull/35812)
- refactor(ui): inject the fetch client's base url instead of reading it at import by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35802](https://github.com/BerriAI/litellm/pull/35802)
- chore: remove unused .flake8 config and flake8 dev dependency by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35888](https://github.com/BerriAI/litellm/pull/35888)
- chore: stop advising pre-commit and bootstrap by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35884](https://github.com/BerriAI/litellm/pull/35884)
- fix(auth): name enable\_jwt\_auth when a JWT-shaped key is rejected by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35831](https://github.com/BerriAI/litellm/pull/35831)
- feat(auto-router): make reminder marker pair configurable by [@&#8203;akapur99](https://github.com/akapur99) in [#&#8203;35874](https://github.com/BerriAI/litellm/pull/35874)
- fix(UI): update anthropic model presets by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35896](https://github.com/BerriAI/litellm/pull/35896)
- fix(bootstrap): switch to the dashboard node floor via nvm or fnm by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35895](https://github.com/BerriAI/litellm/pull/35895)
- perf(pre-commit): run python, dashboard, and gen-api checks concurrently by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35903](https://github.com/BerriAI/litellm/pull/35903)
- feat(spend): derive a default auto-router savings baseline from the hardest tier by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35907](https://github.com/BerriAI/litellm/pull/35907)
- fix(http\_handler): self-heal handler clients closed after cache eviction by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35862](https://github.com/BerriAI/litellm/pull/35862)
- fix(cost\_tracking): keep OpenAI prompt cache token details through usage reassembly by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34812](https://github.com/BerriAI/litellm/pull/34812)
- fix(cost): bill gpt-5.6 prompt cache reads at the cache read rate by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34957](https://github.com/BerriAI/litellm/pull/34957)
- fix(batches): account for Responses API usage by [@&#8203;rimysore](https://github.com/rimysore) in [#&#8203;35367](https://github.com/BerriAI/litellm/pull/35367)
- ci: retry Codecov uploads and stop failing jobs on OIDC token flakes by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35251](https://github.com/BerriAI/litellm/pull/35251)
- feat(complexity\_router): let operators rename the four complexity tiers by [@&#8203;akapur99](https://github.com/akapur99) in [#&#8203;35893](https://github.com/BerriAI/litellm/pull/35893)
- chore(lint): zero stale ruff and LIT headroom and strip inert type: ignore comments by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35928](https://github.com/BerriAI/litellm/pull/35928)
- chore(lint): zero out seven more purely local basedpyright rules by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35927](https://github.com/BerriAI/litellm/pull/35927)
- chore(ui): zero stale headroom on local dashboard eslint budgets by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35929](https://github.com/BerriAI/litellm/pull/35929)
- fix(managed-files): skip rows without file objects by [@&#8203;rimysore](https://github.com/rimysore) in [#&#8203;35365](https://github.com/BerriAI/litellm/pull/35365)
- fix(router): redact fallback tracebacks at the call site and cover the sync deferred stream by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35843](https://github.com/BerriAI/litellm/pull/35843)
- fix(migrations): recover from an interrupted Prisma toolchain install by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35832](https://github.com/BerriAI/litellm/pull/35832)
- fix(lint): bring basedpyright rule counts back under their budget limits by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35962](https://github.com/BerriAI/litellm/pull/35962)
- chore(ui): don't zero out stale headroom except no-console by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35964](https://github.com/BerriAI/litellm/pull/35964)
- fix(proxy): give proxy\_admin\_viewer read parity with proxy\_admin by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35851](https://github.com/BerriAI/litellm/pull/35851)
- refactor(ui): address UI lint budget issues by refactoring UI by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35960](https://github.com/BerriAI/litellm/pull/35960)
- fix(ci): make the env-key doc gate see get\_secret\_bool reads by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35833](https://github.com/BerriAI/litellm/pull/35833)
- fix(caching): re-land evicted LLM client closing ([#&#8203;35492](https://github.com/BerriAI/litellm/issues/35492)) atop self-healing handlers by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35870](https://github.com/BerriAI/litellm/pull/35870)
- fix(proxy): keep the connected DB client when a startup health check fails by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35837](https://github.com/BerriAI/litellm/pull/35837)
- chore(lint): remove litellm/types from the ruff lint exclusion by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35926](https://github.com/BerriAI/litellm/pull/35926)
- feat(sgr): make the gateway middleware the source of truth for successful requests by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35717](https://github.com/BerriAI/litellm/pull/35717)
- feat(auto-router): let operators replace the LLM classifier's system prompt by [@&#8203;akapur99](https://github.com/akapur99) in [#&#8203;35855](https://github.com/BerriAI/litellm/pull/35855)
- fix(docker): bake the pip image's prisma engines at a world-readable path by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35976](https://github.com/BerriAI/litellm/pull/35976)
- fix(auth): return 403 from the OAuth2 enterprise gate by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35838](https://github.com/BerriAI/litellm/pull/35838)
- fix(router): keep custom model\_info across a price data reload by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35491](https://github.com/BerriAI/litellm/pull/35491)
- fix(proxy): resolve pass-through credentials live from router deployments by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35916](https://github.com/BerriAI/litellm/pull/35916)
- fix(ci): fetch only head and merge-base in lint jobs instead of every branch by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35982](https://github.com/BerriAI/litellm/pull/35982)
- fix(autorouter): match CJK keyword\_tier\_rules that regex word boundaries miss by [@&#8203;akapur99](https://github.com/akapur99) in [#&#8203;35984](https://github.com/BerriAI/litellm/pull/35984)
- feat(spend): rebuild the auto-router benchmarks backend as a per-session rollup by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35910](https://github.com/BerriAI/litellm/pull/35910)
- refactor(ui): replace hand-rolled query-param routing with nuqs by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35871](https://github.com/BerriAI/litellm/pull/35871)
- fix(docker): bake the componentized prisma engines at /opt/prisma so any uid can start by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35989](https://github.com/BerriAI/litellm/pull/35989)
- fix(migrations): keep the toolchain heal from raising on an unreadable nodeenv cache by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35986](https://github.com/BerriAI/litellm/pull/35986)
- fix(bedrock): sign Bedrock managed-file S3 requests with S3SigV4Auth by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35983](https://github.com/BerriAI/litellm/pull/35983)
- chore(typing): replace Any seams with real types across responses, proxy, and provider adapters by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35809](https://github.com/BerriAI/litellm/pull/35809)
- fix(ai21): resolve the documented AI21\_API\_KEY instead of a misspelled name by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35985](https://github.com/BerriAI/litellm/pull/35985)
- fix(docker): fail the image build when the generated prisma engine paths drift off /opt/prisma by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35979](https://github.com/BerriAI/litellm/pull/35979)
- fix(jina\_ai): resolve the documented JINA\_API\_KEY as a fallback by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35992](https://github.com/BerriAI/litellm/pull/35992)
- fix(proxy): only treat a recoverable database outage as grounds to serve without one by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35864](https://github.com/BerriAI/litellm/pull/35864)
- fix(ci): make every remaining CI checkout shallow by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35997](https://github.com/BerriAI/litellm/pull/35997)
- fix(auto-router): stop the embedding model's context window from failing long requests by [@&#8203;akapur99](https://github.com/akapur99) in [#&#8203;35956](https://github.com/BerriAI/litellm/pull/35956)
- fix(ci): make the env-key doc gate see bare get\_secret and get\_secret\_str reads by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35996](https://github.com/BerriAI/litellm/pull/35996)
- fix(logging): extend secret redaction to records litellm does not emit directly by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35977](https://github.com/BerriAI/litellm/pull/35977)
- test(utils): pin the register\_model replay test to the recorded half by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35994](https://github.com/BerriAI/litellm/pull/35994)
- fix(ci): run every helm test suite, not just the first one per file by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35993](https://github.com/BerriAI/litellm/pull/35993)
- ci: fail the build when a test file or Dockerfile is invoked by no job by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35991](https://github.com/BerriAI/litellm/pull/35991)
- fix(langfuse): stop a collected httpx handler from closing a shared client by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35981](https://github.com/BerriAI/litellm/pull/35981)
- fix(bedrock): grant bedrock:CountTokens in OIDC session policy by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;33145](https://github.com/BerriAI/litellm/pull/33145)
- feat(pre-commit): save full lint output to a per-worktree log file by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36004](https://github.com/BerriAI/litellm/pull/36004)
- feat(ui): match auto-router preset models against deployments' underlying model IDs by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35972](https://github.com/BerriAI/litellm/pull/35972)
- fix(core\_helpers): map generic 'error' finish\_reason to 'stop' by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;33972](https://github.com/BerriAI/litellm/pull/33972)
- fix(proxy)!: apply request-parameter checks consistently across body, path and form inputs by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36011](https://github.com/BerriAI/litellm/pull/36011)
- fix: rebuild models\_by\_provider in add\_known\_models so cost map reloads reach wildcard expansion by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36010](https://github.com/BerriAI/litellm/pull/36010)
- feat(complexity\_router): report LLM classifier cost per request via routing\_decision and x-litellm-classifier-cost header by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36015](https://github.com/BerriAI/litellm/pull/36015)
- fix(model-prices): correct replicate model key typo by [@&#8203;AkashNaickar](https://github.com/AkashNaickar) in [#&#8203;34800](https://github.com/BerriAI/litellm/pull/34800)
- fix(proxy): register managed batch output files on terminal retrieve by [@&#8203;Souravrajvi0](https://github.com/Souravrajvi0) in [#&#8203;34092](https://github.com/BerriAI/litellm/pull/34092)
- perf(pre-commit): fetch basedpyright base counts from CI artifacts by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35970](https://github.com/BerriAI/litellm/pull/35970)
- fix(ui): sync projects list page index to ?page= so back and reload keep the page by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36003](https://github.com/BerriAI/litellm/pull/36003)
- fix(ui): link project page keys to their virtual key detail by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36002](https://github.com/BerriAI/litellm/pull/36002)
- refactor(ui): drop unreferenced locals from dashboard route components by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35819](https://github.com/BerriAI/litellm/pull/35819)
- fix(ui): opening a project now pushes ?project= so back and deep links work by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36001](https://github.com/BerriAI/litellm/pull/36001)
- refactor(ui): drop unreferenced locals from shared dashboard components by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35821](https://github.com/BerriAI/litellm/pull/35821)
- refactor(ui): drop unreferenced locals from tests and narrow destructures by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36025](https://github.com/BerriAI/litellm/pull/36025)
- fix(guardrails): allow litellm\_content\_filter to run on post\_mcp\_call by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35980](https://github.com/BerriAI/litellm/pull/35980)
- fix(guardrails): scan /v1/messages tool traffic by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35999](https://github.com/BerriAI/litellm/pull/35999)
- refactor(ui): drop dead locals and unused React state across the dashboard by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36026](https://github.com/BerriAI/litellm/pull/36026)
- feat(ui): add the auto-router usage tab to cost optimization by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35995](https://github.com/BerriAI/litellm/pull/35995)
- fix(managed\_files): derive unified output file ids deterministically so concurrent registrations converge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36019](https://github.com/BerriAI/litellm/pull/36019)
- fix(proxy): send keepalive pings on anthropic messages SSE streams during upstream silence by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36024](https://github.com/BerriAI/litellm/pull/36024)
- fix(managed\_files): return unified ids from unscoped file listing by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36031](https://github.com/BerriAI/litellm/pull/36031)
- fix(arize\_phoenix): lowercase OTLP/gRPC auth metadata key by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34883](https://github.com/BerriAI/litellm/pull/34883)
- fix(auto-router): accept every reminder marker pair a harness emits by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36029](https://github.com/BerriAI/litellm/pull/36029)
- fix(pricing): sync flex/priority tier keys to dated OpenAI snapshot variants by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35923](https://github.com/BerriAI/litellm/pull/35923)
- fix(cost): bill reasoning tokens at the service tier output rate by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35925](https://github.com/BerriAI/litellm/pull/35925)
- fix(proxy): include today's UTC bucket when a daily activity range ends at the caller's current day by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36051](https://github.com/BerriAI/litellm/pull/36051)
- fix: expired-miss share over all measured turns + cost-optimization tab labels by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36037](https://github.com/BerriAI/litellm/pull/36037)
- fix(router): include Bedrock batch/S3 fields and model in deployment credentials by [@&#8203;mpcusack-altos](https://github.com/mpcusack-altos) in [#&#8203;24548](https://github.com/BerriAI/litellm/pull/24548)
- fix(batch): track cost for managed batches with no attributable key/u… by [@&#8203;elinacse](https://github.com/elinacse) in [#&#8203;35468](https://github.com/BerriAI/litellm/pull/35468)
- feat(guardrails): add scan\_only\_tool\_results to scope unified guardrails to tool results by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36014](https://github.com/BerriAI/litellm/pull/36014)
- fix(cost): stop token-pricing the placeholder input on file content calls by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35140](https://github.com/BerriAI/litellm/pull/35140)
- fix(proxy): fetch background responses through the router in CheckResponsesCost by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35137](https://github.com/BerriAI/litellm/pull/35137)
- fix(proxy): yaml store\_prompts\_in\_spend\_logs should take precedence over DB cached value by [@&#8203;Praveena-617](https://github.com/Praveena-617) in [#&#8203;35769](https://github.com/BerriAI/litellm/pull/35769)
- fix(lint): measure the basedpyright budget gate in a gate-owned venv by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36050](https://github.com/BerriAI/litellm/pull/36050)
- docs: cap all GitHub comments at 15-25 words, curb semicolon splices by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36059](https://github.com/BerriAI/litellm/pull/36059)
- chore(lint): name MappingProxyType in the mutable-collection fix messages by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36072](https://github.com/BerriAI/litellm/pull/36072)
- test: roll back runtime model registrations between tests by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36039](https://github.com/BerriAI/litellm/pull/36039)
- refactor(types): cut 653 implicit and explicit Any diagnostics across 11 modules by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36054](https://github.com/BerriAI/litellm/pull/36054)
- fix(proxy): stop resolving the UI session sentinel team on /search\_tools/list by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36061](https://github.com/BerriAI/litellm/pull/36061)
- fix(batches): persist managed file ids for cancelled/failed/expired batches by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36048](https://github.com/BerriAI/litellm/pull/36048)
- fix(batches): register managed output files on batch cancel by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36034](https://github.com/BerriAI/litellm/pull/36034)
- fix(proxy): allow non-admins to reach /user/daily/activity/aggregated by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36062](https://github.com/BerriAI/litellm/pull/36062)
- fix(anthropic): coerce explicit additionalProperties to false in output\_format schema by [@&#8203;dkindlund](https://github.com/dkindlund) in [#&#8203;35811](https://github.com/BerriAI/litellm/pull/35811)
- fix(batches): prevent managed file fallbacks by [@&#8203;rimysore](https://github.com/rimysore) in [#&#8203;35371](https://github.com/BerriAI/litellm/pull/35371)
- chore: ignore the mechanical lint and typing sweeps in git blame by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36076](https://github.com/BerriAI/litellm/pull/36076)
- fix(proxy): warn at startup when max\_budget is set but no database is connected by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36041](https://github.com/BerriAI/litellm/pull/36041)
- fix(proxy): promote caller metadata trace fields into litellm\_metadata by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35866](https://github.com/BerriAI/litellm/pull/35866)
- feat(terraform): sync provider 0.3.0 from the mirror and cut 0.4.0 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36098](https://github.com/BerriAI/litellm/pull/36098)
- fix(guardrails): honor configured timeout in Zscaler AI Guard by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36110](https://github.com/BerriAI/litellm/pull/36110)
- fix(logging): fall back to litellm\_metadata when metadata is empty by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36105](https://github.com/BerriAI/litellm/pull/36105)
- fix(proxy): re-assert the authenticated identity on passthrough requests by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36121](https://github.com/BerriAI/litellm/pull/36121)
- chore: bump litellm-enterprise 0.1.53 -> 0.1.54, litellm-proxy-extras 0.4.83 -> 0.4.84 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36139](https://github.com/BerriAI/litellm/pull/36139)
- fix(ui): match auto-router preset models against wildcard-expanded model groups by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36111](https://github.com/BerriAI/litellm/pull/36111)
- test(router): assert the auto-router max\_input\_chars kwarg by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36109](https://github.com/BerriAI/litellm/pull/36109)
- fix(ui): allow clearing a key's budget reset from the Edit Key form by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36140](https://github.com/BerriAI/litellm/pull/36140)
- fix(managed\_files): skip unparseable rows when listing managed files by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36021](https://github.com/BerriAI/litellm/pull/36021)
- fix(a2a): stop writing per-caller headers onto the shared cached httpx client by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35978](https://github.com/BerriAI/litellm/pull/35978)
- build(deps): bump h2 to 4.4.1 and js-yaml to 4.3.1 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36147](https://github.com/BerriAI/litellm/pull/36147)
- chore: promote staging to main by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36057](https://github.com/BerriAI/litellm/pull/36057)
- fix(azure\_sentinel): respect AZURE\_AUTHORITY\_HOST and derive the Azure Monitor audience per cloud by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36137](https://github.com/BerriAI/litellm/pull/36137)
- fix(bedrock): pass SSE-KMS key through to the batch input-file S3 upload by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35148](https://github.com/BerriAI/litellm/pull/35148)
- fix(anthropic adapter): stop indexing choices\[0] on choiceless streaming chunks by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35314](https://github.com/BerriAI/litellm/pull/35314)
- fix(bedrock): normalize /v1/completions and /v1/responses batch records by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35675](https://github.com/BerriAI/litellm/pull/35675)
- fix(proxy): return the real status code when a credential update is rejected by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36166](https://github.com/BerriAI/litellm/pull/36166)
- fix(proxy): improve Headroom /v1/compress HTTP 404 diagnostics by [@&#8203;aayush598](https://github.com/aayush598) in [#&#8203;35952](https://github.com/BerriAI/litellm/pull/35952)
- fix(proxy): invalidate cached project object on project update and delete by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36028](https://github.com/BerriAI/litellm/pull/36028)
- feat(proxy): add apply\_user\_budget\_to\_team\_keys opt-in by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36102](https://github.com/BerriAI/litellm/pull/36102)
- fix(proxy): stop alerting on health probes that lose the planned engine-restart race by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;36141](https://github.com/BerriAI/litellm/pull/36141)
- test(docker): gate the componentized gateway and backend images on an arbitrary-uid offline boot by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;36136](https://github.com/BerriAI/litellm/pull/36136)
- fix(http): stop pooled clients persisting cookies on the aiohttp jar too by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;36149](https://github.com/BerriAI/litellm/pull/36149)
- fix(router): bound fallback-walk work and error-log volume by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;36148](https://github.com/BerriAI/litellm/pull/36148)
- ci: wire credential\_endpoints tests into the proxy endpoints job by [@&#8203;cursor](https://github.com/cursor)\[bot] in [#&#8203;36187](https://github.com/BerriAI/litellm/pull/36187)
- docs(keys): document /key/info fields and clarify budget\_reset\_at is the next reset by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36127](https://github.com/BerriAI/litellm/pull/36127)
- fix(azure\_sentinel): add AZURE\_SENTINEL\_AUTHORITY\_HOST as a Sentinel scoped override by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36165](https://github.com/BerriAI/litellm/pull/36165)
- docs(pr-template): add a User Flow section with authoring instructions by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36162](https://github.com/BerriAI/litellm/pull/36162)
- fix(proxy): derive config agent ids from agent\_name so grants survive secret rotation by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36020](https://github.com/BerriAI/litellm/pull/36020)
- chore(ui): regenerate schema.d.ts for the /key/info docstring update by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36210](https://github.com/BerriAI/litellm/pull/36210)
- build(deps): bump gitpython to 3.1.58 to clear osv-scan on staging by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36212](https://github.com/BerriAI/litellm/pull/36212)
- fix(proxy): deny agent access when key and team grants resolve to nothing by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36221](https://github.com/BerriAI/litellm/pull/36221)
- build(deps): defer the second pypdf advisory until the 6.15.0 bump by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36218](https://github.com/BerriAI/litellm/pull/36218)
- fix(a2a): align agent list annotation and test with the tuple return type by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36217](https://github.com/BerriAI/litellm/pull/36217)
- ci: always run the UI API types sync check so it can be required by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36213](https://github.com/BerriAI/litellm/pull/36213)
- build(deps): bump nanoid to 3.3.17 in the dashboard lockfile by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36227](https://github.com/BerriAI/litellm/pull/36227)
- feat(ui): show user email or alias in usage data export by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36232](https://github.com/BerriAI/litellm/pull/36232)
- feat(auto-router): track turns per complexity tier (LIT-5302) by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36209](https://github.com/BerriAI/litellm/pull/36209)
- fix(websearch): restore snippet text in native web\_search\_tool\_result blocks (LIT-5315) by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36228](https://github.com/BerriAI/litellm/pull/36228)
- fix(proxy): resolve entity access groups in the model listing endpoints by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36230](https://github.com/BerriAI/litellm/pull/36230)
- fix(ui): let access groups be a team's only model source, with hover provenance by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;36234](https://github.com/BerriAI/litellm/pull/36234)
- fix(managed\_files): return unified output file ids from GET /batches by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36049](https://github.com/BerriAI/litellm/pull/36049)
- test(proxy): compare empty agent list to the tuple get\_agent\_list returns by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36225](https://github.com/BerriAI/litellm/pull/36225)
- fix(otel): name the RPC system and upstream on MCP tool-call spans by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35857](https://github.com/BerriAI/litellm/pull/35857)
- fix(guardrails): chunk oversized Bedrock ApplyGuardrail requests instead of failing by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;36119](https://github.com/BerriAI/litellm/pull/36119)
- test(e2e): settle control-plane writes across every replica, not just one by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36247](https://github.com/BerriAI/litellm/pull/36247)
- fix(responses): forward allowed\_openai\_params through the chat completions bridge by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35885](https://github.com/BerriAI/litellm/pull/35885)
- test(proxy): assert the copy \_add\_team\_member\_budget\_table returns by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36244](https://github.com/BerriAI/litellm/pull/36244)
- chore(ui): regenerate dashboard api types for tier\_turns by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36243](https://github.com/BerriAI/litellm/pull/36243)
- refactor(types): declare mirrored pricing fields on ModelInfo by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36215](https://github.com/BerriAI/litellm/pull/36215)
- fix(lint): make strict-gate noqas survive base ruff and flag stale ones by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36257](https://github.com/BerriAI/litellm/pull/36257)
- fix(vertex\_ai): surface real error/status on vertex batch create instead of IndexError 500 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35141](https://github.com/BerriAI/litellm/pull/35141)
- ci: give the remaining pull\_request workflows a concurrency group by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36252](https://github.com/BerriAI/litellm/pull/36252)
- refactor(lint): graduate zero-violation strict rules and guard the budget ratchet by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36161](https://github.com/BerriAI/litellm/pull/36161)
- fix(proxy): enforce require\_managed\_files on every route that accepts a raw provider id by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35551](https://github.com/BerriAI/litellm/pull/35551)
- chore(typing): clear 1.4k basedpyright Any errors across 21 hotspot files by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36282](https://github.com/BerriAI/litellm/pull/36282)
- test: roll back live router replay membership between tests by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36278](https://github.com/BerriAI/litellm/pull/36278)
- chore(ci): sync main into internal staging by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36288](https://github.com/BerriAI/litellm/pull/36288)
- build(lint): rename make pre-commit to make check with a working-tree fallback by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36277](https://github.com/BerriAI/litellm/pull/36277)
- fix(ui): show team BYOK models in team fallback settings by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36241](https://github.com/BerriAI/litellm/pull/36241)
- fix(otel): mark v2 server spans as failed for pre-call errors by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34546](https://github.com/BerriAI/litellm/pull/34546)
- fix(websearch\_interception): bill intercepted searches to the calling key by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35708](https://github.com/BerriAI/litellm/pull/35708)
- chore: remove pre-commit rule by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36295](https://github.com/BerriAI/litellm/pull/36295)
- docs: clarify guideline priority ordering in CLAUDE.md by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36296](https://github.com/BerriAI/litellm/pull/36296)
- feat(router): independent, default-on deployment affinity for the auto-router by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;36146](https://github.com/BerriAI/litellm/pull/36146)
- test: repair stale CircleCI contracts by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36293](https://github.com/BerriAI/litellm/pull/36293)
- chore(ci): promote internal staging to main by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36286](https://github.com/BerriAI/litellm/pull/36286)
- chore: rebuild Admin UI bundle for the 2026-08-08 release by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36297](https://github.com/BerriAI/litellm/pull/36297)
- chore(ci): promote internal staging to main by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;36304](https://github.com/BerriAI/litellm/pull/36304)

##### New Contributors

- [@&#8203;rimysore](https://github.com/rimysore) made their first contribution in [#&#8203;35367](https://github.com/BerriAI/litellm/pull/35367)
- [@&#8203;AkashNaickar](https://github.com/AkashNaickar) made their first contribution in [#&#8203;34800](https://github.com/BerriAI/litellm/pull/34800)
- [@&#8203;Souravrajvi0](https://github.com/Souravrajvi0) made their first contribution in [#&#8203;34092](https://github.com/BerriAI/litellm/pull/34092)
- [@&#8203;elinacse](https://github.com/elinacse) made their first contribution in [#&#8203;35468](https://github.com/BerriAI/litellm/pull/35468)
- [@&#8203;aayush598](https://github.com/aayush598) made their first contribution in [#&#8203;35952](https://github.com/BerriAI/litellm/pull/35952)
- [@&#8203;cursor](https://github.com/cursor)\[bot] made their first contribution in [#&#8203;36187](https://github.com/BerriAI/litellm/pull/36187)

**Full Changelog**: <https://github.com/BerriAI/litellm/compare/v1.96.0...v1.97.0>

### [`v1.97.0`](https://github.com/BerriAI/litellm/releases/tag/v1.97.0)

[Compare Source](https://github.com/BerriAI/litellm/compare/v1.96.2...v1.97.0)

##### Verify Docker Image Signature

All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](https://github.com/BerriAI/litellm/commit/0112e53046018d726492c814b3644b7d376029d0).

**Verify using the pinned commit hash (recommended):**

A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key:

```bash
cosign verify \
  --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \
  ghcr.io/berriai/litellm:v1.97.0
```

**Verify using the release tag (convenience):**

Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules:

```bash
cosign verify \
  --key https://raw.githubusercontent.com/BerriAI/litellm/v1.97.0/cosign.pub \
  ghcr.io/berriai/litellm:v1.97.0
```

Expected output:

```
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key
```

***

##### What's Changed

- feat(proxy): resolve Cursor thinking/fast model-name suffixes on /cursor/chat/completions by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35554](https://github.com/BerriAI/litellm/pull/35554)
- fix(team-callbacks): actually stop logging when disable\_logging is called by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35520](https://github.com/BerriAI/litellm/pull/35520)
- refactor(lint): drop redundant !s f-string conversion flags and fix displaced import-group comments by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35546](https://github.com/BerriAI/litellm/pull/35546)
- fix(proxy): backfill null user\_email on existing users during JWT auth by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34588](https://github.com/BerriAI/litellm/pull/34588)
- feat(playground): add non-streaming response toggle by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35560](https://github.com/BerriAI/litellm/pull/35560)
- feat(teams): apply default organization to new teams from default team settings by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35540](https://github.com/BerriAI/litellm/pull/35540)
- fix(ui): block Playground page for viewer roles on direct URL access by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35676](https://github.com/BerriAI/litellm/pull/35676)
- fix(caching): close evicted LLM clients so their connections are reclaimed by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35492](https://github.com/BerriAI/litellm/pull/35492)
- chore(deps): update brace-expansion, postcss, and gitpython to current patch releases by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35692](https://github.com/BerriAI/litellm/pull/35692)
- refactor(ui): rename the create MCP server component to PascalCase by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35686](https://github.com/BerriAI/litellm/pull/35686)
- fix(openai): drop undefined Union from owns\_wrapped\_http\_client annotation by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35706](https://github.com/BerriAI/litellm/pull/35706)
- fix(openai): drop the undefined Union from owns\_wrapped\_http\_client by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35704](https://github.com/BerriAI/litellm/pull/35704)
- chore(ui): note Google's Agent Platform rename in vector store setup by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;28076](https://github.com/BerriAI/litellm/pull/28076)
- fix(proxy): apply key/team router\_settings.model\_group\_alias by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;35486](https://github.com/BerriAI/litellm/pull/35486)
- feat(complexity\_router): default session affinity off and expose it in the UI by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35714](https://github.com/BerriAI/litellm/pull/35714)
- fix(datadog): read team callback dd\_\* params from kwargs instead of blocked dynamic params ([#&#8203;35115](https://github.com/BerriAI/litellm/issues/35115) port) by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35687](https://github.com/BerriAI/litellm/pull/35687)
- refactor(ui): extract the MCP create form's logic and field groups by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35694](https://github.com/BerriAI/litellm/pull/35694)
- test(ui): tier the MCP create tests into unit and integration by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35697](https://github.com/BerriAI/litellm/pull/35697)
- fix(proxy): redact credential headers from request logging copies by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35678](https://github.com/BerriAI/litellm/pull/35678)
- feat(guardrails/rubrik): prompt moderation, response-text blocking, streaming buffer, failure logging by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35722](https://github.com/BerriAI/litellm/pull/35722)
- fix(ui): render Responses API request and response in the logs drawer by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35718](https://github.com/BerriAI/litellm/pull/35718)
- fix(ui): hide guardrail review buttons from non-admin users by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;27535](https://github.com/BerriAI/litellm/pull/27535)
- feat(team): custom metadata validation hook for team create and update by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;33353](https://github.com/BerriAI/litellm/pull/33353)
- ci(circleci): install a pinned Rust toolchain on the Linux jobs by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35519](https://github.com/BerriAI/litellm/pull/35519)
- fix(bedrock): stop forwarding no-op toolSpec.strict to Converse by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35688](https://github.com/BerriAI/litellm/pull/35688)
- fix(ui): reject an auto-router keyword rule left empty instead of dropping it by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35705](https://github.com/BerriAI/litellm/pull/35705)
- fix(guardrails/rubrik): attribute blocked requests to the caller that made them by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;35734](https://github.com/BerriAI/litellm/pull/35734)
- fix(responses): forward client headers to the provider on /v1/responses by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34531](https://github.com/BerriAI/litellm/pull/34531)
- feat(spend): add net auto-router savings to the cost-optimization dashboard by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35521](https://github.com/BerriAI/litellm/pull/35521)
- chore(typing): clear basedpyright Any errors in budget reset, access groups, and cache settings by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;35719](https://github.com/BerriAI/litellm/pull/35719)
- fix(spend): read what a request cost from the record instead of pricing it again by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35736](https://github.com/BerriAI/litellm/pull/35736)
- perf: install hiredis so redis-py parses replies with its C parser by [@&#8203;Classic298](https://github.com/Classic298) in [#&#8203;35709](https://github.com/BerriAI/litellm/pull/35709)
- feat(ui): show auto-router savings on the cost-optimization dashboard by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35522](https://github.com/BerriAI/litellm/pull/35522)
- perf: build log messages lazily so filtered-out log records cost nothing by [@&#8203;Classic298](https://github.com/Classic298) in [#&#8203;35703](https://github.com/BerriAI/litellm/pull/35703)
- fix(proxy): retry model cost map fetch with Retry-After-aware backoff and keep current map on reload failure by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;35739](https://github.com/BerriAI/litellm/pull/35739)
- feat(otel): stamp service tier attributes on inference spans by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35679](https://github.com/BerriAI/litellm/pull/35679)
- fix(proxy): log the model cost map reload failure lazily by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;35750](https://github.com/BerriAI/litellm/pull/35750)
- fix(groq): translate web\_search\_options to the browser\_search tool by [@&#8203;hMED22](https://github.com/hMED22) in [#&#8203;34971](https://github.com/BerriAI/litellm/pull/34971)
- feat(ui): add admin-configurable user banner by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35729](https://github.com/BerriAI/litellm/pull/35729)
- fix(e2e): make spend-counter redis connection env-driven for non-cluster deployments by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;35732](https://github.com/BerriAI/litellm/pull/35732)
- fix(proxy): make /cursor/chat/completions work with Cursor agent mode by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;34029](https://github.com/BerriAI/litellm/pull/34029)
- fix(proxy): propagate user\_email and bind api\_key on JWT auth attribution paths by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34331](https://github.com/BerriAI/litellm/pull/34331)
- chore(build): move the Admin UI toolchain to Node 24 by [@&#8203;yuneng-berri](https://g…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants