Skip to content

fix(bedrock): honor cache_control ttl on message-level cachePoint blocks - #32551

Merged
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_fix_bedrock_message_cachepoint_ttl
Jul 9, 2026
Merged

fix(bedrock): honor cache_control ttl on message-level cachePoint blocks#32551
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_fix_bedrock_message_cachepoint_ttl

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Completes the fix for #32154. Copy of #32155 by @arashne, cherry-picked onto litellm_internal_staging so CircleCI can run on it; the code and tests are their work and the commit keeps the original authorship

Linear ticket

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

See #32155 for the original repro (a message-level cache_control with ttl: "1h" silently emitting cachePoint: {"type": "default"}) and #32538 for live Bedrock usage output captured at bfff5e8d86 (before) and 7195c2dfa2 (after), showing the full prefix served from the 1h cache on turn 2 after a 7 minute wait once model reaches the cachePoint builder. The paths completed here (tool-result blocks and per-tool-call cache_control) fail the same way on current staging: they hardcode CachePointBlock(type="default") without ever consulting _get_cache_point_block, so a requested ttl is dropped. After this change they emit {"type": "default", "ttl": "1h"} on supported models and keep emitting the plain default block on unsupported ones

Type

🐛 Bug Fix

Changes

Copy of #32155, authored by @arashne, so CircleCI can run on it

Since the original PR was opened, #32538 landed on litellm_internal_staging and covered part of the same ground: it passes model= at the 8 existing _get_cache_point_block call sites in _bedrock_converse_messages_pt / _bedrock_converse_messages_pt_async. This PR carries the remaining paths from #32155, the ones that never reached _get_cache_point_block at all because they hardcoded CachePointBlock(type="default"): tool-result blocks now capture the cache_control dict (previously reduced to a boolean) and route it through _get_cache_point_block so ttl survives, and _convert_to_bedrock_tool_call_invoke now accepts an optional model and routes per-tool-call cache_control the same way, with both callers passing model=model

The regression tests from the original PR are parameterized over user, tool-call, and tool-result placements crossed with a supported and an unsupported model, and assert the sync and async paths emit identical blocks


Note

Medium Risk
Changes Bedrock prompt shaping for cached agentic/tool loops; wrong TTL handling could affect cache behavior and API acceptance, but scope is limited to cache-point emission with new tests.

Overview
Bedrock Converse message conversion no longer hardcodes cachePoint: {"type": "default"} for tool results and assistant tool-call cache_control; those paths now build cache points via AmazonConverseConfig()._get_cache_point_block(..., model=model) so a requested ttl (e.g. 1h) is preserved on models that support extended caching and omitted on unsupported ones.

_convert_to_bedrock_tool_call_invoke gains an optional model argument and both sync/async Converse processors pass it through when converting tool calls. Tool-message handling now keeps the actual cache_control dict (message- or content-level) instead of a boolean before emitting the cache block.

Regression tests cover user, per-tool-call, and tool-result placements for supported vs unsupported models, including sync/async parity.

Reviewed by Cursor Bugbot for commit adb6e5a. Bugbot is set up for automated code reviews on this repo. Configure here.

Bedrock Converse supports cachePoint ttl (1h GA for Claude 4.5+), and
_get_cache_point_block maps cache_control.ttl -> cachePoint.ttl, but the
model parameter its allow-list gate requires was only threaded through
the system-message path. Every message-level path either called
_get_cache_point_block without model= (8 call sites in
_bedrock_converse_messages_pt / _pt_async) or hardcoded
CachePointBlock(type="default") (tool-result blocks and
_convert_to_bedrock_tool_call_invoke), so a requested 1h ttl silently
degraded to the 5-minute default - exactly on the conversation-tail
breakpoint that long-running agents need to survive tool calls longer
than 5 minutes.

- pass model= at the 8 _get_cache_point_block call sites
- tool-result blocks: capture the cache_control dict (was a boolean)
  and route through _get_cache_point_block so ttl survives
- _convert_to_bedrock_tool_call_invoke: accept optional model and route
  per-tool-call cache_control through _get_cache_point_block

