From f65c1c35219a931f4772ae36118b6cccd2ecb1d4 Mon Sep 17 00:00:00 2001 From: Lengshuang <90967079+Lesereingrape@users.noreply.github.com> Date: Mon, 28 Sep 2026 11:40:18 +0800 Subject: [PATCH 1/2] Python: accept the registered audio/mpeg media type for MP3 input --- .../openai/agent_framework_openai/_chat_client.py | 2 +- .../_chat_completion_client.py | 2 +- .../openai/tests/openai/test_openai_chat_client.py | 7 +++++++ .../openai/test_openai_chat_completion_client.py | 12 ++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index 6ead6a55895..382285b7b77 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -2120,7 +2120,7 @@ def _prepare_content_for_openai( if content.has_top_level_media_type("audio"): if content.media_type and "wav" in content.media_type: format = "wav" - elif content.media_type and "mp3" in content.media_type: + elif content.media_type and ("mp3" in content.media_type or "mpeg" in content.media_type): format = "mp3" else: logger.warning("Unsupported audio media type: %s", content.media_type) diff --git a/python/packages/openai/agent_framework_openai/_chat_completion_client.py b/python/packages/openai/agent_framework_openai/_chat_completion_client.py index a816a391259..4108fa03868 100644 --- a/python/packages/openai/agent_framework_openai/_chat_completion_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_completion_client.py @@ -1315,7 +1315,7 @@ def _prepare_content_for_openai(self, content: Content) -> dict[str, Any]: case "data" | "uri" if content.has_top_level_media_type("audio"): if content.media_type and "wav" in content.media_type: audio_format = "wav" - elif content.media_type and "mp3" in content.media_type: + elif content.media_type and ("mp3" in content.media_type or "mpeg" in content.media_type): audio_format = "mp3" else: logger.debug("Unsupported audio media type: %s", content.media_type) diff --git a/python/packages/openai/tests/openai/test_openai_chat_client.py b/python/packages/openai/tests/openai/test_openai_chat_client.py index 9def958ba96..a825a8b15d2 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_client.py @@ -6961,6 +6961,13 @@ def test_prepare_content_for_openai_audio_content() -> None: assert result["type"] == "input_audio" assert result["input_audio"]["format"] == "mp3" + # Test MP3 audio content with the registered media type + mpeg_content = Content.from_uri(uri="data:audio/mpeg;base64,ghi789", media_type="audio/mpeg") + result = client._prepare_content_for_openai("user", mpeg_content) + assert result["type"] == "input_audio" + assert result["input_audio"]["data"] == "data:audio/mpeg;base64,ghi789" + assert result["input_audio"]["format"] == "mp3" + def test_prepare_content_for_openai_unsupported_content() -> None: """Test _prepare_content_for_openai with unsupported content types.""" diff --git a/python/packages/openai/tests/openai/test_openai_chat_completion_client.py b/python/packages/openai/tests/openai/test_openai_chat_completion_client.py index acdc233f4c2..804a8ddf52f 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_completion_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_completion_client.py @@ -602,6 +602,18 @@ def test_prepare_content_for_openai_data_content_image( assert result["input_audio"]["data"] == "//uQAAAAWGluZwAAAA8AAAACAAACcQ==" assert result["input_audio"]["format"] == "mp3" + # Test DataContent with MP3 audio using the registered media type + mpeg_data_content = Content.from_uri( + uri="data:audio/mpeg;base64,//uQAAAAWGluZwAAAA8AAAACAAACcQ==", + media_type="audio/mpeg", + ) + + result = client._prepare_content_for_openai(mpeg_data_content) # type: ignore + + assert result["type"] == "input_audio" + assert result["input_audio"]["data"] == "//uQAAAAWGluZwAAAA8AAAACAAACcQ==" + assert result["input_audio"]["format"] == "mp3" + unsupported_audio = Content.from_uri(uri="data:audio/ogg;base64,abc123", media_type="audio/ogg") assert client._prepare_content_for_openai(unsupported_audio) == {} # type: ignore From e0ba2eb1c9b7d6e981a2442aecd9e7a3d40285b4 Mon Sep 17 00:00:00 2001 From: "Grapette.L" <90967079+Lesereingrape@users.noreply.github.com> Date: Fri, 2 Oct 2026 12:38:41 +0800 Subject: [PATCH 2/2] Address review: match MP3 aliases instead of the "mpeg" substring Normalize the media type (drop parameters, case-fold) and compare it against the registered MP3 aliases so audio/mpegurl and audio/mpeg4-generic take the unsupported-content path in both serializers. --- .../agent_framework_openai/_chat_client.py | 3 ++- .../_chat_completion_client.py | 3 ++- .../openai/agent_framework_openai/_shared.py | 23 +++++++++++++++++++ .../tests/openai/test_openai_chat_client.py | 19 +++++++++++++++ .../test_openai_chat_completion_client.py | 21 +++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index bcee26eaeb4..57275a26c8b 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -117,6 +117,7 @@ from ._shared import ( AzureTokenProvider, _attach_prompt_cache_breakpoint, # pyright: ignore[reportPrivateUsage] + _is_mp3_media_type, # pyright: ignore[reportPrivateUsage] load_openai_service_settings, maybe_append_azure_endpoint_guidance, ) @@ -2150,7 +2151,7 @@ def _prepare_content_for_openai( if content.has_top_level_media_type("audio"): if content.media_type and "wav" in content.media_type: format = "wav" - elif content.media_type and ("mp3" in content.media_type or "mpeg" in content.media_type): + elif _is_mp3_media_type(content.media_type): format = "mp3" else: logger.warning("Unsupported audio media type: %s", content.media_type) diff --git a/python/packages/openai/agent_framework_openai/_chat_completion_client.py b/python/packages/openai/agent_framework_openai/_chat_completion_client.py index 4108fa03868..ff88f65cd04 100644 --- a/python/packages/openai/agent_framework_openai/_chat_completion_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_completion_client.py @@ -67,6 +67,7 @@ PROMPT_CACHE_BREAKPOINT_KEY, AzureTokenProvider, _attach_prompt_cache_breakpoint, # pyright: ignore[reportPrivateUsage] + _is_mp3_media_type, # pyright: ignore[reportPrivateUsage] load_openai_service_settings, maybe_append_azure_endpoint_guidance, ) @@ -1315,7 +1316,7 @@ def _prepare_content_for_openai(self, content: Content) -> dict[str, Any]: case "data" | "uri" if content.has_top_level_media_type("audio"): if content.media_type and "wav" in content.media_type: audio_format = "wav" - elif content.media_type and ("mp3" in content.media_type or "mpeg" in content.media_type): + elif _is_mp3_media_type(content.media_type): audio_format = "mp3" else: logger.debug("Unsupported audio media type: %s", content.media_type) diff --git a/python/packages/openai/agent_framework_openai/_shared.py b/python/packages/openai/agent_framework_openai/_shared.py index 2541f20b512..77e7dac867b 100644 --- a/python/packages/openai/agent_framework_openai/_shared.py +++ b/python/packages/openai/agent_framework_openai/_shared.py @@ -56,6 +56,29 @@ PROMPT_CACHE_BREAKPOINT_KEY = "prompt_cache_breakpoint" +MP3_MEDIA_TYPES = frozenset({"audio/mp3", "audio/mpeg", "audio/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3"}) + + +def _is_mp3_media_type( # pyright: ignore[reportUnusedFunction] + media_type: str | None, +) -> bool: + """Check whether an audio media type names MP3 audio. + + The media type is stripped of parameters and case-folded before it is compared against + the registered MP3 aliases, so that sibling MPEG subtypes such as ``audio/mpegurl`` or + ``audio/mpeg4-generic`` are not mistaken for MP3. + + Args: + media_type: The media type of the audio content, if any. + + Returns: + True if the media type is an alias of MP3 audio, False otherwise. + """ + if not media_type: + return False + return media_type.partition(";")[0].strip().lower() in MP3_MEDIA_TYPES + + def _attach_prompt_cache_breakpoint( # pyright: ignore[reportUnusedFunction] part: dict[str, Any], content: Content ) -> dict[str, Any]: diff --git a/python/packages/openai/tests/openai/test_openai_chat_client.py b/python/packages/openai/tests/openai/test_openai_chat_client.py index 5761b2e1528..c0a822dabf7 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_client.py @@ -7724,6 +7724,25 @@ def test_prepare_content_for_openai_audio_content() -> None: assert result["input_audio"]["data"] == "data:audio/mpeg;base64,ghi789" assert result["input_audio"]["format"] == "mp3" + # Test MP3 aliases with media type parameters and mixed case + parameterized_content = Content.from_uri( + uri="data:audio/mpeg;base64,jkl012", + media_type="Audio/MPEG; codecs=mpeg-3", + ) + result = client._prepare_content_for_openai("user", parameterized_content) + assert result["type"] == "input_audio" + assert result["input_audio"]["format"] == "mp3" + + +def test_prepare_content_for_openai_non_mp3_mpeg_audio_is_unsupported() -> None: + """Test _prepare_content_for_openai omits MPEG audio that is not MP3.""" + client = OpenAIChatClient(model="test-model", api_key="test-key") + + for media_type in ("audio/mpegurl", "audio/mpeg4-generic"): + content = Content.from_uri(uri=f"data:{media_type};base64,abc123", media_type=media_type) + + assert client._prepare_content_for_openai("user", content) == {} + def test_prepare_content_for_openai_unsupported_content() -> None: """Test _prepare_content_for_openai with unsupported content types.""" diff --git a/python/packages/openai/tests/openai/test_openai_chat_completion_client.py b/python/packages/openai/tests/openai/test_openai_chat_completion_client.py index 804a8ddf52f..e01c3d92d45 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_completion_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_completion_client.py @@ -614,11 +614,32 @@ def test_prepare_content_for_openai_data_content_image( assert result["input_audio"]["data"] == "//uQAAAAWGluZwAAAA8AAAACAAACcQ==" assert result["input_audio"]["format"] == "mp3" + # Test MP3 aliases with media type parameters and mixed case + parameterized_content = Content.from_uri( + uri="data:audio/mpeg;base64,//uQAAAAWGluZwAAAA8AAAACAAACcQ==", + media_type="Audio/MPEG; codecs=mpeg-3", + ) + + result = client._prepare_content_for_openai(parameterized_content) # type: ignore + + assert result["type"] == "input_audio" + assert result["input_audio"]["format"] == "mp3" + unsupported_audio = Content.from_uri(uri="data:audio/ogg;base64,abc123", media_type="audio/ogg") assert client._prepare_content_for_openai(unsupported_audio) == {} # type: ignore +def test_prepare_content_for_openai_non_mp3_mpeg_audio_is_unsupported() -> None: + """Test _prepare_content_for_openai omits MPEG audio that is not MP3.""" + client = OpenAIChatCompletionClient(model="test-model", api_key="test-key") + + for media_type in ("audio/mpegurl", "audio/mpeg4-generic"): + content = Content.from_uri(uri=f"data:{media_type};base64,abc123", media_type=media_type) + + assert client._prepare_content_for_openai(content) == {} # type: ignore + + def test_prepare_message_for_openai_omits_unsupported_content() -> None: client = OpenAIChatCompletionClient(model="test-model", api_key="test-key") unsupported = Content.from_uri(uri="data:text/plain;base64,SGVsbG8=", media_type="text/plain")