From 8f5c08d777459aee3f38bf57acb921f51eb1ac35 Mon Sep 17 00:00:00 2001 From: kimnamu Date: Sat, 20 Jun 2026 00:13:47 +0900 Subject: [PATCH] Python: Fix Bedrock non-ASCII escaping in JSON content blocks The Bedrock Converse `json` content block was serialized with `json.dumps(json_value)`, whose default `ensure_ascii=True` escapes CJK/emoji/accented characters to `\uXXXX` and surfaces garbled text. Add `ensure_ascii=False` to match the sibling OpenAI client and the 16+ other call sites across the repo. Includes a regression test. Closes #6627 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../agent_framework_bedrock/_chat_client.py | 4 ++- .../bedrock/tests/test_bedrock_client.py | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/python/packages/bedrock/agent_framework_bedrock/_chat_client.py b/python/packages/bedrock/agent_framework_bedrock/_chat_client.py index cf8b3c562ae..f61a23a7669 100644 --- a/python/packages/bedrock/agent_framework_bedrock/_chat_client.py +++ b/python/packages/bedrock/agent_framework_bedrock/_chat_client.py @@ -698,7 +698,9 @@ def _parse_message_contents(self, content_blocks: Sequence[dict[str, Any]]) -> l contents.append(Content.from_text(text=text_value, raw_representation=block)) continue if (json_value := block.get("json")) is not None: - contents.append(Content.from_text(text=json.dumps(json_value), raw_representation=block)) + contents.append( + Content.from_text(text=json.dumps(json_value, ensure_ascii=False), raw_representation=block) + ) continue tool_use_value = block.get("toolUse") tool_use = ( diff --git a/python/packages/bedrock/tests/test_bedrock_client.py b/python/packages/bedrock/tests/test_bedrock_client.py index 9e1b42ea251..7e50250e243 100644 --- a/python/packages/bedrock/tests/test_bedrock_client.py +++ b/python/packages/bedrock/tests/test_bedrock_client.py @@ -2,6 +2,7 @@ from __future__ import annotations +import json from typing import Any import pytest @@ -169,3 +170,34 @@ def test_prepare_options_tool_choice_required_without_tools_raises() -> None: with pytest.raises(ValueError, match="tool_choice='required' requires at least one tool"): client._prepare_options(messages, options) + + +def test_process_converse_response_preserves_non_ascii_in_json_block() -> None: + """Non-ASCII text in a Bedrock ``json`` content block must be preserved, not \\uXXXX-escaped. + + The Converse API can return structured ``json`` content blocks. These are serialized to + text via ``json.dumps``; without ``ensure_ascii=False`` CJK characters and emoji are escaped + to ``\\uXXXX`` sequences and surface garbled to the user. + """ + client = _make_client() + json_payload = {"greeting": "你好世界", "emoji": "🎉"} + response: dict[str, Any] = { + "modelId": "amazon.titan-text", + "output": { + "completionReason": "end_turn", + "message": { + "role": "assistant", + "content": [{"json": json_payload}], + }, + }, + } + + chat_response = client._process_converse_response(response) + + text = chat_response.messages[0].text + assert "你好世界" in text + assert "🎉" in text + # Must not be escaped to Unicode code points. + assert "\\u" not in text + # Serialized text must remain valid JSON that round-trips to the original payload. + assert json.loads(text) == json_payload