Skip to content

chore(release): backport #30480, #30543, #30542, #30573 to stable/1.89.x and cut 1.89.3 - #30888

Merged
yuneng-berri merged 6 commits into
stable/1.89.xfrom
litellm_backport_1_89_x_0620
Jun 20, 2026
Merged

chore(release): backport #30480, #30543, #30542, #30573 to stable/1.89.x and cut 1.89.3#30888
yuneng-berri merged 6 commits into
stable/1.89.xfrom
litellm_backport_1_89_x_0620

Conversation

@yuneng-berri

Copy link
Copy Markdown
Collaborator

Relevant issues

Backports four already-merged guardrail and integration fixes from litellm_internal_staging onto stable/1.89.x and cuts 1.89.3. All four are guardrail/integration reliability fixes; none touch DB schema, dependencies, or auth defaults. Datadog 413-split (#29444) was requested too but is already on this line (shipped in v1.89.0), so it is not re-applied.

What is included

In cherry-pick (original staging merge) order:

  • fix(integrations): cap Anthropic cache_control injection at 4 blocks #30480 fix(integrations): cap Anthropic cache_control injection at 4 blocks. AnthropicCacheControlHook now counts client-supplied breakpoints, skips messages that already carry cache_control, and stops auto-injecting once Anthropic's hard limit of 4 is reached, so a configured injection point can no longer push a request to a 400 "A maximum of 4 blocks with cache_control may be provided"
  • fix(guardrails): run pre_call hook once for model-level guardrails #30543 fix(guardrails): run pre_call hook once for model-level guardrails. A CustomGuardrail attached via litellm_params.guardrails no longer has its async_pre_call_hook invoked twice per request; a per-process-secret marker records that the proxy loop already ran it and the deployment hook skips it, and the marker cannot be forged from caller metadata
  • fix(guardrails): stop re-initializing DB guardrails on every poll #30542 fix(guardrails): stop re-initializing DB guardrails on every poll. InMemoryGuardrailHandler normalizes both sides through LitellmParams before diffing, so an unchanged DB guardrail is no longer rebuilt on every poll, and deleting a guardrail purges its callback from every callback list instead of leaking stale instances
  • fix(guardrails): return 400 not 500 when AIM blocks a request #30573 fix(guardrails): return 400 not 500 when AIM blocks a request. AIM rejections raise a conformant ProxyException and the shared error funnel honors an already-normalized ProxyException instead of re-deriving its status and downgrading the 400 to a 500; ProxyException is also classified correctly in post_call_failure (excluded from llm_exceptions paging, still recorded by failure logging)

Then the version bump to 1.89.3 and the uv.lock refresh.

Adaptation notes

#30573 is the only adapted pick; the other three applied patch-identical to their staging source (verified by patch-id). The pick's own added lines are byte-identical to staging. The divergences are dropped staging-only neighbors this line does not carry:

  • litellm/proxy/common_request_processing.py: staging anchored the new ProxyException short-circuit just after a self._apply_router_cooldown_retry_after(headers, e) context line. That helper, and the router-cooldown retry-after feature, landed on staging after 1.89.x was cut and are absent here; the line was context in the diff, not part of the fix. The identical ProxyException short-circuit is inserted immediately before the if isinstance(e, HTTPException) branch, reusing the headers dict the funnel already builds on this line. The staging-only helper call is omitted
  • tests/test_litellm/proxy/test_common_request_processing.py: kept only fix(guardrails): return 400 not 500 when AIM blocks a request #30573's own added method test_already_normalized_proxy_exception_is_honored in the existing TestHandleLLMApiExceptionDictDetail class; dropped the staging-only neighbor tests bundled in the same hunk (including the entire TestHandleLLMApiExceptionRetryAfter class, which exercises the absent _apply_router_cooldown_retry_after)
  • tests/test_litellm/proxy/test_proxy_utils.py: kept only fix(guardrails): return 400 not 500 when AIM blocks a request #30573's own added classes (TestPostCallFailureHookLLMExceptionAlerting, TestPostCallFailureHookProxyExceptionLogging); dropped the staging-only SMTP/SSL neighbor classes. Those tests use module-global patch, which on staging is imported on the same line that the dropped SMTP change added, so patch was restored to from unittest.mock import MagicMock, patch to match staging. This is a stdlib import the picked tests already use; no production code or test assertion changed

Known noise on this line

Targeted tests run as a delta against a pre-pick baseline. One failure pre-exists on stable/1.89.x and is unrelated to this PR:

  • tests/test_litellm/proxy/test_proxy_utils.py::test_get_custom_url - asserts the host stays 0.0.0.0 but gets localhost; host-normalization, present before any pick

Everything else is green. Baseline: 219 passed, 1 failed. With the picks: 246 passed, 1 failed (same known-noise test). Zero new failures, +27 passing from the picks' own regression tests.

Screenshots / Proof of Fix

Live proxy on the picked code, real Anthropic API (claude-haiku-4-5), real spend.

Sanity floor, unchanged from the pre-pick baseline:

GET  /health/liveliness           -> "I'm alive!"
POST /v1/chat/completions          -> 200, model=claude-haiku-4-5, content="shipped"
POST /key/generate                 -> 200, key created, models=["claude-haiku-4-5"]

#30480 cache_control cap, live:

# Call A: injection point role=user over 6 user messages.
# Without the cap -> 6 cache_control blocks -> Anthropic 400 "A maximum of 4 blocks".
POST /v1/chat/completions  (cache_control_injection_points=[{location:message,role:user}])
  -> HTTP 200, model=claude-haiku-4-5, content="capped"

# Call B: 5 CLIENT-supplied cache_control blocks (the fix deliberately leaves these alone).
POST /v1/chat/completions  (5 client cache_control blocks, no injection points)
  -> Anthropic rejects >4; request falls back, served by model=grok-4.3

Call A stays on claude (<=4 blocks reached Anthropic) while Call B falls back (>4 reached Anthropic), so the only thing that changed Call A's outcome is the cap. Together they show the fix caps auto-injection and does not strip client-supplied breakpoints.

#30573, #30542, #30543 are guardrail-internal fixes whose live reproducers need an AIM API key and a Postgres-backed guardrail harness that this environment does not have. They are verified by their own regression tests passing on this line (in the 246-passed delta above) and by the gauntlet below.

Gauntlet (behavioral, deep, universal): SURVIVED, zero verified regression findings. It independently re-ran the decisive checks on the picked tree and live-validated each fix: the #30573 funnel short-circuit returns HTTP 400 for a ProxyException with a generic-exception 500 negative control, the dropped staging-only helper is referenced nowhere, #30543 resists forged and wrong-token suppression markers while honoring the genuine per-process token, #30480 caps at 4 while preserving client blocks, and #30542 purges across all five callback lists. Targeted-test delta: 0 new failures vs baseline.

shivamrawat1 and others added 6 commits June 20, 2026 11:49
…30480)

