From 944303728bea9f6aa45245fdc00e5e8b1458a479 Mon Sep 17 00:00:00 2001 From: liuhao1024 Date: Thu, 28 May 2026 07:57:09 +0800 Subject: [PATCH] fix(bedrock): decode base64 data URL to bytes for Converse image source The _convert_content_to_converse() function passed the base64-encoded ASCII string directly as ImageBlock.source.bytes. Bedrock's Converse API expects raw bytes (boto3 base64-encodes on the wire), so the image was double-encoded and rejected with 'Invalid or unsupported image format'. Fix: base64.b64decode(data) before passing to source.bytes. Fixes #33317 --- agent/bedrock_adapter.py | 3 ++- tests/agent/test_bedrock_adapter.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/agent/bedrock_adapter.py b/agent/bedrock_adapter.py index 620d1c997852..a7b18fdc70f2 100644 --- a/agent/bedrock_adapter.py +++ b/agent/bedrock_adapter.py @@ -27,6 +27,7 @@ Requires: ``boto3`` (optional dependency — only needed when using the Bedrock provider). """ +import base64 import json import logging import os @@ -479,7 +480,7 @@ def _convert_content_to_converse(content) -> List[Dict]: blocks.append({ "image": { "format": media_type.split("/")[-1] if "/" in media_type else "jpeg", - "source": {"bytes": data}, + "source": {"bytes": base64.b64decode(data)}, } }) else: diff --git a/tests/agent/test_bedrock_adapter.py b/tests/agent/test_bedrock_adapter.py index 04c0913f2897..f95d8a7888ca 100644 --- a/tests/agent/test_bedrock_adapter.py +++ b/tests/agent/test_bedrock_adapter.py @@ -9,6 +9,7 @@ - Edge cases: empty messages, consecutive roles, image content """ +import base64 import json import os import time @@ -349,6 +350,10 @@ def test_image_data_url_converted(self): image_blocks = [b for b in content if "image" in b] assert len(image_blocks) == 1 assert image_blocks[0]["image"]["format"] == "png" + # Verify bytes are decoded (not base64 double-encoded) + img_bytes = image_blocks[0]["image"]["source"]["bytes"] + assert isinstance(img_bytes, bytes), f"Expected bytes, got {type(img_bytes)}" + assert img_bytes == base64.b64decode("iVBORw0KGgo=") def test_multiple_system_messages_merged(self): from agent.bedrock_adapter import convert_messages_to_converse @@ -364,6 +369,30 @@ def test_multiple_system_messages_merged(self): assert system[1]["text"] == "Rule 2" + def test_image_data_url_not_double_encoded(self): + """Regression test: base64 data must be decoded to bytes, not passed as string. + + Bedrock's Converse API expects raw bytes in ImageBlock.source.bytes. + boto3 base64-encodes bytes on the wire. Passing a base64 ASCII string + causes double-encoding and Bedrock rejects it with 'Invalid or + unsupported image format'. See #33317. + """ + import base64 as b64 + from agent.bedrock_adapter import _convert_content_to_converse + raw = b"\x89PNG\r\n\x1a\n" + b"\x00" * 8 + b64_str = b64.b64encode(raw).decode("ascii") + content = [ + {"type": "text", "text": "describe"}, + {"type": "image_url", "image_url": { + "url": f"data:image/png;base64,{b64_str}", + }}, + ] + blocks = _convert_content_to_converse(content) + image_blocks = [b for b in blocks if "image" in b] + assert len(image_blocks) == 1 + src_bytes = image_blocks[0]["image"]["source"]["bytes"] + assert isinstance(src_bytes, bytes) + assert src_bytes == raw # --------------------------------------------------------------------------- # Response normalization: Converse → OpenAI # ---------------------------------------------------------------------------