Completes the ttl support added for system messages (#19848, #20326):
message-level cache_control now behaves identically.

Note: message-level cache_control on a content-less assistant message
emits no cachePoint at all today; that pre-existing gap is orthogonal
to ttl and left out of scope (per-tool-call placement covers it).

Copy link
Copy Markdown
Contributor Author

@greptileai


Generated by Claude Code

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the Bedrock prompt-caching ttl being silently dropped for tool-result blocks and per-tool-call cache_control, the two code paths that previously hard-coded CachePointBlock(type="default") instead of routing through _get_cache_point_block.

  • _convert_to_bedrock_tool_call_invoke gains an optional model parameter; both call sites (sync _bedrock_converse_messages_pt and async BedrockConverseMessagesProcessor) now forward model=model so the shared helper can attach ttl for models that carry cache_creation_input_token_cost_above_1hr in the pricing table.
  • Tool-result blocks in both the sync and async message-processor paths replace the boolean has_cache_control flag with the actual cache_control dict so ttl is preserved through the call to _get_cache_point_block.
  • Two parametrized regression tests verify all three placement targets (user, tool_call, tool) against a supported and an unsupported model, and assert sync/async output parity.

Confidence Score: 5/5

The change is a focused, backward-compatible bug fix with symmetric treatment of sync and async paths and comprehensive regression tests. No network calls, no new dependencies, no structural changes.

Both affected code paths (tool-result blocks and per-tool-call cache_control) are fixed symmetrically and tested. The model-capability check reuses the existing is_claude_4_5_on_bedrock helper backed by model_prices_and_context_window.json, so no model names are hardcoded. The test model global.anthropic.claude-opus-4-7 correctly has cache_creation_input_token_cost_above_1hr in the pricing table, confirming the parametrized assertions will behave as expected. No correctness or compatibility issues were found.

No files require special attention.

Important Files Changed

Filename Overview
litellm/litellm_core_utils/prompt_templates/factory.py Adds optional model parameter to _convert_to_bedrock_tool_call_invoke and threads it through to _get_cache_point_block at all four affected call sites; replaces the hard-coded CachePointBlock(type="default") pattern with the shared helper so ttl survives for extended-caching models. Sync and async tool-result paths receive symmetric treatment.
tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py Adds two parametrized regression tests covering user-level, per-tool-call, and tool-result cache_control placements for both a supported model (global.anthropic.claude-opus-4-7, which has cache_creation_input_token_cost_above_1hr in the pricing table) and an unsupported model. The async test also asserts sync/async parity. No real network calls are made.

Reviews (1): Last reviewed commit: "fix(bedrock): honor cache_control ttl on..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor Author

bugbot run


Generated by Claude Code

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the silent TTL downgrade that occurred when cache_control with ttl: "1h" was attached to tool-call or tool-result messages on Bedrock: the affected paths hardcoded CachePointBlock(type="default") instead of routing through _get_cache_point_block, so any requested TTL was dropped. The fix threads model into _convert_to_bedrock_tool_call_invoke and replaces direct CachePointBlock construction with _get_cache_point_block calls in both the sync and async paths for tool invokes and tool results.

  • factory.py: Six call sites updated — the two within _convert_to_bedrock_tool_call_invoke (normal JSON and concatenated-JSON fallback), two in BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async (tool-result block and the _convert_to_bedrock_tool_call_invoke caller), and two in the standalone _bedrock_converse_messages_pt (same two symmetric paths).
  • test_converse_transformation.py: Two new parametrized tests added — one asserting ttl=1h propagates on a supported model across all three placement targets (user, tool_call, tool), and one asserting the plain {"type":"default"} block is emitted on unsupported models; sync and async results are asserted equal.

Confidence Score: 5/5

The change is narrowly scoped to four previously-broken cache-point emission paths; unsupported models continue to emit the plain default block, and supported models now correctly forward the ttl value.

All six affected call sites are updated symmetrically in both the sync and async paths. The wrapping pattern used to call _get_cache_point_block is consistent with the dozen pre-existing call sites in the same file. The deduplication step cannot accidentally strip the new cache-point blocks because it only deduplicates on toolUseId. The two new tests cover all three placement targets and assert sync/async parity.

No files require special attention.

Important Files Changed

Filename Overview
litellm/litellm_core_utils/prompt_templates/factory.py Fixes four call sites that hardcoded CachePointBlock(type="default") to instead route through _get_cache_point_block so ttl survives; model is now threaded into _convert_to_bedrock_tool_call_invoke and the tool-result blocks in both sync and async paths.
tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py Adds two new parametrized tests covering all three placement targets (user, tool_call, tool) on a supported model (ttl=1h must survive) and an unsupported model (plain default block must be emitted); sync and async outputs are asserted equal.

Reviews (2): Last reviewed commit: "fix(bedrock): honor cache_control ttl on..." | Re-trigger Greptile

@cursor cursor Bot left a comment

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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit adb6e5a. Configure here.

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 11.45%

⚡ 1 improved benchmark
✅ 29 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
test_completion_simple_message 4.8 ms 4.3 ms +11.45%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing litellm_fix_bedrock_message_cachepoint_ttl (adb6e5a) with litellm_internal_staging (bd23c44)

Open in CodSpeed

@mateo-berri
mateo-berri merged commit 142d5aa into litellm_internal_staging Jul 9, 2026
129 checks passed
@mateo-berri
mateo-berri deleted the litellm_fix_bedrock_message_cachepoint_ttl branch July 9, 2026 03:54
edelauna pushed a commit to edelauna/litellm that referenced this pull request Jul 22, 2026
…cks (BerriAI#32551)

Bedrock Converse supports cachePoint ttl (1h GA for Claude 4.5+), and
_get_cache_point_block maps cache_control.ttl -> cachePoint.ttl, but the
model parameter its allow-list gate requires was only threaded through
the system-message path. Every message-level path either called
_get_cache_point_block without model= (8 call sites in
_bedrock_converse_messages_pt / _pt_async) or hardcoded
CachePointBlock(type="default") (tool-result blocks and
_convert_to_bedrock_tool_call_invoke), so a requested 1h ttl silently
degraded to the 5-minute default - exactly on the conversation-tail
breakpoint that long-running agents need to survive tool calls longer
than 5 minutes.

- pass model= at the 8 _get_cache_point_block call sites
- tool-result blocks: capture the cache_control dict (was a boolean)
  and route through _get_cache_point_block so ttl survives
- _convert_to_bedrock_tool_call_invoke: accept optional model and route
  per-tool-call cache_control through _get_cache_point_block

Completes the ttl support added for system messages (BerriAI#19848, BerriAI#20326):
message-level cache_control now behaves identically.

Note: message-level cache_control on a content-less assistant message
emits no cachePoint at all today; that pre-existing gap is orthogonal
to ttl and left out of scope (per-tool-call placement covers it).

Co-authored-by: Arash <arashne@glia-ai.com>
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