Repository navigation
feat(tools): add image caption fallback for FileReadTool when provide… - #10528
Open
kawayiYokami wants to merge 3 commits into
Open
kawayiYokami wants to merge 3 commits into
kawayiYokami wants to merge 3 commits into
Conversation
…r lacks image modality When the active chat provider does not accept image input, describe images returned by astrbot_file_read_tool with the provider configured via provider_settings.default_image_caption_provider_id, instead of handing back a raw ImageContent the model cannot consume. The fallback is fail-open: when the provider modality cannot be determined, the original ImageContent is returned unchanged. A data URI built from the already-compressed payload keeps sandbox runtimes working without host filesystem access.
Contributor
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/tools/computer_tools/fs.py" line_range="347" />
<code_context>
+ return "image" in modalities
+
+
+async def _provider_supports_image(
+ context: ContextWrapper[AstrAgentContext],
+) -> bool:
</code_context>
<issue_to_address>
**Image handling uses wrong provider**
When the agent turn uses a provider different from the session manager’s currently resolved provider, such as after a fallback, an explicit provider selection, or a concurrent session switch, `_provider_supports_image` re-resolves the session provider instead of checking the provider selected for the agent turn, so `FileReadTool.call` can return raw `ImageContent` to a text-only provider or caption an image for a vision-capable one. The agent receives image content it cannot consume or loses raw image input.
Have `_provider_supports_image` inspect the provider selected for the agent turn rather than re-resolving the session’s current provider.
Also at `astrbot/core/tools/computer_tools/fs.py:365-368`, `astrbot/core/tools/computer_tools/fs.py:566`.
</issue_to_address>
### Comment 2
<location path="tests/test_computer_fs_tools.py" line_range="951-952" />
<code_context>
+ assert result == "[Image description]: a red rectangle"
+ caption_provider.text_chat.assert_awaited_once()
+ captioned = caption_provider.text_chat.await_args.kwargs["image_urls"]
+ assert captioned == [captioned[0]]
+ assert captioned[0].startswith("data:image/jpeg;base64,")
+
+
</code_context>
<issue_to_address>
**Empty image payload passes test**
When the fallback produces a URI with the expected prefix but no valid encoded image bytes, `test_file_read_tool_captions_image_when_provider_lacks_image_modality` passes with an empty or truncated base64 payload: line 951 only checks that `image_urls` has one entry, line 952 checks only its prefix, and the mocked `text_chat` returns its caption regardless of the image. The test therefore passes even though the caption provider receives no usable image.
Assert that the URI payload decodes to non-empty image bytes, for example by checking its JPEG signature.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 2 findings to address first, and when the active provider lacks image support, image data read from local files is automatically sent to a separately configured caption provider, so an incorrect provider choice or routing decision could expose sensitive file contents externally. Reverting stops future transfers but cannot undo images that were already sent.
Blocking findings: astrbot/core/tools/computer_tools/fs.py:347, tests/test_computer_fs_tools.py:952
…n fallback The tool re-resolved the session provider to decide whether the model can consume images, which can diverge from the provider actually selected for the agent turn (after a fallback, an explicit model pick, or a concurrent session switch). The agent runner now publishes the turn provider's modalities on the run context and the tool reads that instead. Also strengthen the caption test to decode the data URI and assert real JPEG bytes instead of only checking the prefix.
Replacing the read result with an error string dropped the upstream behaviour of caching the image and handing the model its file path, which is what lets the agent forward the image to the user. When no caption provider is configured, the provider is unavailable, or the caption comes back empty, keep the original image and attach the reason as a text part instead. The failure reason is logged rather than embedded in the tool result, so internal error details no longer reach the model context.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation / 动机
EN: When the current chat provider does not support image input (
modalitieslacksimage) and a vision/caption provider is configured viaprovider_settings.default_image_caption_provider_id,astrbot_file_read_toolpreviously returned a rawImageContentthat the chat model cannot consume. The intention is to automatically describe the image as text (image-to-text fallback) when a multimodal model is available.中文: 当主对话模型不支持图片输入(
modalities未包含image)且已在provider_settings.default_image_caption_provider_id配置了多模态/图转文模型时,astrbot_file_read_tool原先会直接返回ImageContent,导致主模型无法消费。期望行为是:自动调用已配置的多模态模型将图片转述为文本(图转文兜底),仅在同时满足“主模型不支持图片 + 已配置图转文模型”时触发。Modifications / 改动点
EN:
astrbot/core/tools/computer_tools/fs.pymcp.typesforCallToolResult/ImageContentdetection._provider_supports_image(context)— resolves the active chat provider viaProviderManager.get_using_provider_async(fallback toget_using_provider), treatsNone/ empty / non-listmodalitiesas supporting image (backward-compatible, consistent withtool_loop_agent_runner)._caption_image_fallback(context, image_ref)— readsprovider_settings.default_image_caption_provider_idandimage_caption_prompt, resolves the caption provider viaget_provider_by_id, callstext_chat(prompt, image_urls=[image_ref]), returns[Image description]: {caption}or a clear error when not configured / unavailable / empty.FileReadTool.call— afterread_file_tool_result, if the result is anImageContentand the active provider lacksimage, builds adata:image/jpeg;base64,...URI from the already-compressed payload (so sandbox runtimes work without host file access) and delegates to the caption fallback; otherwise returns the original result. Text/PDF/docx paths are unaffected.中文:
astrbot/core/tools/computer_tools/fs.pymcp.types用于识别CallToolResult/ImageContent。_provider_supports_image(context)—— 通过ProviderManager.get_using_provider_async(回退到get_using_provider)解析当前会话的主对话模型,将modalities为None/ 空数组 / 非列表视为支持图片(向后兼容,与tool_loop_agent_runner一致)。_caption_image_fallback(context, image_ref)—— 读取provider_settings.default_image_caption_provider_id和image_caption_prompt,通过get_provider_by_id获取图转文模型,调用text_chat(prompt, image_urls=[image_ref]),成功返回[Image description]: {caption},未配置/不可用/空返回时给出明确错误提示。FileReadTool.call—— 在read_file_tool_result之后,若结果为ImageContent且主模型不支持image,则从已压缩的图片数据构造data:image/jpeg;base64,...URI(兼容 sandbox 容器内文件无法通过宿主机路径访问的场景)并走图转文兜底,否则原样返回;文本/PDF/docx 流程不受影响。This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
EN - Manual verification (7 cases, all passed):
modalities=[text,image,tool_use]→ returnsImageContentdirectlymodalities=[text]+ caption providervision-1configured →text_chatcalled withdata:image/jpeg;base64,...+ prompt, returns[Image description]: ...default_image_caption_provider_id=""→Error: ... no image caption provider is configured. Please set provider_settings.default_image_caption_provider_id.Error: ... is not available. Unable to read image file.modalities=[]or missing → treated as supporting image, returnsImageContenthello.txt) → unaffected, returns plain text中文 - 手动验证(7 项均通过):
modalities=[text,image,tool_use]→ 直接返回ImageContentmodalities=[text]且已配置vision-1→ 以data:image/jpeg;base64,...+ 提示词调用text_chat,返回[Image description]: ...default_image_caption_provider_id=""→ 提示未配置图转文模型modalities=[]或缺失 → 视为支持图片,直接返回ImageContenthello.txt)→ 不受影响,原样返回文本Existing suite against
master(tests/test_computer_fs_tools.py -k "not test_sandbox_file_download_handles_windows_remote_filename"): 15 passed / 5 pre-existing Windows failures unchanged.Checklist / 检查清单
😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
/ 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。
👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
/ 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。
🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in
requirements.txtandpyproject.toml./ 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到
requirements.txt和pyproject.toml文件相应位置。😮 My changes do not introduce malicious code.
/ 我的更改没有引入恶意代码。
Summary by Sourcery
Enable FileReadTool to provide text descriptions for images when the current agent model cannot consume image input.
New Features:
Bug Fixes:
Enhancements:
Tests: