diff --git a/astrbot/core/provider/func_tool_manager.py b/astrbot/core/provider/func_tool_manager.py index f3b3cb77c2..7afcbbd09a 100644 --- a/astrbot/core/provider/func_tool_manager.py +++ b/astrbot/core/provider/func_tool_manager.py @@ -266,6 +266,19 @@ async def call(self, context: Any, **kwargs: Any) -> Any: if call_override is not None and call_override is not FunctionTool.call: return await self._wrapped.call(context, **kwargs) + run = getattr(self._wrapped, "run", None) + if run is not None: + event = context.context.event + result = run(event, **kwargs) + if _inspect.isasyncgen(result): + last: Any = None + async for item in result: + last = item + return last + if _inspect.isawaitable(result): + return await result + return result + return "error: tool has no callable handler" diff --git a/tests/unit/test_tool_permission.py b/tests/unit/test_tool_permission.py index 9e69609e6b..40a09cbb81 100644 --- a/tests/unit/test_tool_permission.py +++ b/tests/unit/test_tool_permission.py @@ -254,6 +254,27 @@ async def call(self, context, **kwargs): assert result == "from call()" +@pytest.mark.asyncio +async def test_guarded_tool_delegates_to_wrapped_run(): + _clear_tool_permissions() + mgr = FunctionToolManager() + + class RunnableTool(FunctionTool): + async def run(self, event, **kwargs): + return f"from run(): {event.get_sender_id()} {kwargs['value']}" + + wrapped = RunnableTool( + name="has_run", + description="desc", + parameters={}, + ) + guarded = _PermissionGuardedTool(wrapped, mgr) + context = _make_context(sender_id="runner") + + result = await guarded.call(context, value="ok") + assert result == "from run(): runner ok" + + @pytest.mark.asyncio async def test_guarded_tool_handles_async_generator_handler(): _clear_tool_permissions()