Skip to content

Commit b207e03

Browse files
ElliottJWsmar-elliott-jwclaude
authored
fix(litellm): forward ttl field from CachePoint in _format_system_messages (strands-agents#2153)
Co-authored-by: Elliott Jacobsen-Watts <elliott.jacobsen-watts@smartsheet.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 4e9ed26 commit b207e03

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

src/strands/models/litellm.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,14 @@ def _format_system_messages(
221221
for block in system_prompt_content or []:
222222
if "text" in block:
223223
system_content.append({"type": "text", "text": block["text"]})
224-
elif "cachePoint" in block and block["cachePoint"].get("type") == "default":
224+
elif "cachePoint" in block and block["cachePoint"]["type"] == "default":
225225
# Apply cache control to the immediately preceding content block
226226
# for LiteLLM/Anthropic compatibility
227227
if system_content:
228-
system_content[-1]["cache_control"] = {"type": "ephemeral"}
228+
cache_control: dict[str, Any] = {"type": "ephemeral"}
229+
if ttl := block["cachePoint"].get("ttl"):
230+
cache_control["ttl"] = ttl
231+
system_content[-1]["cache_control"] = cache_control
229232

230233
# Create single system message with content array rather than mulitple system messages
231234
return [{"role": "system", "content": system_content}] if system_content else []

src/strands/types/content.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,12 @@ class CachePoint(TypedDict):
6767
6868
Attributes:
6969
type: The type of cache point, typically "default".
70+
ttl: Optional cache TTL duration (e.g. "5m", "1h"). Supported by providers
71+
that accept Anthropic-compatible cache_control fields.
7072
"""
7173

7274
type: str
75+
ttl: NotRequired[str]
7376

7477

7578
class ContentBlock(TypedDict, total=False):

tests/strands/models/test_litellm.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,39 @@ def test_format_request_message_tool_call_no_reasoning_signature():
955955
assert "__thought__" not in result["id"]
956956

957957

958+
def test_format_system_messages_preserves_cache_point_ttl():
959+
"""CachePoint with ttl="1h" should produce cache_control with ttl field."""
960+
result = LiteLLMModel._format_system_messages(
961+
system_prompt_content=[
962+
{"text": "You are a helpful assistant."},
963+
{"cachePoint": {"type": "default", "ttl": "1h"}},
964+
]
965+
)
966+
assert result[0]["content"][0]["cache_control"] == {"type": "ephemeral", "ttl": "1h"}
967+
968+
969+
def test_format_system_messages_cache_point_without_ttl():
970+
"""CachePoint without ttl should produce cache_control with no ttl key (backward compat)."""
971+
result = LiteLLMModel._format_system_messages(
972+
system_prompt_content=[
973+
{"text": "You are a helpful assistant."},
974+
{"cachePoint": {"type": "default"}},
975+
]
976+
)
977+
assert result[0]["content"][0]["cache_control"] == {"type": "ephemeral"}
978+
assert "ttl" not in result[0]["content"][0]["cache_control"]
979+
980+
981+
def test_format_system_messages_cache_point_with_no_preceding_content():
982+
"""CachePoint with no preceding text block should be silently ignored."""
983+
result = LiteLLMModel._format_system_messages(
984+
system_prompt_content=[
985+
{"cachePoint": {"type": "default", "ttl": "1h"}},
986+
]
987+
)
988+
assert result == []
989+
990+
958991
def test_thought_signature_round_trip():
959992
"""Test that thought signature is preserved through a full response -> internal -> request cycle."""
960993
model = LiteLLMModel(model_id="test")

0 commit comments

Comments
 (0)