feat: save file attachments to disk before model processing#30153
feat: save file attachments to disk before model processing#30153ivanfernadezm99 wants to merge 1 commit into
Conversation
Add config option save_to_disk that writes attached files (images, PDFs, etc.) to a temp directory so they remain accessible to filesystem tools even when the model does not support the file modality. - New util/attachment-save.ts: decode data URLs and write to disk - Prompt pipeline hook in resolvePart() case 'data:' - savedPath threaded through message metadata - unsupportedParts() error now includes saved file path - Config: save_to_disk (default true) + save_to_disk_path (optional) - Tests: unit + integration + non-regression All 254 tests pass.
|
The following comment was made by an LLM, it may be inaccurate: Found 2 potentially related PRs:
These PRs tackle similar problems (making attachments accessible to tools when models don't support them), but the current PR (#30153) appears to be a more comprehensive solution with the new |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Issue for this PR
No linked issue — this addresses the unrecoverable file attachment problem when using text-only models.
Type of change
What does this PR do?
When a user uploads an image or PDF to OpenCode and the model doesn't support that modality (e.g.,
deepseek-v4-prowith images), the file data is irreversibly lost. The model rejects it with a generic error, and the base64 data URL in the message part is discarded.This PR saves the file to disk before it reaches the model layer, so tools like
tesseract(OCR) can still access it.How it works:
save_to_disk(bool, defaulttrue) +save_to_disk_path(optional string)attachment-save.ts: decodes base64 data URLs and writes the raw buffer to{tmpdir}/opencode/attachments/{sessionID}/{timestamp}-{filename}prompt.tsresolvePart()case"data:"— saves every file part before it enters the sessionmetadata.savedPathis threaded throughmessage-v2.tsso the path survives the pipelineunsupportedParts()intransform.tsnow appends" (saved to /path/to/file)"to the rejection messageConfig behavior:
save_to_disk: true(default) — all data URL files are written to disksave_to_disk: false— behavior is identical to current (no disk IO)save_to_disk_path: "/custom/path"— overrides the default temp dirFiles changed: 11 files, +392 / -3 lines:
util/attachment-save.ts,test/util/attachment-save.test.ts,test/config/attachment-save.test.tsconfig/attachment.ts(+core mirror),session/legacy.ts(metadata field),prompt.ts,message-v2.ts,transform.ts,transform.test.ts,prompt.test.tsHow did you verify your code works?
saveDataUrlToFile()with a real PNG base64, confirmed the file was written to/tmp/opencode/attachments/with the correct PNG headerimage,unsupportedParts()never fires and no error text is injectedScreenshots / recordings
N/A — no UI changes.
Checklist