* fix(integrations): cap Anthropic cache_control injection at 4 blocks

Respect Anthropic's 4 cache_control breakpoint limit by counting client-supplied blocks, skipping messages that already carry cache_control, and stopping further auto-injection once the limit is reached.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(integrations): reserve cache slot for tool_config and short-circuit cap

Address review feedback on the cache_control cap: break out of the injection loop before resolving target indices once the limit is reached, and reserve one of the four breakpoint slots when a tool_config injection point is present so the cachePoint appended by the Bedrock transform does not push the total past Anthropic's limit.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit fc9d789)
…30543)

* fix(guardrails): run pre_call hook once for model-level guardrails

A CustomGuardrail attached to a deployment via litellm_params.guardrails
gets its async_pre_call_hook invoked twice per request: once by the proxy
pre-call loop and again by async_pre_call_deployment_hook after the router
spreads the model-level guardrails into the top-level request kwargs.

Record in request metadata that the proxy pre-call loop already ran a given
guardrail, and have the deployment hook skip it when the marker is present.
Direct-SDK usage never runs the proxy loop, so the deployment hook stays the
sole invocation there and still fires exactly once.

The marker key is stripped from untrusted caller metadata so a request body
cannot suppress a model-only guardrail by pre-seeding it.

* fix(guardrails): mark pre_call dedup on the post-hook request data

