feat: Enhance Hook System with HookHandler and Dispatcher

- Introduced HookHandlerEntry to manage hook processing with attributes like hook name, mode, order, timeout, and error policy.
- Implemented normalization methods for hook attributes to ensure valid configurations.
- Updated ComponentRegistry to support retrieval of hook handlers based on hook names, with sorting by mode and order.
- Refactored HookDispatcher to handle invocation of hooks, separating blocking and non-blocking handlers, and managing execution results.
- Added support for registering hook specifications and invoking hooks across supervisors in PluginRuntimeManager.
- Removed deprecated workflow step handling from PluginRunner, streamlining hook invocation responses.
This commit is contained in:
DrSmoothl
2026-03-24 19:04:05 +08:00
parent 865e4916e3
commit 0b0f47a444
6 changed files with 1247 additions and 523 deletions

View File

@@ -330,7 +330,6 @@ class PluginRunner:
self._rpc_client.register_method("plugin.invoke_message_gateway", self._handle_invoke)
self._rpc_client.register_method("plugin.emit_event", self._handle_event_invoke)
self._rpc_client.register_method("plugin.invoke_hook", self._handle_hook_invoke)
self._rpc_client.register_method("plugin.invoke_workflow_step", self._handle_workflow_step)
self._rpc_client.register_method("plugin.health", self._handle_health)
self._rpc_client.register_method("plugin.prepare_shutdown", self._handle_prepare_shutdown)
self._rpc_client.register_method("plugin.shutdown", self._handle_shutdown)
@@ -1053,73 +1052,28 @@ class PluginRunner:
)
except Exception as exc:
logger.error(f"插件 {plugin_id} hook_handler {component_name} 执行异常: {exc}", exc_info=True)
return envelope.make_response(payload={"success": False, "continue_processing": True})
return envelope.make_response(
payload={
"success": False,
"action": "continue",
"error_message": str(exc),
}
)
if raw is None:
result = {"success": True, "continue_processing": True}
result = {"success": True, "action": "continue"}
elif isinstance(raw, dict):
result = {
"success": True,
"continue_processing": raw.get("continue_processing", True),
"action": str(raw.get("action", "continue") or "continue").strip().lower() or "continue",
"modified_kwargs": raw.get("modified_kwargs"),
"custom_result": raw.get("custom_result"),
}
else:
result = {"success": True, "continue_processing": True, "custom_result": raw}
result = {"success": True, "action": "continue", "custom_result": raw}
return envelope.make_response(payload=result)
async def _handle_workflow_step(self, envelope: Envelope) -> Envelope:
"""处理 WorkflowStep 调用请求
与通用 invoke 不同,会将返回值规范化为
{hook_result, modified_message, stage_output} 格式。
"""
try:
invoke = InvokePayload.model_validate(envelope.payload)
except Exception as e:
return envelope.make_error_response(ErrorCode.E_BAD_PAYLOAD.value, str(e))
plugin_id = envelope.plugin_id
meta = self._loader.get_plugin(plugin_id)
if meta is None:
return envelope.make_error_response(
ErrorCode.E_PLUGIN_NOT_FOUND.value,
f"插件 {plugin_id} 未加载",
)
component_name = invoke.component_name
handler_method = self._resolve_component_handler(meta, component_name)
if handler_method is None or not callable(handler_method):
return envelope.make_error_response(
ErrorCode.E_METHOD_NOT_ALLOWED.value,
f"插件 {plugin_id} 无组件: {component_name}",
)
try:
raw = (
await handler_method(**invoke.args)
if inspect.iscoroutinefunction(handler_method)
else handler_method(**invoke.args)
)
# 规范化返回值
if isinstance(raw, str):
result = {"hook_result": raw}
elif isinstance(raw, dict):
result = raw
result.setdefault("hook_result", "continue")
else:
result = {"hook_result": "continue"}
resp_payload = InvokeResultPayload(success=True, result=result)
return envelope.make_response(payload=resp_payload.model_dump())
except Exception as e:
logger.error(f"插件 {plugin_id} workflow_step {component_name} 执行异常: {e}", exc_info=True)
resp_payload = InvokeResultPayload(success=False, result=str(e))
return envelope.make_response(payload=resp_payload.model_dump())
async def _handle_health(self, envelope: Envelope) -> Envelope:
"""处理健康检查"""
uptime_ms = int((time.monotonic() - self._start_time) * 1000)