Files
JARVIS/backend/app/agents/tools/hooks/executor.py
WIN-JHFT4D3SIVT\caoxiaozhu e5bd492d74 feat(agents): Phase 6 tool system refactoring
Phase 6.1: ToolRegistry infrastructure
- Add ToolManifest with ToolCategory, PermissionClass, SideEffectScope
- Add ToolRegistry singleton with register/get/unregister/list/search
- Add BaseTool abstract class with ReadTool/WriteTool/DBWriteTool/ExternalTool/NetworkTool subclasses
- Add migration layer for backward compatibility

Phase 6.2: Hook interception system
- Add HookType (PRE_TOOL_USE, POST_TOOL_USE, TOOL_ERROR, TOOL_SKIP)
- Add HookManager with singleton for hook registration
- Add HookExecutor for pre/post/error hook execution

Phase 6.3: Streaming execution
- Add StreamingToolExecutor with batch execution support

Phase 6.4: New builtin tools
- Add file_tools: GlobTool, GrepTool, ReadFileTool, WriteFileTool
- Add system_tools: BashTool, PowerShellTool
- Add dev_tools: LSPTools, GitTool
- Add collaboration_tools: TeamAgentTool, TaskBroadcastTool

Tests: 29 passed
2026-04-04 22:47:48 +08:00

171 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Hook 执行器 - Phase 6.2
执行 Hook 拦截逻辑。
"""
import time
from typing import Any
from app.agents.tools.hooks.manager import get_hook_manager
from app.agents.tools.hooks.types import (
HookDefinition,
HookResult,
HookType,
ExecutionContext,
)
class HookExecutor:
"""Hook 执行器
负责在工具执行前后执行 Hook 逻辑。
"""
def __init__(self):
self._manager = get_hook_manager()
async def execute_pre_hooks(
self, context: ExecutionContext
) -> tuple[bool, dict[str, Any] | None]:
"""执行 pre-tool Hook
Args:
context: 执行上下文
Returns:
(是否继续执行, 修改后的输入)
"""
hooks = self._manager.get_hooks(HookType.PRE_TOOL_USE, context.tool_name)
modified_input = context.tool_input
for hook in hooks:
try:
# 调用 hook handler
handler = hook.handler
if callable(handler):
result = await self._call_hook(handler, context)
if result and not result.continue_execution:
# Hook 决定中断执行
return False, modified_input
if result.modified_input is not None:
modified_input = result.modified_input
except Exception as e:
# Hook 出错,默认继续执行
pass
return True, modified_input
async def execute_post_hooks(self, context: ExecutionContext, result: Any) -> Any:
"""执行 post-tool Hook
Args:
context: 执行上下文
result: 工具执行结果
Returns:
修改后的结果
"""
hooks = self._manager.get_hooks(HookType.POST_TOOL_USE, context.tool_name)
modified_result = result
for hook in hooks:
try:
handler = hook.handler
if callable(handler):
hook_result = await self._call_hook(handler, context, modified_result)
if hook_result and hook_result.modified_output is not None:
modified_result = hook_result.modified_output
except Exception:
# Hook 出错,默认保留原结果
pass
return modified_result
async def execute_error_hooks(
self, context: ExecutionContext, error: Exception
) -> HookResult | None:
"""执行 error Hook
Args:
context: 执行上下文
error: 异常
Returns:
Hook 结果,如果返回 None 则继续传播错误
"""
hooks = self._manager.get_hooks(HookType.TOOL_ERROR, context.tool_name)
for hook in hooks:
try:
handler = hook.handler
if callable(handler):
result = await self._call_hook(handler, context, error)
if result is not None and result.continue_execution:
return result
except Exception:
# Hook 出错,继续执行其他 error hooks
pass
return None
async def execute_skip_check(self, context: ExecutionContext) -> bool:
"""检查是否应跳过工具执行
Args:
context: 执行上下文
Returns:
True 表示跳过False 表示执行
"""
hooks = self._manager.get_hooks(HookType.TOOL_SKIP, context.tool_name)
for hook in hooks:
try:
handler = hook.handler
if callable(handler):
result = await self._call_hook(handler, context)
if result is not None and isinstance(result, bool):
return result
except Exception:
# Hook 出错,默认不跳过
pass
return False
async def _call_hook(
self, handler: Any, context: ExecutionContext, *args: Any
) -> HookResult | None:
"""调用 Hook 处理函数
Args:
handler: Hook 处理函数
context: 执行上下文
*args: 额外参数
Returns:
Hook 结果
"""
import asyncio
# 如果是普通函数,直接调用
if asyncio.iscoroutinefunction(handler):
return await handler(context, *args)
else:
return handler(context, *args)
# 全局单例
_executor: HookExecutor | None = None
def get_hook_executor() -> HookExecutor:
"""获取全局 Hook 执行器
Returns:
全局 HookExecutor 实例
"""
global _executor
if _executor is None:
_executor = HookExecutor()
return _executor