From 258940fbeef6e994fce041bdff336a742f6372d9 Mon Sep 17 00:00:00 2001 From: Kihyeon Myung Date: Sat, 28 Mar 2026 20:47:57 -0600 Subject: [PATCH] fix(bedrock): place cache point before non-PDF document blocks (#1966) --- src/strands/models/bedrock.py | 15 +++++- tests/strands/models/test_bedrock.py | 76 ++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/strands/models/bedrock.py b/src/strands/models/bedrock.py index 5de34a6c2..85b1fe00d 100644 --- a/src/strands/models/bedrock.py +++ b/src/strands/models/bedrock.py @@ -362,7 +362,20 @@ def _inject_cache_point(self, messages: list[dict[str, Any]]) -> None: last_user_idx = msg_idx if last_user_idx is not None and messages[last_user_idx].get("content"): - messages[last_user_idx]["content"].append({"cachePoint": {"type": "default"}}) + content = messages[last_user_idx]["content"] + + # Insert before non-PDF document blocks to avoid Bedrock ValidationException + first_non_pdf_doc_idx = None + for i, block in enumerate(content): + if "document" in block and block["document"].get("format", "") != "pdf": + first_non_pdf_doc_idx = i + break + + if first_non_pdf_doc_idx is not None: + content.insert(first_non_pdf_doc_idx, {"cachePoint": {"type": "default"}}) + else: + content.append({"cachePoint": {"type": "default"}}) + logger.debug("msg_idx=<%s> | added cache point to last user message", last_user_idx) def _find_last_user_text_message_index(self, messages: Messages) -> int | None: diff --git a/tests/strands/models/test_bedrock.py b/tests/strands/models/test_bedrock.py index 5f81efd24..000ef5d00 100644 --- a/tests/strands/models/test_bedrock.py +++ b/tests/strands/models/test_bedrock.py @@ -2683,6 +2683,82 @@ def test_inject_cache_point_strips_existing_cache_points(bedrock_client): assert "cachePoint" in cleaned_messages[2]["content"][-1] +def test_inject_cache_point_before_non_pdf_document(bedrock_client): + """Test that cache point is inserted before non-PDF document blocks.""" + model = BedrockModel( + model_id="us.anthropic.claude-sonnet-4-20250514-v1:0", cache_config=CacheConfig(strategy="auto") + ) + + cleaned_messages = [ + { + "role": "user", + "content": [ + {"text": "Analyze this file"}, + {"document": {"format": "md", "name": "readme", "source": {"bytes": b"# Hello"}}}, + ], + }, + ] + + model._inject_cache_point(cleaned_messages) + + content = cleaned_messages[0]["content"] + assert len(content) == 3 + assert content[0] == {"text": "Analyze this file"} + assert "cachePoint" in content[1] + assert "document" in content[2] + + +def test_inject_cache_point_after_pdf_document(bedrock_client): + """Test that cache point is appended at end when only PDF documents are present.""" + model = BedrockModel( + model_id="us.anthropic.claude-sonnet-4-20250514-v1:0", cache_config=CacheConfig(strategy="auto") + ) + + cleaned_messages = [ + { + "role": "user", + "content": [ + {"text": "Analyze this PDF"}, + {"document": {"format": "pdf", "name": "report", "source": {"bytes": b"%PDF-1.4"}}}, + ], + }, + ] + + model._inject_cache_point(cleaned_messages) + + content = cleaned_messages[0]["content"] + assert len(content) == 3 + assert "document" in content[1] + assert "cachePoint" in content[2] + + +def test_inject_cache_point_mixed_pdf_and_non_pdf_documents(bedrock_client): + """Test that cache point is inserted before the first non-PDF document in mixed content.""" + model = BedrockModel( + model_id="us.anthropic.claude-sonnet-4-20250514-v1:0", cache_config=CacheConfig(strategy="auto") + ) + + cleaned_messages = [ + { + "role": "user", + "content": [ + {"text": "Analyze these files"}, + {"document": {"format": "pdf", "name": "report", "source": {"bytes": b"%PDF-1.4"}}}, + {"document": {"format": "csv", "name": "data", "source": {"bytes": b"a,b,c"}}}, + ], + }, + ] + + model._inject_cache_point(cleaned_messages) + + content = cleaned_messages[0]["content"] + assert len(content) == 4 + assert "text" in content[0] + assert "document" in content[1] and content[1]["document"]["format"] == "pdf" + assert "cachePoint" in content[2] + assert "document" in content[3] and content[3]["document"]["format"] == "csv" + + def test_inject_cache_point_anthropic_strategy_skips_model_check(bedrock_client): """Test that anthropic strategy injects cache point without model support check.""" model = BedrockModel(