Record the exactly-once marker after async_pre_call_hook runs, on the data
object that flows downstream, rather than before it. A guardrail whose hook
returns a brand-new request dict (instead of mutating or spreading the one it
received) would otherwise discard the marker, letting the deployment hook
re-run the guardrail a second time.

(cherry picked from commit 4faeabc)
…0542)

* fix(guardrails): stop re-initializing DB guardrails on every poll

InMemoryGuardrailHandler._has_guardrail_params_changed compared the
in-memory LitellmParams against the raw dict loaded from the DB. The
in-memory side carries every field default and coerces enums via
model_dump(), while the DB side only holds the keys originally stored,
so the two shapes never compared equal and the guardrail was rebuilt on
every poll cycle.

Each rebuild created a fresh instance, but delete_in_memory_guardrail
only removed the old callback from litellm.callbacks. Request handling
promotes guardrail callbacks into the success/failure/async lists, so
the previous instance stayed referenced there and instances accumulated.

Normalize both sides through LitellmParams(...).model_dump() before
diffing, and purge the callback from every callback list on delete.

* refactor(guardrails): narrow params-normalization fallback to ValidationError

The comparison normalizer caught a bare Exception and silently fell back
to the raw dict, which hid the cause and quietly degraded the affected
guardrail back to re-initializing on every poll. Catch only the
ValidationError that LitellmParams construction can raise, log a warning
so the offending row is diagnosable, and let any other error surface
instead of being swallowed.

* refactor(callbacks): add remove_callback_from_all_lists helper to manager

Move the knowledge of which callback lists a callback can be promoted
into out of the guardrail registry and into LoggingCallbackManager, where
the rest of the callback-list bookkeeping already lives. delete_in_memory_guardrail
now delegates to the new helper instead of iterating the lists itself.

(cherry picked from commit 9fa74ad)
* fix(guardrails): return 400 not 500 when AIM blocks a request

AIM guardrail blocks raised a bare HTTPException whose type and param
serialized as the literal string "None", which broke OpenAI-SDK error
parsing for downstream consumers. Switching AIM to raise a ProxyException
surfaced a second bug: the shared error funnel re-derived the HTTP status
from a nonexistent status_code attribute and downgraded the 400 to a 500.
The funnel now honors an already-normalized ProxyException rather than
rebuilding it, and ProxyException is excluded from llm_exceptions alerting
so a content-policy block no longer pages on-call as an LLM API failure

Resolves LIT-3751

* fix(guardrails): route all AIM rejection paths through ProxyException

The block-action fix left two AIM rejection paths raising a bare
HTTPException: the multimodal anonymize rejection and the output-side
block. Both serialized type and param as the literal string "None", the
same malformed shape the block fix removed. Funnel all three through a
shared _rejection helper so they return a conformant OpenAI error body.
The output block carries content_policy_violation; the multimodal
rejection stays a plain invalid_request_error because it is a usage
error, not a policy violation

Resolves LIT-3751

* fix(guardrails): record AIM ProxyException blocks in failure logs

Switching AIM blocks from HTTPException to ProxyException made
_is_proxy_only_llm_api_error return False for them, so
_handle_logging_proxy_only_error was skipped and the blocked prompt was
dropped from the configured failure loggers. Classify ProxyException as a
proxy-only error alongside HTTPException so guardrail blocks are recorded
again, matching the prior behavior. The llm_exceptions alert suppression
is a separate check and stays in place

Resolves LIT-3751

* style(guardrails): use str | None over Optional[str] in AIM _rejection

* style(guardrails): collapse AIM _rejection signature per black

(cherry picked from commit b5fcd85)
@yuneng-berri
yuneng-berri requested a review from a team June 20, 2026 19:32
@CLAassistant

CLAassistant commented Jun 20, 2026

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 all sign our Contributor License Agreement before we can accept your contribution.
3 out of 4 committers have signed the CLA.

