mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-01 01:10:21 +08:00
feat: update permission handling to delegate event context in guarded tools (#9001)
This commit is contained in:
@@ -249,9 +249,10 @@ class _PermissionGuardedTool(FunctionTool):
|
||||
if error is not None:
|
||||
return error
|
||||
|
||||
# Delegate to handler first (plugin tools).
|
||||
# @filter.llm_tool decorated tools have a handler attribute, which is the actual callable.
|
||||
if self._wrapped.handler is not None:
|
||||
result = self._wrapped.handler(context, **kwargs)
|
||||
event = context.context.event
|
||||
result = self._wrapped.handler(event, **kwargs)
|
||||
if _inspect.isasyncgen(result):
|
||||
last: Any = None
|
||||
async for item in result:
|
||||
@@ -261,11 +262,12 @@ class _PermissionGuardedTool(FunctionTool):
|
||||
return await result
|
||||
return result
|
||||
|
||||
# Fall back to overridden call() on subclasses (e.g. MCPTool).
|
||||
# If the tool has a "call" method that is not the default FunctionTool.call, invoke it.
|
||||
call_override = getattr(type(self._wrapped), "call", None)
|
||||
if call_override is not None and call_override is not FunctionTool.call:
|
||||
return await self._wrapped.call(context, **kwargs)
|
||||
|
||||
# Compatibility fallback: if the tool has a "run" method, invoke it. This is for legacy tools that don't use the new handler/call interface.
|
||||
run = getattr(self._wrapped, "run", None)
|
||||
if run is not None:
|
||||
event = context.context.event
|
||||
|
||||
@@ -174,16 +174,19 @@ async def test_check_permission_passes_for_member_when_configured_member():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guarded_tool_delegates_when_permission_passes():
|
||||
async def test_guarded_tool_delegates_handler_with_event_when_permission_passes():
|
||||
_clear_tool_permissions()
|
||||
mgr = FunctionToolManager()
|
||||
|
||||
called = False
|
||||
received_event = None
|
||||
|
||||
async def handler(ctx, **kw):
|
||||
async def handler(event, **kw):
|
||||
nonlocal called
|
||||
nonlocal received_event
|
||||
called = True
|
||||
return "ok"
|
||||
received_event = event
|
||||
return f"ok:{event.get_sender_id()}:{kw['value']}"
|
||||
|
||||
wrapped = FunctionTool(
|
||||
name="delegated",
|
||||
@@ -194,9 +197,10 @@ async def test_guarded_tool_delegates_when_permission_passes():
|
||||
guarded = _PermissionGuardedTool(wrapped, mgr)
|
||||
context = _make_context(role="member")
|
||||
|
||||
result = await guarded.call(context)
|
||||
result = await guarded.call(context, value="sentinel")
|
||||
assert called
|
||||
assert result == "ok"
|
||||
assert received_event is context.context.event
|
||||
assert result == "ok:user_123:sentinel"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -280,7 +284,8 @@ async def test_guarded_tool_handles_async_generator_handler():
|
||||
_clear_tool_permissions()
|
||||
mgr = FunctionToolManager()
|
||||
|
||||
async def gen_handler(ctx, **kw): # type: ignore[misc]
|
||||
async def gen_handler(event, **kw): # type: ignore[misc]
|
||||
assert event is context.context.event
|
||||
yield "A"
|
||||
yield "B"
|
||||
yield "C"
|
||||
|
||||
Reference in New Issue
Block a user