mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-16 01:40:15 +08:00
fix(message_tools): throw exception and block message sending when path does not exist (#8149)
* fix(message_tools): 路径不存在时抛出异常并阻止消息发送 - _resolve_path_from_sandbox 在所有解析路径均失败时改为抛出 FileNotFoundError,而非静默返回原始路径,避免将无效路径传递给下游组件 - 新增 component_type 关键字参数,使错误信息能明确指出是 image/record/video/file 哪类资源路径缺失 - 在 call 方法中捕获 FileNotFoundError 并提前返回错误字符串,确保路径无效时不会继续构建或发送任何消息组件 - 补充单元测试,验证缺失图片路径场景下 send_message 不会被调用 * Update tests/unit/test_message_tools.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * fix(tools): propagate sandbox error instead of masking as FileNotFoundError --------- Co-authored-by: Ruochen Pan <badbatch0x01@gmail.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: RC-CHN <1051989940@qq.com>
This commit is contained in:
@@ -79,8 +79,13 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
)
|
||||
|
||||
async def _resolve_path_from_sandbox(
|
||||
self, context: ContextWrapper[AstrAgentContext], path: str
|
||||
self,
|
||||
context: ContextWrapper[AstrAgentContext],
|
||||
path: str,
|
||||
*,
|
||||
component_type: str = "file",
|
||||
) -> tuple[str, bool]:
|
||||
path = str(path)
|
||||
# if the path is relative, check if the file exists in user's local workspace
|
||||
if not os.path.isabs(path):
|
||||
unified_msg_origin = context.context.event.unified_msg_origin
|
||||
@@ -115,8 +120,9 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
return local_path, True
|
||||
except Exception as exc:
|
||||
logger.warning(f"Failed to check/download file from sandbox: {exc}")
|
||||
raise
|
||||
|
||||
return path, False
|
||||
raise FileNotFoundError(f"{component_type} path does not exist: {path}")
|
||||
|
||||
async def call(
|
||||
self, context: ContextWrapper[AstrAgentContext], **kwargs
|
||||
@@ -155,7 +161,7 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
url = msg.get("url")
|
||||
if path:
|
||||
local_path, _ = await self._resolve_path_from_sandbox(
|
||||
context, path
|
||||
context, path, component_type="image"
|
||||
)
|
||||
components.append(Comp.Image.fromFileSystem(path=local_path))
|
||||
elif url:
|
||||
@@ -167,7 +173,7 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
url = msg.get("url")
|
||||
if path:
|
||||
local_path, _ = await self._resolve_path_from_sandbox(
|
||||
context, path
|
||||
context, path, component_type="record"
|
||||
)
|
||||
components.append(Comp.Record.fromFileSystem(path=local_path))
|
||||
elif url:
|
||||
@@ -179,7 +185,7 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
url = msg.get("url")
|
||||
if path:
|
||||
local_path, _ = await self._resolve_path_from_sandbox(
|
||||
context, path
|
||||
context, path, component_type="video"
|
||||
)
|
||||
components.append(Comp.Video.fromFileSystem(path=local_path))
|
||||
elif url:
|
||||
@@ -197,7 +203,7 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
)
|
||||
if path:
|
||||
local_path, _ = await self._resolve_path_from_sandbox(
|
||||
context, path
|
||||
context, path, component_type="file"
|
||||
)
|
||||
components.append(Comp.File(name=name, file=local_path))
|
||||
elif url:
|
||||
@@ -213,6 +219,8 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
||||
return (
|
||||
f"error: unsupported message type '{msg_type}' at index {idx}."
|
||||
)
|
||||
except FileNotFoundError as exc:
|
||||
return f"error: {exc}"
|
||||
except Exception as exc:
|
||||
return f"error: failed to build messages[{idx}] component: {exc}"
|
||||
|
||||
|
||||
@@ -133,3 +133,31 @@ async def test_send_message_empty_messages_returns_error():
|
||||
result = await tool.call(ctx, messages=[], session="oc_xxx")
|
||||
assert "error:" in result
|
||||
assert "messages" in result.lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_missing_image_path_stops_before_send(tmp_path, monkeypatch):
|
||||
"""Missing image paths fail before sending any message components."""
|
||||
tool = SendMessageToUserTool()
|
||||
ctx = _make_context()
|
||||
missing_image_path = tmp_path / "missing.png"
|
||||
|
||||
async def mock_get_booter(*args, **kwargs):
|
||||
del args, kwargs
|
||||
raise RuntimeError("sandbox unavailable")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"astrbot.core.tools.message_tools.get_booter",
|
||||
mock_get_booter,
|
||||
)
|
||||
|
||||
result = await tool.call(
|
||||
ctx,
|
||||
messages=[
|
||||
{"type": "plain", "text": "before image"},
|
||||
{"type": "image", "path": str(missing_image_path)},
|
||||
],
|
||||
)
|
||||
|
||||
assert "error: failed to build messages[1] component: sandbox unavailable" in result
|
||||
ctx.context.context.send_message.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user