chore(release): backport #30380, #30503, #30558, #30130, #30588, #30495, #30690 to stable/1.89.x and cut 1.89.2#30681
Conversation
|
|
Greptile SummaryThis is a well-structured stable-branch backport that cherry-picks seven already-merged fixes onto
Confidence Score: 5/5Safe to merge — all seven cherry-picks are narrowly scoped bugfixes, each backed by dedicated mock-only unit tests, with zero new failures in the 908-test delta run. Every changed code path has a direct test. The service_tier coercion guard, vector store route classification, team-name translation, healthy_only filter, and BYOK credential resolution all follow the existing codebase conventions and are well-isolated. No backwards-incompatible changes, no new DB queries in the critical path, and no FastAPI imports outside the proxy directory. health_state_cache is always initialized in Router.init so async_get_fully_unhealthy_model_names cannot raise AttributeError. The team_id query parameter added to model_info is secured by validate_membership when the database is present, and has no access-expansion effect when it is not. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/cost_calculator.py | Adds service_tier passthrough to anthropic_cost_per_token and guards non-string/auto service_tier values with a null coercion before pricing; logic is correct and well-tested. |
| litellm/integrations/otel/emitter.py | Adds a standard OTel exception event so backends don't truncate error messages via dynamic keyword mapping; clean additive change. |
| litellm/proxy/proxy_server.py | Adds healthy_only filter and team-public-model-name translation to /v1/models and /v1/models/{id}; logic is correct, membership check on team_id is properly guarded by DB availability. |
| litellm/proxy/common_utils/model_listing_utils.py | New TeamModelNameTranslator utility mapping internal routing keys to public team names; stateless, well-tested, includes backward-compat opt-out flag. |
| litellm/proxy/openai_files_endpoints/common_utils.py | Adds get_team_provider_credentials that resolves BYOK credentials scoped to the team's allowlist; priority order (own BYOK → team-accessible) is correct and access is gated to team models. |
| litellm/router.py | Adds async_get_fully_unhealthy_model_names; correctly uses health_state_cache (always initialized in init), aggregates both model_name and team_public_model_name, fails open when no health state exists. |
| litellm/proxy/_types.py | Adds /vector_stores/{vector_store_id} and /v1/vector_stores/{vector_store_id} to LLM API routes so internal roles can reach CRUD operations. |
Reviews (2): Last reviewed commit: "chore: refresh uv.lock for 1.89.2" | Re-trigger Greptile
| internal_to_public = TeamModelNameTranslator.build_internal_to_public_map( | ||
| llm_router, settings | ||
| ) | ||
| resolved_model_id = TeamModelNameTranslator.resolve_public_name( | ||
| model_id=model_id, | ||
| available_models=all_models, | ||
| llm_router=llm_router, | ||
| general_settings=settings, | ||
| ) |
There was a problem hiding this comment.
build_internal_to_public_map (which calls llm_router.get_model_list()) is invoked twice on every GET /v1/models/{id} request: once explicitly to populate internal_to_public, and again inside resolve_public_name. You can pass the already-built map into resolve_public_name to avoid the redundant traversal, or simply reuse the first map to drive the public-name lookup directly.
| internal_to_public = TeamModelNameTranslator.build_internal_to_public_map( | |
| llm_router, settings | |
| ) | |
| resolved_model_id = TeamModelNameTranslator.resolve_public_name( | |
| model_id=model_id, | |
| available_models=all_models, | |
| llm_router=llm_router, | |
| general_settings=settings, | |
| ) | |
| internal_to_public = TeamModelNameTranslator.build_internal_to_public_map( | |
| llm_router, settings | |
| ) | |
| # Re-use the already-built map instead of rebuilding it inside resolve_public_name. | |
| public_to_internal = TeamModelNameTranslator._response_to_lookup_map( | |
| all_models, internal_to_public | |
| ) | |
| resolved_model_id = public_to_internal.get(model_id, model_id) |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| team_id: Optional[str] = None, | ||
| healthy_only: Optional[bool] = False, |
There was a problem hiding this comment.
healthy_only is silently a no-op on this stable branch (1.89.x lacks async_get_fully_unhealthy_model_names on Router). Callers passing ?healthy_only=true will receive a 200 with no filtering, which is silent rather than explicit. The PR notes document this intentionally, but a comment in the function body (similar to the existing docstring note) would help future maintainers avoid accidentally wiring this param to a stub that raises.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
| # against the team's own account (e.g. the team's openai deployment). | ||
| team_credentials = get_team_provider_credentials( | ||
| llm_router=llm_router, | ||
| team_models=user_api_key_dict.team_models or [], |
There was a problem hiding this comment.
High: Key model restrictions bypassed for provider file listing
get_team_provider_credentials() is only given team_models, so a virtual key restricted to a subset of the team's models can call GET /v1/files?provider=openai without target_model_names and still have credentials selected from the team's OpenAI deployment. Use the effective key-scoped model set here, or require and validate a target model before resolving provider credentials.
| # vector stores | ||
| "/vector_stores", | ||
| "/v1/vector_stores", | ||
| "/vector_stores/{vector_store_id}", |
There was a problem hiding this comment.
Medium: View-only users can mutate vector stores
Adding the bare vector-store route to openai_routes makes RouteChecks.is_llm_api_route() return true for GET, POST, and DELETE on /v1/vector_stores/{vector_store_id}. That path is allowed before the view-only write checks run, so an INTERNAL_USER_VIEW_ONLY key can now update or delete vector stores it can access; keep this out of the blanket LLM route group or make the route check method-aware so only reads are allowed for view-only roles.
PR overviewThis release backport pulls several changes into stable/1.89.x for the 1.89.2 cut, including updates around OpenAI-compatible file/provider handling and vector store route definitions in the proxy. There are still open authorization issues in the proxy paths touched by this backport. A key scoped to limited models may be able to use provider file listing through broader team credentials, and view-only users may be able to update or delete accessible vector stores due to route classification. No issues have been addressed yet, so these access-control gaps remain the current security focus before release. Open issues (2)
Fixed/addressed: 0 · PR risk: 7/10 |
…tel v2 (#30380) The v2 span engine only stamped error.type and stuffed the message into the span status description; it never recorded the standard OTel exception event. Backends that dynamic-map unknown string fields (e.g. Elasticsearch) index the message as a keyword capped at ignore_above:1024, truncating it. Emit the full message under the recognized exception.message semconv field via a span event so it is mapped as full text instead. Co-authored-by: Claude <noreply@anthropic.com> (cherry picked from commit 3b84150)
* feat: add opt-in healthy_only filter to GET /v1/models Adds an opt-in `healthy_only=true` query parameter to GET /v1/models and GET /models that hides models whose backing deployments are all marked unhealthy by background health checks. - Add Router.async_get_fully_unhealthy_model_names(), mirroring the semantics of get_fully_blocked_model_names(): a model is hidden only when every backing deployment is unhealthy and the health state is not stale (fail open otherwise). - Reuses the existing DeploymentHealthCache populated by _run_background_health_check(), so no new health state is introduced. - No-op when allowed_fails_policy is set, mirroring _async_filter_health_check_unhealthy_deployments semantics. - team_public_model_name aliases are aggregated alongside model_name. - Hiding is presentation-only; default behavior is unchanged. Fixes #30128 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: address Greptile review notes - Note team-alias asymmetry vs get_fully_blocked_model_names - Debug-log when healthy_only is set but no health state is available Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: shin-berri <shin-laptop@berri.ai> Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 9dd9d23)
* fix(proxy): optionally surface public team model name in /v1/models
Behind general_settings.use_team_public_model_name (default False). When
enabled, /v1/models and /models surface the public team_public_model_name
for team-scoped (BYOK) models instead of the internal routing key
model_name_{team_id}_{uuid} -- consistent with /v1/model/info and
OpenAI-compatible. Off by default so the listing's model ids stay
backward-compatible for callers that scripted against the internal name;
routing by the internal name is unchanged regardless of the flag.
Presentation-layer only: access-group, auth, and routing semantics are
unchanged; non-team models are pass-through.
* fix(proxy): default team model listings to public names
* test(proxy): cover team model listing metadata
* test(proxy): cover empty team listing deployments
* refactor(proxy): simplify team model listing translation
* fix(proxy): resolve public team model name on GET /v1/models/{id}
The listing endpoints advertise team_public_model_name, but the retrieve
endpoint validated and looked up by the raw id, so a public name 404'd.
Resolve the public name back to the internal routing key (scoped to the
caller's accessible models so colliding names never cross teams), look up
by it, and echo the public name back as the response id.
* test(proxy): cover public-name resolution on model retrieve
* refactor(proxy): extract team model-name translation into TeamModelNameTranslator
Move the team-scoped (BYOK) listing/retrieve name translation out of
proxy_server.py into a dedicated common_utils module. Static methods with
general_settings injected so the logic is unit-testable without globals and
proxy_server.py stays thin.
* refactor(proxy): use TeamModelNameTranslator in model_list and model_info
* test(proxy): target TeamModelNameTranslator for model-name translation
* fix(proxy): type create_model_info_response return as dict[str, object]
* fix(proxy): keep internal routing key for team model listing metadata lookup
Add listing_entries returning (public response id, internal lookup id) so
include_metadata=true resolves fallbacks against the routing key the router
indexes by, instead of the translated public name (which never matches).
* fix(proxy): build /v1/models metadata from internal key, show public id
* test(proxy): cover team listing fallback metadata via internal key
* fix(proxy): use builtin dict generics in create_model_info_response (UP006)
---------
Co-authored-by: Tushar More <tusharmore8408@gmail.com>
Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
(cherry picked from commit 60f4c01)
…#30495) * fix(proxy): resolve list files credentials from team BYOK deployments GET /v1/files without target_model_names now prefers the team's own deployment (model_info.team_id) over shared global provider keys, so JWT team auth lists files against the correct upstream account. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(proxy): scope list files credential lookup to team allowlist Remove the unrestricted deployment scan that could leak global provider keys to teams without access, normalize all-proxy-models to the team-scoped model list, and fix TID251 violations by using dict instead of Dict/Any. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 6c8b60d)
…racking (#30690) completion_cost read service_tier straight from the request optional_params and called service_tier.lower() on it, so a non-string value (dict/int/list, reachable via allowed_openai_params/drop_params) raised AttributeError. _response_cost_calculator swallowed that and returned response_cost=None, so the request's cost was silently lost. The isinstance guard alone is not enough: a surviving dict would crash again downstream in _get_service_tier_cost_key, which also calls .lower(). A request-level service_tier is only meaningful for pricing when it is a concrete billable tier string, so coerce any non-string value to None and defer to the tier the provider reports on the response usage, the same way "auto" already does. Adds a regression test driving a dict service_tier through completion_cost; it raises AttributeError before the fix and prices at the served tier after. (cherry picked from commit 43dadc5)
e142b28 to
ad758c9
Compare
|
@greptile |
|
@veria-ai review |
….2) (#327) 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)) | patch | `v1.89.1` → `v1.89.2` | --- ### Release Notes <details> <summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary> ### [`v1.89.2`](https://github.com/BerriAI/litellm/releases/tag/v1.89.2) [Compare Source](BerriAI/litellm@v1.89.2...v1.89.2) ##### 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`](BerriAI/litellm@0112e53). **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.89.2 ``` **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.89.2/cosign.pub \ ghcr.io/berriai/litellm:v1.89.2 ``` 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 - chore(ui): rebuild ui by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30703](BerriAI/litellm#30703) - chore(release): backport [#​30380](BerriAI/litellm#30380), [#​30503](BerriAI/litellm#30503), [#​30558](BerriAI/litellm#30558), [#​30130](BerriAI/litellm#30130), [#​30588](BerriAI/litellm#30588), [#​30495](BerriAI/litellm#30495), [#​30690](BerriAI/litellm#30690) to stable/1.89.x and cut 1.89.2 by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30681](BerriAI/litellm#30681) **Full Changelog**: <BerriAI/litellm@v1.89.1...v1.89.2> ### [`v1.89.2`](https://github.com/BerriAI/litellm/releases/tag/v1.89.2) [Compare Source](BerriAI/litellm@v1.89.1...v1.89.2) ##### 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`](BerriAI/litellm@0112e53). **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.89.2 ``` **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.89.2/cosign.pub \ ghcr.io/berriai/litellm:v1.89.2 ``` 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 - chore(ui): rebuild ui by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30703](BerriAI/litellm#30703) - chore(release): backport [#​30380](BerriAI/litellm#30380), [#​30503](BerriAI/litellm#30503), [#​30558](BerriAI/litellm#30558), [#​30130](BerriAI/litellm#30130), [#​30588](BerriAI/litellm#30588), [#​30495](BerriAI/litellm#30495), [#​30690](BerriAI/litellm#30690) to stable/1.89.x and cut 1.89.2 by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30681](BerriAI/litellm#30681) **Full Changelog**: <BerriAI/litellm@v1.89.1...v1.89.2> </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMjQuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIyNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/327
…to v1.89.2 (#206) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [https://github.com/BerriAI/litellm.git](https://github.com/BerriAI/litellm) | patch | `v1.89.1` → `v1.89.2` | --- ### Release Notes <details> <summary>BerriAI/litellm (https://github.com/BerriAI/litellm.git)</summary> ### [`v1.89.2`](https://github.com/BerriAI/litellm/releases/tag/v1.89.2) [Compare Source](BerriAI/litellm@v1.89.1...v1.89.2) #### 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`](BerriAI/litellm@0112e53). **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.89.2 ``` **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.89.2/cosign.pub \ ghcr.io/berriai/litellm:v1.89.2 ``` 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 - chore(ui): rebuild ui by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30703](BerriAI/litellm#30703) - chore(release): backport [#​30380](BerriAI/litellm#30380), [#​30503](BerriAI/litellm#30503), [#​30558](BerriAI/litellm#30558), [#​30130](BerriAI/litellm#30130), [#​30588](BerriAI/litellm#30588), [#​30495](BerriAI/litellm#30495), [#​30690](BerriAI/litellm#30690) to stable/1.89.x and cut 1.89.2 by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30681](BerriAI/litellm#30681) **Full Changelog**: <BerriAI/litellm@v1.89.1...v1.89.2> </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMjAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIyMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=--> Co-authored-by: Renovate Bot <renovate@bhamm-lab.com> Reviewed-on: https://codeberg.org/blake-hamm/bhamm-lab/pulls/206
….2) (#143) 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)) | patch | `v1.89.1` → `v1.89.2` | --- ### Release Notes <details> <summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary> ### [`v1.89.2`](https://github.com/BerriAI/litellm/releases/tag/v1.89.2) [Compare Source](BerriAI/litellm@v1.89.2...v1.89.2) ##### 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`](BerriAI/litellm@0112e53). **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.89.2 ``` **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.89.2/cosign.pub \ ghcr.io/berriai/litellm:v1.89.2 ``` 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 - chore(ui): rebuild ui by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30703](BerriAI/litellm#30703) - chore(release): backport [#​30380](BerriAI/litellm#30380), [#​30503](BerriAI/litellm#30503), [#​30558](BerriAI/litellm#30558), [#​30130](BerriAI/litellm#30130), [#​30588](BerriAI/litellm#30588), [#​30495](BerriAI/litellm#30495), [#​30690](BerriAI/litellm#30690) to stable/1.89.x and cut 1.89.2 by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30681](BerriAI/litellm#30681) **Full Changelog**: <BerriAI/litellm@v1.89.1...v1.89.2> ### [`v1.89.2`](https://github.com/BerriAI/litellm/releases/tag/v1.89.2) [Compare Source](BerriAI/litellm@v1.89.1...v1.89.2) ##### 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`](BerriAI/litellm@0112e53). **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.89.2 ``` **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.89.2/cosign.pub \ ghcr.io/berriai/litellm:v1.89.2 ``` 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 - chore(ui): rebuild ui by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30703](BerriAI/litellm#30703) - chore(release): backport [#​30380](BerriAI/litellm#30380), [#​30503](BerriAI/litellm#30503), [#​30558](BerriAI/litellm#30558), [#​30130](BerriAI/litellm#30130), [#​30588](BerriAI/litellm#30588), [#​30495](BerriAI/litellm#30495), [#​30690](BerriAI/litellm#30690) to stable/1.89.x and cut 1.89.2 by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​30681](BerriAI/litellm#30681) **Full Changelog**: <BerriAI/litellm@v1.89.1...v1.89.2> </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/London) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMzIuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIzMi4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://forgejo.hayden.moe/hayden/phoebe/pulls/143
Relevant issues
Backports seven already-merged fixes onto
stable/1.89.xand cuts1.89.2. The tip of the line,1.89.1, has already been released, so this PR carries the next patch bump. Every commit is agit cherry-pick -xof the corresponding staging squash; the only non-pick commits are the version bump and theuv.lockrefresh.What is included, in merge order:
exceptionevent in otel v2, so backends that dynamic-map unknown fields stop truncating it/vector_stores/{vector_store_id}and/v1/vector_stores/{vector_store_id}routes as LLM API routes so internal user and internal viewer roles can retrieve, update, and delete vector stores, consistent with searchservice_tieroff the Anthropic response usage, surface it in the usage block, and price tier-specific costs through the anthropic cost pathhealthy_onlyfilter toGET /v1/models; included here as the prerequisite for fix(proxy): list public team model name in /v1/models #30588, which callsRouter.async_get_fully_unhealthy_model_namesintroduced by this PR. Without it,GET /v1/models/{id}?healthy_only=truewould raise an AttributeError on this line. See the provenance note below/v1/modelsand/v1/models/{id}for team-scoped (BYOK) deployments, while routing and metadata lookups stay on the internal keyGET /v1/files, scoped to the team allowlist, instead of falling back to shared global provider keysservice_tierto None incompletion_costso a dict/int/list value can no longer raiseAttributeErrorand silently drop cost tracking; builds on fix(anthropic): price and surface response service_tier in cost tracking #30558 and sits on top of itPlus
bump: version 1.89.1 -> 1.89.2andchore: refresh uv.lock for 1.89.2.Linear ticket
N/A
Pre-Submission checklist
Screenshots / Proof of Fix
All run against a live proxy on this branch (worktree loads the picked 1.89.x source). Targeted unit tests for every picked PR, run as a delta against the line baseline: the targeted files on
stable/1.89.xbefore any pick were 760 passed andtest_router.pywas 91 passed (851 total, 0 failed); after the seven picks the full set, including the newtest_model_list_healthy_only.py, is 908 passed / 2 skipped / 0 failed. Zero new failures.#30130 / #30588 prerequisite --
GET /v1/models/{id}?healthy_only=trueno longer raises AttributeError, driven against the real endpoint function with a real Router:#30558 -- Anthropic response surfaces
service_tierand tracks cost:#30503 -- internal_user role reaches the vector store retrieve handler (auth allowed) while still blocked on an admin route:
#30690 -- the non-string
service_tierguard is a cost-calculation path: real providers reject a dictservice_tierat the API boundary (Anthropic returns "service_tier: Input should be 'auto' or 'standard_only'"), so the regression is exercised by the added unit test that drives a dictservice_tierstraight throughcompletion_cost(raisesAttributeErrorbefore the fix, prices at the served tier after). That test passes in the post-pick run.#30380, #30495 are proven by their added unit tests in the same 908-passed delta (otel telemetry, and team BYOK file-credential resolution, need specific backend/team wiring to show live; their behaviour is covered by the picked tests).
Gauntlet (behavioral, deep, universal): SURVIVED. All three sub-claims held: every referenced identifier resolves (including
Router.async_get_fully_unhealthy_model_namesfrom #30130, somodel_info(healthy_only=True)returns cleanly), every added test passes as a delta with zero new failures, and no existing caller of a modified function breaks. Zero verdict-flipping findings. The run surfaced one pre-existing condition for transparency: a non-stringservice_tiercarried on the response usage object (not requestoptional_params) can still raise in_get_service_tier_cost_key; this was verified to predate all seven picks onstable/1.89.x, so it is upstream-equivalent and not introduced by this backport, and it sits outside #30690's request-side scope.Type
🐛 Bug Fix
Changes
Seven cherry-picks (five verbatim, two adapted) plus the bump and lock. #30130 is the prerequisite for #30588 and carries a provenance note; the two adapted picks differ from staging only in test-file context drift from intermediate staging commits not on this line, never in a pick's own logic.
Provenance note -- #30130
#30130 merged to an oss-staging branch (
litellm_oss_110626); its merge commit is not directly reachable fromlitellm_internal_staging(its content reached internal staging via an aggregator squash). The discrete oss commit was cherry-picked and content-verified against internal staging:Router.async_get_fully_unhealthy_model_namesis byte-identical to staging, andtest_model_list_healthy_only.pyis the same blob. Runtime closure was confirmed on the branch: the method resolves on a real Router andmodel_info(healthy_only=True)returns cleanly.Adaptation notes
#30558: in
test_spend_management_endpoints.pytheignored_keysconflict was resolved by adding only #30558's ownmetadata.additional_usage_values.service_tierentry and dropping the neighbouring...iterationsentry, which is not part of #30558 and whose handling does not exist in this line's anthropic transformation. All source hunks applied verbatim.#30588: applied verbatim once #30130 precedes it (the prior
blocked_namesvshidden_namesdivergence was entirely an artifact of the missing prerequisite).#30495: in
test_files_endpoint.pythe 5 newtest_list_files_*functions were kept verbatim and re-anchored after this line's actual last test; the 5 staging-onlytest_require_managed_files_*functions the merge pulled into the conflict (added to staging by an unpicked PR, absent here) were dropped. Both source files applied verbatim and all router-method dependencies resolve on this line.Known noise on this line
The targeted baseline on
stable/1.89.xwas clean (851 passed / 0 failed across the existing files), so there is no pre-existing red to discount. One pre-existingruff PLR0915inlitellm/types/utils.py(a long__init__untouched by these picks) is present on the line before and after; the picks add no new lint violations.