✅ ryan-crabbe-berri
✅ shivamrawat1
✅ yuneng-berri
❌ yassin-berriai
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This backport cherry-picks four guardrail and integration reliability fixes onto stable/1.89.x and cuts v1.89.3. All four apply cleanly, with only #30573 requiring a documented adaptation to omit a staging-only helper (_apply_router_cooldown_retry_after) that does not exist on this branch.

  • fix(integrations): cap Anthropic cache_control injection at 4 blocks #30480 (anthropic_cache_control_hook.py): Counts client-supplied cache_control breakpoints before injecting, skips messages that already carry one, and stops auto-injection once Anthropic's 4-block hard limit is reached; also fixes a pre-existing bug where role-based injection was a silent no-op (the old loop reassigned a local variable without updating the list).
  • fix(guardrails): run pre_call hook once for model-level guardrails #30543 (custom_guardrail.py, pipeline_executor.py, utils.py): A per-process secret token is written into request metadata after the proxy pre_call loop runs a guardrail; the deployment-level hook checks for that token and skips if found, preventing double-execution of async_pre_call_hook on model-level guardrails; the key is sanitized from outbound metadata in both litellm_pre_call_utils.py and callback_utils.py.
  • fix(guardrails): stop re-initializing DB guardrails on every poll #30542 (guardrail_registry.py, logging_callback_manager.py): Normalizes both sides of the params comparison through LitellmParams so unchanged DB guardrails are no longer rebuilt on every poll; deleting a guardrail now purges its callback from all five callback lists rather than only litellm.callbacks.
  • fix(guardrails): return 400 not 500 when AIM blocks a request #30573 (aim.py, common_request_processing.py, utils.py): AIM rejections raise a ProxyException(code=400) instead of a bare HTTPException; the error funnel short-circuits on ProxyException before re-deriving its status code; post_call_failure_hook excludes ProxyException from llm_exceptions alerting but keeps it in proxy-only failure logging.

Confidence Score: 4/5

The four fixes are well-scoped and well-tested; no DB schema, auth, or dependency changes are included, and the most delicate path (ProxyException short-circuit in the error funnel) is covered by direct unit tests and a live-proxy gauntlet.

The changes are narrow bug fixes with strong regression coverage (246 passing, zero new failures). The reserved_blocks heuristic could be wrong if multiple tool_config entries are supplied in a single request, and the header-merge order in the ProxyException short-circuit could silently drop guardrail-supplied headers if they collide with funnel headers in a future scenario. Neither condition arises with current usage.

The reserved_blocks slot calculation in anthropic_cache_control_hook.py and the header merge ordering in common_request_processing.py are the two spots worth a second look.

Important Files Changed

Filename Overview
litellm/integrations/anthropic_cache_control_hook.py Refactors message-level cache_control injection to enforce Anthropic's 4-block limit, count client-supplied breakpoints, and skip messages that already carry cache_control; also fixes the previously-broken role-based injection (the old loop reassigned a local variable and never modified the list)
litellm/integrations/custom_guardrail.py Adds per-process secret token and mark/check helpers so the proxy pre_call loop and the router deployment hook together invoke async_pre_call_hook exactly once per request; PRE_CALL_EXECUTED_GUARDRAILS_KEY imported from constants.py as required
litellm/proxy/common_request_processing.py Inserts ProxyException short-circuit before the HTTPException branch so already-normalized guardrail rejections raise with their original 400 status instead of being re-derived to 500; headers are merged in
litellm/proxy/guardrails/guardrail_hooks/aim/aim.py Replaces all three HTTPException raises with a conformant ProxyException via the new _rejection helper; removes FastAPI import from a non-proxy integration file, aligning with the project rule
litellm/proxy/guardrails/guardrail_registry.py Fixes two bugs: normalizes both sides of the params comparison through LitellmParams so unchanged DB guardrails are not rebuilt on every poll; purges deleted guardrail callbacks from all five callback lists
litellm/proxy/utils.py Calls mark_pre_call_hook_ran after the proxy pre_call loop executes a guardrail; extends the llm_exceptions and proxy-only logging classifier to also match ProxyException alongside HTTPException
litellm/proxy/policy_engine/pipeline_executor.py Marks the pre_call hook on both the input data dict and the possibly-fresh response dict so the deployment-level dedup check works when hooks return a new dict rather than mutating in-place
litellm/litellm_core_utils/logging_callback_manager.py Adds remove_callback_from_all_lists helper used by guardrail_registry to purge stale instances from every callback list on deletion
litellm/proxy/litellm_pre_call_utils.py Adds PRE_CALL_EXECUTED_GUARDRAILS_KEY to _UNTRUSTED_METADATA_CONTROL_FIELDS so a caller cannot inject the marker through request metadata to bypass guardrail checks
litellm/proxy/common_utils/callback_utils.py Adds PRE_CALL_EXECUTED_GUARDRAILS_KEY to LITELLM_PROXY_INTERNAL_METADATA_KEYS so the key is stripped from outbound requests
litellm/constants.py Adds PRE_CALL_EXECUTED_GUARDRAILS_KEY sentinel to constants.py per the project's sentinel-in-constants rule
tests/test_litellm/proxy/test_common_request_processing.py Adds test_already_normalized_proxy_exception_is_honored covering the ProxyException short-circuit; staging-only neighbor tests excluded as documented
tests/test_litellm/proxy/test_proxy_utils.py Adds TestPostCallFailureHookLLMExceptionAlerting and TestPostCallFailureHookProxyExceptionLogging verifying ProxyException is excluded from alerting but still drives failure logging
tests/local_testing/test_aim_guardrails.py Updates exception assertions from HTTPException to ProxyException; adds three new tests for output block, multimodal anonymize rejection, and AIM conformance; all HTTP calls are mocked
tests/test_litellm/proxy/guardrails/test_guardrail_registry.py Adds comprehensive tests for LitellmParams normalization comparison, all-list deletion, and repeated-sync accumulation regression

