diff --git a/litellm/proxy/common_utils/http_parsing_utils.py b/litellm/proxy/common_utils/http_parsing_utils.py index 246b6227cc5..522967ea794 100644 --- a/litellm/proxy/common_utils/http_parsing_utils.py +++ b/litellm/proxy/common_utils/http_parsing_utils.py @@ -119,7 +119,11 @@ def strip_internal_control_fields(data: dict) -> None: except (json.JSONDecodeError, ValueError): continue if isinstance(parsed, dict) and _strip_internal_metadata_keys(parsed): - data[container_key] = json.dumps(parsed) + # ensure_ascii=False keeps non-ASCII characters in their + # original UTF-8 form rather than \uXXXX-escaping them, so + # the re-serialized string stays as close to the original + # representation as possible. + data[container_key] = json.dumps(parsed, ensure_ascii=False) async def _read_request_body(request: Optional[Request]) -> Dict: diff --git a/tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py b/tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py index 7387f7cdb11..e818e6910dd 100644 --- a/tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py +++ b/tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py @@ -1006,6 +1006,26 @@ class TestStripInternalControlFields: parsed = json.loads(data["metadata"]) assert parsed == {"tag": "ok"} + def test_json_string_metadata_preserves_unicode_when_stripped(self): + """When the JSON-string metadata is re-serialized after stripping, + non-ASCII characters survive as UTF-8 instead of being + \\uXXXX-escaped (ensure_ascii=False).""" + from litellm.proxy.common_utils.http_parsing_utils import ( + strip_internal_control_fields, + ) + + data = { + "metadata": json.dumps( + {"applied_guardrails": ["caller"], "tag": "café"}, + ensure_ascii=False, + ) + } + strip_internal_control_fields(data) + # The literal "é" survives in the re-serialized output. + assert "é" in data["metadata"] + assert "\\u00e9" not in data["metadata"] + assert json.loads(data["metadata"]) == {"tag": "café"} + def test_clean_json_string_metadata_not_reserialized(self): """When nothing is removed from a JSON-string metadata, the original byte representation is preserved (no ordering / whitespace /