From 58f0d2687adaa4c921080378b84b9aecef289bb7 Mon Sep 17 00:00:00 2001 From: Anmol Jaiswal <69112823+anmolg1997@users.noreply.github.com> Date: Wed, 29 Apr 2026 10:21:47 +0530 Subject: [PATCH] core[patch]: preserve mime_type and guard chat/completions for openai file blocks The base64 file branch in `_convert_openai_format_to_data_block` was hard-coding `mime_type="application/pdf"`, while the image branch right above used `parsed["mime_type"]`. So a non-PDF data URI (e.g. CSV) passed via the OpenAI Chat Completions file block shape got silently relabeled as PDF in the v1 content block, which is wrong for non-OpenAI chat models that consume v1 blocks via shared `_normalize_messages`. Changes: 1. Use `parsed["mime_type"]` in the base64 file branch, matching the image branch right above it. 2. In `convert_to_openai_data_block(api="chat/completions")`, raise a clear `ValueError` when MIME is not `application/pdf`, pointing the caller to the Responses API. This keeps Chat Completions semantics intact and fails fast with a friendlier error than OpenAI's raw 400. 3. Regression tests for both behaviors. Fixes #36939 --- .../messages/block_translators/openai.py | 11 ++++- .../messages/block_translators/test_openai.py | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/libs/core/langchain_core/messages/block_translators/openai.py b/libs/core/langchain_core/messages/block_translators/openai.py index 627459254c5ea..0efcbbfd096fc 100644 --- a/libs/core/langchain_core/messages/block_translators/openai.py +++ b/libs/core/langchain_core/messages/block_translators/openai.py @@ -93,6 +93,15 @@ def convert_to_openai_data_block( if block.get("source_type") == "base64" or "base64" in block: # Handle v0 format (Base64CB): {"source_type": "base64", "data": "...", ...} # Handle v1 format (IDCB): {"base64": "...", ...} + if ( + api == "chat/completions" + and block.get("mime_type") != "application/pdf" + ): + error_msg = ( + "OpenAI Chat Completions only supports application/pdf for " + "file data blocks. Use Responses API for other file MIME types." + ) + raise ValueError(error_msg) base64_data = block["data"] if "source_type" in block else block["base64"] file = {"file_data": f"data:{block['mime_type']};base64,{base64_data}"} if filename := block.get("filename"): @@ -545,7 +554,7 @@ def _extract_extras(block_dict: dict, known_keys: set[str]) -> dict[str, Any]: filename = block["file"].get("filename") return types.create_file_block( base64=parsed["data"], - mime_type="application/pdf", + mime_type=parsed["mime_type"], filename=filename, **all_extras, ) diff --git a/libs/core/tests/unit_tests/messages/block_translators/test_openai.py b/libs/core/tests/unit_tests/messages/block_translators/test_openai.py index b39b768993cda..f1ab7e79d2ed3 100644 --- a/libs/core/tests/unit_tests/messages/block_translators/test_openai.py +++ b/libs/core/tests/unit_tests/messages/block_translators/test_openai.py @@ -490,6 +490,16 @@ def test_convert_to_openai_data_block() -> None: result = convert_to_openai_data_block(block) assert result == expected + # File / base64 with non-PDF MIME (chat/completions) + block = { + "type": "file", + "base64": "", + "mime_type": "text/csv", + "filename": "test.csv", + } + with pytest.raises(ValueError, match="only supports application/pdf"): + convert_to_openai_data_block(block) + # Image / base64 block = { "type": "image", @@ -603,3 +613,39 @@ def test_convert_to_openai_data_block() -> None: expected = {"type": "input_file", "file_id": "file-abc123"} result = convert_to_openai_data_block(block, api="responses") assert result == expected + + +def test_convert_to_v1_file_block_preserves_non_pdf_mime_type() -> None: + message = HumanMessage( + content=[ + { + "type": "file", + "file": { + "filename": "sheet.csv", + "file_data": "data:text/csv;base64,aGVsbG8=", + }, + }, + { + "type": "file", + "file": { + "file_data": "data:text/plain;base64,aGVsbG8=", + }, + }, + ] + ) + + expected: list[types.ContentBlock] = [ + { + "type": "file", + "base64": "aGVsbG8=", + "mime_type": "text/csv", + "extras": {"filename": "sheet.csv"}, + }, + { + "type": "file", + "base64": "aGVsbG8=", + "mime_type": "text/plain", + }, + ] + + assert _content_blocks_equal_ignore_id(message.content_blocks, expected)