Reviews (1): Last reviewed commit: "chore: refresh uv.lock for 1.89.3" | Re-trigger Greptile

Comment on lines +89 to 98
messages=processed_messages,
max_blocks=MAX_CACHE_CONTROL_BLOCKS - reserved_blocks,
)

# Pass through non-message injection points for provider-specific handling
if remaining_points:
non_default_params["cache_control_injection_points"] = remaining_points

return model, processed_messages, non_default_params

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 reserved_blocks assumes exactly one cachePoint per non-message injection point

reserved_blocks is set to 1 whenever any tool_config point exists in remaining_points, regardless of how many there are. In practice a single tool_config field can only carry one cachePoint so this is currently harmless, but if a caller passes multiple tool_config entries the over-count would prevent only one message-level block from injecting while the transform later appends N cachePoints, potentially pushing the total past Anthropic's limit. A comment here noting the one-slot-per-unique-non-message-location assumption would help future contributors understand why counting distinct locations matters.

Comment on lines +1910 to +1915
if isinstance(e, ProxyException):
e.headers = {
**e.headers,
**{k: v if isinstance(v, str) else str(v) for k, v in headers.items()},
}
raise e

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Funnel headers may overwrite ProxyException's own headers

The merge is {**e.headers, **funnel_headers}, so any key present in both dicts resolves to the funnel value. For a guardrail 400, e.headers is typically {}, so there is no practical conflict today. If a future guardrail sets a response header (e.g., a custom X-Blocked-By header) and the funnel accumulates a same-named key from a callback, the guardrail's value would be silently dropped. Reversing the merge order to {**funnel_headers, **e.headers} would let the ProxyException's own headers win.

Comment on lines +55 to +57
# Per-process secret tagging each recorded marker. The deployment hook only
# honors markers carrying this token, so a caller cannot forge the metadata
# field to suppress a guardrail on the direct-SDK path that never reaches the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 _PRE_CALL_EXECUTED_TOKEN is a module-level secret, not in constants.py

The project rule (5f7d9a68) requires sentinel-like module-level variables to live in constants.py. _PRE_CALL_EXECUTED_TOKEN is a per-process secrets.token_hex value, not a static string, so it differs from the canonical sentinel example. However, keeping it unexported in custom_guardrail.py (rather than in constants.py) actually strengthens the security property: any code that imports from constants.py would gain access to the token, widening the attack surface. The companion key name PRE_CALL_EXECUTED_GUARDRAILS_KEY is correctly placed in constants.py. Consider adding an inline comment explaining why this value is intentionally kept module-private rather than in constants.py.

