From 56ba762e22066fea5f3e8d67622e764317fc9de8 Mon Sep 17 00:00:00 2001 From: Abhinavexist Date: Sat, 18 Jul 2026 03:37:49 +0530 Subject: [PATCH] fix: derive audio() format from data-URL MIME (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit audio() derived format from the file extension only, so a data: URI (no extension) fell through to "wav" — mislabeling every base64 audio part — and skipped the MIME lookup and blacklist check the other builders run. Read the data-URL MIME, use its subtype as the format (the server prepends audio/), run _assert_allowed, and have auto_part forward the detected format. --- src/interfaze/_inputs.py | 43 +++++++++++++++++++++++++-------- tests/test_inputs_and_client.py | 15 ++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/interfaze/_inputs.py b/src/interfaze/_inputs.py index c43069a..a6b63ef 100644 --- a/src/interfaze/_inputs.py +++ b/src/interfaze/_inputs.py @@ -10,14 +10,35 @@ BytesLike = Union[bytes, bytearray] _EXT_MIME = { - "png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg", "webp": "image/webp", - "gif": "image/gif", "bmp": "image/bmp", "heic": "image/heic", "heif": "image/heif", - "pdf": "application/pdf", "csv": "text/csv", "tsv": "text/tab-separated-values", - "xml": "application/xml", "json": "application/json", "txt": "text/plain", - "md": "text/markdown", "markdown": "text/markdown", "yaml": "application/yaml", "yml": "application/yaml", - "wav": "audio/wav", "mp3": "audio/mpeg", "m4a": "audio/mp4", "ogg": "audio/ogg", "flac": "audio/flac", - "mp4": "video/mp4", "mov": "video/quicktime", "webm": "video/webm", "avi": "video/x-msvideo", - "mkv": "video/x-matroska", "3gp": "video/3gpp", + "png": "image/png", + "jpg": "image/jpeg", + "jpeg": "image/jpeg", + "webp": "image/webp", + "gif": "image/gif", + "bmp": "image/bmp", + "heic": "image/heic", + "heif": "image/heif", + "pdf": "application/pdf", + "csv": "text/csv", + "tsv": "text/tab-separated-values", + "xml": "application/xml", + "json": "application/json", + "txt": "text/plain", + "md": "text/markdown", + "markdown": "text/markdown", + "yaml": "application/yaml", + "yml": "application/yaml", + "wav": "audio/wav", + "mp3": "audio/mpeg", + "m4a": "audio/mp4", + "ogg": "audio/ogg", + "flac": "audio/flac", + "mp4": "video/mp4", + "mov": "video/quicktime", + "webm": "video/webm", + "avi": "video/x-msvideo", + "mkv": "video/x-matroska", + "3gp": "video/3gpp", } @@ -70,7 +91,9 @@ def file(src: str, *, filename: Optional[str] = None, format: Optional[str] = No def audio(src: str, *, format: Optional[str] = None) -> Dict[str, Any]: """Audio content part via ``input_audio`` (``audio_url`` is a dead field in Interfaze).""" - fmt = format or _ext_of(src) or "wav" + mime = _mime_from_data_url(src) + _assert_allowed(mime or _EXT_MIME.get(_ext_of(src) or "")) + fmt = format or (mime.split("/", 1)[-1] if mime else _ext_of(src)) or "wav" return {"type": "input_audio", "input_audio": {"data": src, "format": fmt}} @@ -85,5 +108,5 @@ def auto_part(src: str, *, filename: Optional[str] = None, format: Optional[str] if mime and mime.startswith("image/"): return image(src) if mime and mime.startswith("audio/"): - return audio(src, format=format) if format else audio(src) + return audio(src, format=format or mime.split("/", 1)[-1]) return file(src, filename=filename, format=format) diff --git a/tests/test_inputs_and_client.py b/tests/test_inputs_and_client.py index 34eb8d7..3f3ff35 100644 --- a/tests/test_inputs_and_client.py +++ b/tests/test_inputs_and_client.py @@ -27,6 +27,16 @@ def test_audio_uses_input_audio(): assert part["type"] == "input_audio" and part["input_audio"]["format"] == "wav" +def test_audio_data_uri_uses_mime_subtype(): + assert inputs.audio("data:audio/mpeg;base64,AAAA")["input_audio"]["format"] == "mpeg" + assert inputs.audio("data:audio/wav;base64,AAAA")["input_audio"]["format"] == "wav" + + +def test_audio_rejects_blacklisted_data_uri(): + with pytest.raises(InterfazeError): + inputs.audio("data:image/gif;base64,AAAA") + + def test_gif_rejected(): with pytest.raises(InterfazeError): inputs.image("https://x.com/a.gif") @@ -53,6 +63,11 @@ def test_auto_part_routing(): assert inputs.auto_part("https://x.com/a.mp4")["type"] == "file" +def test_auto_part_forwards_audio_data_uri_format(): + part = inputs.auto_part("data:audio/mpeg;base64,AAAA") + assert part["type"] == "input_audio" and part["input_audio"]["format"] == "mpeg" + + # ---- client surface ---- def test_curated_surface(): c = Interfaze(api_key="t")