fix(sdk): handle base64 reads with unknown file extensions (#3663)

Closes #3657

---

Binary file reads now respect the backend-declared `encoding` before
deciding whether content should be line-numbered as text. This prevents
base64 payloads for extensions outside the multimodal block map, like
`.docx`, from being treated as plain text.

## Changes
- Route `FilesystemMiddleware` read results with `encoding="base64"`
through binary `content_blocks` even when `_get_file_type` returns
`text`.
- Fall back to a generic `file` block for unknown binary extensions
while still using `mimetypes.guess_type` to preserve the best available
media type.
- Keep existing image/audio/video handling unchanged by using the mapped
block type whenever the extension is recognized.
This commit is contained in:
Mason Daugherty
2026-05-29 20:50:52 -04:00
committed by GitHub
parent 54485a3058
commit 9857a08b61
2 changed files with 57 additions and 2 deletions
@@ -900,12 +900,19 @@ class FilesystemMiddleware(AgentMiddleware[FilesystemState, ContextT, ResponseT]
)
file_type = _get_file_type(validated_path)
encoding = read_result.file_data.get("encoding", "utf-8")
content = read_result.file_data["content"]
if file_type != "text":
# Route on the backend-declared encoding first: `"base64"` means the
# content is binary and must never be line-numbered as text, even
# when the extension is absent from `_EXTENSION_TO_FILE_TYPE` (#3657).
# The extension map is only consulted to pick the multimodal block
# type; unknown binary extensions fall back to the generic `"file"`.
if encoding == "base64" or file_type != "text":
block_type = file_type if file_type != "text" else "file"
mime_type = mimetypes.guess_type("file" + Path(validated_path).suffix)[0] or "application/octet-stream"
return ToolMessage(
content_blocks=cast("list[ContentBlock]", [{"type": file_type, "base64": content, "mime_type": mime_type}]),
content_blocks=cast("list[ContentBlock]", [{"type": block_type, "base64": content, "mime_type": mime_type}]),
name="read_file",
tool_call_id=tool_call_id,
additional_kwargs={"read_file_path": validated_path, "read_file_media_type": mime_type},
@@ -1,3 +1,4 @@
import mimetypes
import time
from unittest.mock import patch
@@ -1218,6 +1219,53 @@ class TestFilesystemMiddleware:
assert result.content[0]["mime_type"] == "image/png"
assert result.content[0]["base64"] == "<base64_data>"
def test_read_file_base64_unknown_extension_returns_file_block(self):
"""Binary reads route on `encoding`, not the extension map (#3657).
A backend may return `encoding="base64"` for a file whose extension is
absent from `_EXTENSION_TO_FILE_TYPE` (e.g. `.docx`). The base64 payload
must be emitted as a generic `file` content block, never line-numbered
as text into the LLM context.
"""
class DocxBackend(StateBackend):
def read(self, path, *, offset=0, limit=100):
return ReadResult(
file_data={
"content": "<docx_base64_data>",
"encoding": "base64",
}
)
middleware = FilesystemMiddleware(backend=DocxBackend())
state = FilesystemState(messages=[], files={})
runtime = ToolRuntime(
state=state,
context=None,
tool_call_id="docx-read-1",
store=None,
stream_writer=lambda _: None,
config={},
)
read_file_tool = next(tool for tool in middleware.tools if tool.name == "read_file")
result = read_file_tool.invoke({"file_path": "/app/report.docx", "runtime": runtime})
assert isinstance(result, ToolMessage)
assert result.status == "success"
# Binary content_blocks, not a line-numbered text string.
assert isinstance(result.content, list)
assert result.content[0]["type"] == "file"
assert result.content[0]["base64"] == "<docx_base64_data>"
# The block carries the best media type `mimetypes` can resolve for the
# extension, falling back to `application/octet-stream`. The resolved
# value is platform-dependent (`.docx` maps to the full Office type on
# macOS/Linux but to `application/octet-stream` on Windows, where
# `mimetypes` reads the registry), so mirror the production logic rather
# than hardcoding a single value.
expected_mime = mimetypes.guess_type("file.docx")[0] or "application/octet-stream"
assert result.content[0]["mime_type"] == expected_mime
def test_read_file_image_returns_error_when_download_fails(self):
"""Image reads should return a clear backend error string."""