Rule Used: What: Require sentinel-like variables (e.g., `_NEG... (source)

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!

@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

@yuneng-berri
yuneng-berri merged commit 33df589 into stable/1.89.x Jun 20, 2026
40 of 51 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_backport_1_89_x_0620 branch June 20, 2026 21:45
hbjydev pushed a commit to hbjydev/phoebe that referenced this pull request Jun 22, 2026
….3) (#171)

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.2` → `v1.89.3` |

---

### Release Notes

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

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

[Compare Source](BerriAI/litellm@v1.89.3...v1.89.3)

##### 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.3
```

**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.3/cosign.pub \
  ghcr.io/berriai/litellm:v1.89.3
```

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(release): backport [#&#8203;30480](BerriAI/litellm#30480), [#&#8203;30543](BerriAI/litellm#30543), [#&#8203;30542](BerriAI/litellm#30542), [#&#8203;30573](BerriAI/litellm#30573) to stable/1.89.x and cut 1.89.3 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;30888](BerriAI/litellm#30888)

**Full Changelog**: <BerriAI/litellm@v1.89.2...v1.89.3>

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

[Compare Source](BerriAI/litellm@v1.89.2...v1.89.3)

##### 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.3
```

**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.3/cosign.pub \
  ghcr.io/berriai/litellm:v1.89.3
```

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(release): backport [#&#8203;30480](BerriAI/litellm#30480), [#&#8203;30543](BerriAI/litellm#30543), [#&#8203;30542](BerriAI/litellm#30542), [#&#8203;30573](BerriAI/litellm#30573) to stable/1.89.x and cut 1.89.3 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;30888](BerriAI/litellm#30888)

**Full Changelog**: <BerriAI/litellm@v1.89.2...v1.89.3>

</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:eyJjcmVhdGVkSW5WZXIiOiI0My4yMzIuMSIsInVwZGF0ZWRJblZlciI6IjQzLjIzMi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://forgejo.hayden.moe/hayden/phoebe/pulls/171
doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Jun 22, 2026
….3) (#352)

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.2` → `v1.89.3` |

---

### Release Notes

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

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

[Compare Source](BerriAI/litellm@v1.89.3...v1.89.3)

##### 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.3
```

**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.3/cosign.pub \
  ghcr.io/berriai/litellm:v1.89.3
```

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(release): backport [#&#8203;30480](BerriAI/litellm#30480), [#&#8203;30543](BerriAI/litellm#30543), [#&#8203;30542](BerriAI/litellm#30542), [#&#8203;30573](BerriAI/litellm#30573) to stable/1.89.x and cut 1.89.3 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;30888](BerriAI/litellm#30888)

**Full Changelog**: <BerriAI/litellm@v1.89.2...v1.89.3>

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

[Compare Source](BerriAI/litellm@v1.89.2...v1.89.3)

##### 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.3
```

**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.3/cosign.pub \
  ghcr.io/berriai/litellm:v1.89.3
```

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(release): backport [#&#8203;30480](BerriAI/litellm#30480), [#&#8203;30543](BerriAI/litellm#30543), [#&#8203;30542](BerriAI/litellm#30542), [#&#8203;30573](BerriAI/litellm#30573) to stable/1.89.x and cut 1.89.3 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;30888](BerriAI/litellm#30888)

**Full Changelog**: <BerriAI/litellm@v1.89.2...v1.89.3>

</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:eyJjcmVhdGVkSW5WZXIiOiI0My4yMjQuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIzNC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/352
blake-hamm added a commit to blake-hamm/bhamm-lab that referenced this pull request Jun 24, 2026
…to v1.89.3 (#216)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [https://github.com/BerriAI/litellm.git](https://github.com/BerriAI/litellm) | patch | `v1.89.2` → `v1.89.3` |

---

### Release Notes

<details>
<summary>BerriAI/litellm (https://github.com/BerriAI/litellm.git)</summary>

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

[Compare Source](BerriAI/litellm@v1.89.2...v1.89.3)

#### 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.3
```

**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.3/cosign.pub \
  ghcr.io/berriai/litellm:v1.89.3
```

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(release): backport [#&#8203;30480](BerriAI/litellm#30480), [#&#8203;30543](BerriAI/litellm#30543), [#&#8203;30542](BerriAI/litellm#30542), [#&#8203;30573](BerriAI/litellm#30573) to stable/1.89.x and cut 1.89.3 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;30888](BerriAI/litellm#30888)

**Full Changelog**: <BerriAI/litellm@v1.89.2...v1.89.3>

</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/216
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.

6 participants