refactor: component_registry更易理解
This commit is contained in:
committed by
DrSmoothl
parent
ca6fd96d4c
commit
14a0c21cbf
@@ -1,7 +1,7 @@
|
|||||||
"""Host-side ComponentRegistry
|
"""Host-side ComponentRegistry
|
||||||
|
|
||||||
对齐旧系统 component_registry.py 的核心能力:
|
对齐旧系统 component_registry.py 的核心能力:
|
||||||
- 按类型注册组件(action / command / tool / event_handler / workflow_step)
|
- 按类型注册组件(action / command / tool / event_handler / workflow_handler / message_gateway)
|
||||||
- 命名空间 (plugin_id.component_name)
|
- 命名空间 (plugin_id.component_name)
|
||||||
- 命令正则匹配
|
- 命令正则匹配
|
||||||
- 组件启用/禁用
|
- 组件启用/禁用
|
||||||
@@ -9,8 +9,10 @@
|
|||||||
- 注册统计
|
- 注册统计
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional
|
from enum import Enum
|
||||||
|
from typing import Any, Dict, List, Optional, Set, TypedDict, Tuple
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
@@ -18,8 +20,28 @@ from src.common.logger import get_logger
|
|||||||
logger = get_logger("plugin_runtime.host.component_registry")
|
logger = get_logger("plugin_runtime.host.component_registry")
|
||||||
|
|
||||||
|
|
||||||
class RegisteredComponent:
|
class ComponentTypes(str, Enum):
|
||||||
"""已注册的组件条目"""
|
ACTION = "ACTION"
|
||||||
|
COMMAND = "COMMAND"
|
||||||
|
TOOL = "TOOL"
|
||||||
|
EVENT_HANDLER = "EVENT_HANDLER"
|
||||||
|
WORKFLOW_HANDLER = "WORKFLOW_HANDLER"
|
||||||
|
MESSAGE_GATEWAY = "MESSAGE_GATEWAY"
|
||||||
|
|
||||||
|
|
||||||
|
class StatusDict(TypedDict):
|
||||||
|
total: int
|
||||||
|
ACTION: int
|
||||||
|
COMMAND: int
|
||||||
|
TOOL: int
|
||||||
|
EVENT_HANDLER: int
|
||||||
|
WORKFLOW_HANDLER: int
|
||||||
|
MESSAGE_GATEWAY: int
|
||||||
|
plugins: int
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentEntry:
|
||||||
|
"""组件条目"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"name",
|
"name",
|
||||||
@@ -28,31 +50,74 @@ class RegisteredComponent:
|
|||||||
"plugin_id",
|
"plugin_id",
|
||||||
"metadata",
|
"metadata",
|
||||||
"enabled",
|
"enabled",
|
||||||
"_compiled_pattern",
|
"compiled_pattern",
|
||||||
|
"disabled_session",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
self,
|
self.name: str = name
|
||||||
name: str,
|
self.full_name: str = f"{plugin_id}.{name}"
|
||||||
component_type: str,
|
self.component_type: ComponentTypes = ComponentTypes(component_type)
|
||||||
plugin_id: str,
|
self.plugin_id: str = plugin_id
|
||||||
metadata: Dict[str, Any],
|
self.metadata: Dict[str, Any] = metadata
|
||||||
) -> None:
|
self.enabled: bool = metadata.get("enabled", True)
|
||||||
self.name = name
|
self.disabled_session: Set[str] = set()
|
||||||
self.full_name = f"{plugin_id}.{name}"
|
|
||||||
self.component_type = component_type
|
|
||||||
self.plugin_id = plugin_id
|
|
||||||
self.metadata = metadata
|
|
||||||
self.enabled = metadata.get("enabled", True)
|
|
||||||
|
|
||||||
# 预编译命令正则(仅 command 类型)
|
|
||||||
self._compiled_pattern: Optional[re.Pattern] = None
|
class ActionEntry(ComponentEntry):
|
||||||
if component_type == "command":
|
"""Action 组件条目"""
|
||||||
if pattern := metadata.get("command_pattern", ""):
|
|
||||||
try:
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
self._compiled_pattern = re.compile(pattern)
|
super().__init__(name, component_type, plugin_id, metadata)
|
||||||
except re.error as e:
|
|
||||||
logger.warning(f"命令 {self.full_name} 正则编译失败: {e}")
|
|
||||||
|
class CommandEntry(ComponentEntry):
|
||||||
|
"""Command 组件条目"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
self.compiled_pattern: Optional[re.Pattern] = None
|
||||||
|
self.aliases: List[str] = metadata.get("aliases", [])
|
||||||
|
if pattern := metadata.get("command_pattern", ""):
|
||||||
|
try:
|
||||||
|
self.compiled_pattern = re.compile(pattern)
|
||||||
|
except re.error as e:
|
||||||
|
logger.warning(f"命令 {self.full_name} 正则编译失败: {e}")
|
||||||
|
super().__init__(name, component_type, plugin_id, metadata)
|
||||||
|
|
||||||
|
|
||||||
|
class ToolEntry(ComponentEntry):
|
||||||
|
"""Tool 组件条目"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
self.description: str = metadata.get("description", "")
|
||||||
|
self.parameters: List[Dict[str, Any]] = metadata.get("parameters", [])
|
||||||
|
self.parameters_raw: List[Dict[str, Any]] = metadata.get("parameters_raw", [])
|
||||||
|
super().__init__(name, component_type, plugin_id, metadata)
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandlerEntry(ComponentEntry):
|
||||||
|
"""EventHandler 组件条目"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
self.event_type: str = metadata.get("event_type", "")
|
||||||
|
self.weight: int = metadata.get("weight", 0)
|
||||||
|
super().__init__(name, component_type, plugin_id, metadata)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowHandlerEntry(ComponentEntry):
|
||||||
|
"""WorkflowHandler 组件条目"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
self.stage: str = metadata.get("stage", "")
|
||||||
|
self.priority: int = metadata.get("priority", 0)
|
||||||
|
super().__init__(name, component_type, plugin_id, metadata)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageGatewayEntry(ComponentEntry):
|
||||||
|
"""MessageGateway 组件条目"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
super().__init__(name, component_type, plugin_id, metadata)
|
||||||
|
|
||||||
|
|
||||||
class ComponentRegistry:
|
class ComponentRegistry:
|
||||||
@@ -64,19 +129,15 @@ class ComponentRegistry:
|
|||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
# 全量索引
|
# 全量索引
|
||||||
self._components: Dict[str, RegisteredComponent] = {} # full_name -> comp
|
self._components: Dict[str, ComponentEntry] = {} # full_name -> comp
|
||||||
|
|
||||||
# 按类型索引
|
# 按类型索引
|
||||||
self._by_type: Dict[str, Dict[str, RegisteredComponent]] = {
|
self._by_type: Dict[ComponentTypes, Dict[str, ComponentEntry]] = {
|
||||||
"action": {},
|
comp_type: {} for comp_type in ComponentTypes
|
||||||
"command": {},
|
} # component_type -> (full_name -> comp)
|
||||||
"tool": {},
|
|
||||||
"event_handler": {},
|
|
||||||
"workflow_step": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
# 按插件索引
|
# 按插件索引
|
||||||
self._by_plugin: Dict[str, List[RegisteredComponent]] = {}
|
self._by_plugin: Dict[str, List[ComponentEntry]] = {}
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
"""清空全部组件注册状态。"""
|
"""清空全部组件注册状态。"""
|
||||||
@@ -85,47 +146,63 @@ class ComponentRegistry:
|
|||||||
type_dict.clear()
|
type_dict.clear()
|
||||||
self._by_plugin.clear()
|
self._by_plugin.clear()
|
||||||
|
|
||||||
# ──── 注册 / 注销 ─────────────────────────────────────────
|
# ====== 注册 / 注销 ======
|
||||||
|
def register_component(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> bool:
|
||||||
|
"""注册单个组件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: 组件名称(不含插件id前缀)
|
||||||
|
component_type: 组件类型(如 `ACTION`、`COMMAND` 等)
|
||||||
|
plugin_id: 插件id
|
||||||
|
metadata: 组件元数据
|
||||||
|
Returns:
|
||||||
|
success (bool): 是否成功注册(失败原因通常是组件类型无效)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if component_type == ComponentTypes.ACTION:
|
||||||
|
comp = ActionEntry(name, component_type, plugin_id, metadata)
|
||||||
|
elif component_type == ComponentTypes.COMMAND:
|
||||||
|
comp = CommandEntry(name, component_type, plugin_id, metadata)
|
||||||
|
elif component_type == ComponentTypes.TOOL:
|
||||||
|
comp = ToolEntry(name, component_type, plugin_id, metadata)
|
||||||
|
elif component_type == ComponentTypes.EVENT_HANDLER:
|
||||||
|
comp = EventHandlerEntry(name, component_type, plugin_id, metadata)
|
||||||
|
elif component_type == ComponentTypes.WORKFLOW_HANDLER:
|
||||||
|
comp = WorkflowHandlerEntry(name, component_type, plugin_id, metadata)
|
||||||
|
elif component_type == ComponentTypes.MESSAGE_GATEWAY:
|
||||||
|
comp = MessageGatewayEntry(name, component_type, plugin_id, metadata)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"组件类型 {component_type} 不存在")
|
||||||
|
except ValueError:
|
||||||
|
logger.error(f"组件类型 {component_type} 不存在")
|
||||||
|
return False
|
||||||
|
|
||||||
def register_component(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
component_type: str,
|
|
||||||
plugin_id: str,
|
|
||||||
metadata: Dict[str, Any],
|
|
||||||
) -> bool:
|
|
||||||
"""注册单个组件。"""
|
|
||||||
comp = RegisteredComponent(name, component_type, plugin_id, metadata)
|
|
||||||
if comp.full_name in self._components:
|
if comp.full_name in self._components:
|
||||||
logger.warning(f"组件 {comp.full_name} 已存在,覆盖")
|
logger.warning(f"组件 {comp.full_name} 已存在,覆盖")
|
||||||
old_comp = self._components[comp.full_name]
|
old_comp = self._components[comp.full_name]
|
||||||
# 从 _by_plugin 列表中移除旧条目,防止幽灵组件堆积
|
# 从 _by_plugin 列表中移除旧条目,防止幽灵组件堆积
|
||||||
old_list = self._by_plugin.get(old_comp.plugin_id)
|
old_list = self._by_plugin.get(old_comp.plugin_id)
|
||||||
if old_list is not None:
|
if old_list is not None:
|
||||||
try:
|
with contextlib.suppress(ValueError):
|
||||||
old_list.remove(old_comp)
|
old_list.remove(old_comp)
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
# 从旧类型索引中移除,防止类型变更时幽灵残留
|
# 从旧类型索引中移除,防止类型变更时幽灵残留
|
||||||
if old_type_dict := self._by_type.get(old_comp.component_type):
|
if old_type_dict := self._by_type.get(old_comp.component_type):
|
||||||
old_type_dict.pop(comp.full_name, None)
|
old_type_dict.pop(comp.full_name, None)
|
||||||
|
|
||||||
self._components[comp.full_name] = comp
|
self._components[comp.full_name] = comp
|
||||||
|
self._by_type[comp.component_type][comp.full_name] = comp
|
||||||
if component_type not in self._by_type:
|
|
||||||
self._by_type[component_type] = {}
|
|
||||||
self._by_type[component_type][comp.full_name] = comp
|
|
||||||
|
|
||||||
self._by_plugin.setdefault(plugin_id, []).append(comp)
|
self._by_plugin.setdefault(plugin_id, []).append(comp)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def register_plugin_components(
|
def register_plugin_components(self, plugin_id: str, components: List[Dict[str, Any]]) -> int:
|
||||||
self,
|
"""批量注册一个插件的所有组件,返回成功注册数。
|
||||||
plugin_id: str,
|
Args:
|
||||||
components: List[Dict[str, Any]],
|
plugin_id (str): 插件id
|
||||||
) -> int:
|
components (List[Dict[str, Any]]): 组件字典列表,每个组件包含 name, component_type, metadata 等字段
|
||||||
"""批量注册一个插件的所有组件,返回成功注册数。"""
|
Returns:
|
||||||
|
count (int): 成功注册的组件数量
|
||||||
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
for comp_data in components:
|
for comp_data in components:
|
||||||
ok = self.register_component(
|
ok = self.register_component(
|
||||||
@@ -139,7 +216,13 @@ class ComponentRegistry:
|
|||||||
return count
|
return count
|
||||||
|
|
||||||
def remove_components_by_plugin(self, plugin_id: str) -> int:
|
def remove_components_by_plugin(self, plugin_id: str) -> int:
|
||||||
"""移除某个插件的所有组件,返回移除数量。"""
|
"""移除某个插件的所有组件,返回移除数量。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_id (str): 插件id
|
||||||
|
Returns:
|
||||||
|
count (int): 移除的组件数量
|
||||||
|
"""
|
||||||
comps = self._by_plugin.pop(plugin_id, [])
|
comps = self._by_plugin.pop(plugin_id, [])
|
||||||
for comp in comps:
|
for comp in comps:
|
||||||
self._components.pop(comp.full_name, None)
|
self._components.pop(comp.full_name, None)
|
||||||
@@ -147,106 +230,200 @@ class ComponentRegistry:
|
|||||||
type_dict.pop(comp.full_name, None)
|
type_dict.pop(comp.full_name, None)
|
||||||
return len(comps)
|
return len(comps)
|
||||||
|
|
||||||
# ──── 启用 / 禁用 ─────────────────────────────────────────
|
# ====== 启用 / 禁用 ======
|
||||||
|
def check_component_enabled(self, component: ComponentEntry, session_id: Optional[str] = None):
|
||||||
|
if session_id and session_id in component.disabled_session:
|
||||||
|
return False
|
||||||
|
return component.enabled
|
||||||
|
|
||||||
def set_component_enabled(self, full_name: str, enabled: bool) -> bool:
|
def toggle_component_status(self, full_name: str, enabled: bool, session_id: Optional[str] = None) -> bool:
|
||||||
"""启用或禁用指定组件。"""
|
"""启用或禁用指定组件。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
full_name (str): 组件全名
|
||||||
|
enabled (bool): 使能情况
|
||||||
|
session_id (Optional[str]): 可选的会话ID,仅对该会话禁用(如果提供)
|
||||||
|
Returns:
|
||||||
|
success (bool): 是否成功设置(失败原因通常是组件不存在)
|
||||||
|
"""
|
||||||
comp = self._components.get(full_name)
|
comp = self._components.get(full_name)
|
||||||
if comp is None:
|
if comp is None:
|
||||||
return False
|
return False
|
||||||
comp.enabled = enabled
|
if session_id:
|
||||||
|
if enabled:
|
||||||
|
comp.disabled_session.discard(session_id)
|
||||||
|
else:
|
||||||
|
comp.disabled_session.add(session_id)
|
||||||
|
else:
|
||||||
|
comp.enabled = enabled
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_plugin_enabled(self, plugin_id: str, enabled: bool) -> int:
|
def toggle_plugin_status(self, plugin_id: str, enabled: bool, session_id: Optional[str] = None) -> int:
|
||||||
"""批量启用或禁用某插件的所有组件。"""
|
"""批量启用或禁用某插件的所有组件。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_id (str): 插件id
|
||||||
|
enabled (bool): 使能情况
|
||||||
|
session_id (Optional[str]): 可选的会话ID,仅对该会话禁用(如果提供)
|
||||||
|
Returns:
|
||||||
|
count (int): 成功设置的组件数量(失败原因通常是插件不存在)
|
||||||
|
"""
|
||||||
comps = self._by_plugin.get(plugin_id, [])
|
comps = self._by_plugin.get(plugin_id, [])
|
||||||
for comp in comps:
|
for comp in comps:
|
||||||
comp.enabled = enabled
|
if session_id:
|
||||||
|
if enabled:
|
||||||
|
comp.disabled_session.discard(session_id)
|
||||||
|
else:
|
||||||
|
comp.disabled_session.add(session_id)
|
||||||
|
else:
|
||||||
|
comp.enabled = enabled
|
||||||
return len(comps)
|
return len(comps)
|
||||||
|
|
||||||
# ──── 查询方法 ─────────────────────────────────────────────
|
def get_component(self, full_name: str) -> Optional[ComponentEntry]:
|
||||||
|
"""按全名查询。
|
||||||
|
|
||||||
def get_component(self, full_name: str) -> Optional[RegisteredComponent]:
|
Args:
|
||||||
"""按全名查询。"""
|
full_name (str): 组件全名
|
||||||
|
Returns:
|
||||||
|
component (Optional[ComponentEntry]): 组件条目,未找到时为 None
|
||||||
|
"""
|
||||||
return self._components.get(full_name)
|
return self._components.get(full_name)
|
||||||
|
|
||||||
def get_components_by_type(self, component_type: str, *, enabled_only: bool = True) -> List[RegisteredComponent]:
|
def get_components_by_type(
|
||||||
"""按类型查询。"""
|
self, component_type: str, *, enabled_only: bool = True, session_id: Optional[str] = None
|
||||||
type_dict = self._by_type.get(component_type, {})
|
) -> List[ComponentEntry]:
|
||||||
|
"""按类型查询组件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
component_type (str): 组件类型(如 `ACTION`、`COMMAND` 等)
|
||||||
|
enabled_only (bool): 是否仅返回启用的组件
|
||||||
|
session_id (Optional[str]): 可选的会话ID,若提供则考虑会话禁用状态
|
||||||
|
Returns:
|
||||||
|
components (List[ComponentEntry]): 组件条目列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
comp_type = ComponentTypes(component_type)
|
||||||
|
except ValueError:
|
||||||
|
logger.error(f"组件类型 {component_type} 不存在")
|
||||||
|
raise
|
||||||
|
type_dict = self._by_type.get(comp_type, {})
|
||||||
if enabled_only:
|
if enabled_only:
|
||||||
return [c for c in type_dict.values() if c.enabled]
|
return [c for c in type_dict.values() if self.check_component_enabled(c, session_id)]
|
||||||
return list(type_dict.values())
|
return list(type_dict.values())
|
||||||
|
|
||||||
def get_components_by_plugin(self, plugin_id: str, *, enabled_only: bool = True) -> List[RegisteredComponent]:
|
def get_components_by_plugin(
|
||||||
"""按插件查询。"""
|
self, plugin_id: str, *, enabled_only: bool = True, session_id: Optional[str] = None
|
||||||
comps = self._by_plugin.get(plugin_id, [])
|
) -> List[ComponentEntry]:
|
||||||
return [c for c in comps if c.enabled] if enabled_only else list(comps)
|
"""按插件查询组件。
|
||||||
|
|
||||||
def find_command_by_text(self, text: str) -> Optional[tuple[RegisteredComponent, Dict[str, Any]]]:
|
Args:
|
||||||
|
plugin_id (str): 插件ID
|
||||||
|
enabled_only (bool): 是否仅返回启用的组件
|
||||||
|
session_id (Optional[str]): 可选的会话ID,若提供则考虑会话禁用状态
|
||||||
|
Returns:
|
||||||
|
components (List[ComponentEntry]): 组件条目列表
|
||||||
|
"""
|
||||||
|
comps = self._by_plugin.get(plugin_id, [])
|
||||||
|
return [c for c in comps if self.check_component_enabled(c, session_id)] if enabled_only else list(comps)
|
||||||
|
|
||||||
|
def find_command_by_text(
|
||||||
|
self, text: str, session_id: Optional[str] = None
|
||||||
|
) -> Optional[Tuple[ComponentEntry, Dict[str, Any]]]:
|
||||||
"""通过文本匹配命令正则,返回 (组件, matched_groups) 元组。
|
"""通过文本匹配命令正则,返回 (组件, matched_groups) 元组。
|
||||||
|
|
||||||
matched_groups 为正则命名捕获组 dict,别名匹配时为空 dict。
|
matched_groups 为正则命名捕获组 dict,别名匹配时为空 dict。
|
||||||
|
Args:
|
||||||
|
text (str): 待匹配文本
|
||||||
|
session_id (Optional[str]): 可选的会话ID,若提供则考虑会话禁用状态
|
||||||
|
Returns:
|
||||||
|
result (Optional[tuple[ComponentEntry, Dict[str, Any]]]): 匹配到的组件及正则捕获组,未找到时为 None
|
||||||
"""
|
"""
|
||||||
for comp in self._by_type.get("command", {}).values():
|
for comp in self._by_type.get(ComponentTypes.COMMAND, {}).values():
|
||||||
if not comp.enabled:
|
if not self.check_component_enabled(comp, session_id):
|
||||||
continue
|
continue
|
||||||
if comp._compiled_pattern:
|
if not isinstance(comp, CommandEntry):
|
||||||
m = comp._compiled_pattern.search(text)
|
continue
|
||||||
if m:
|
if comp.compiled_pattern:
|
||||||
|
if m := comp.compiled_pattern.search(text):
|
||||||
return comp, m.groupdict()
|
return comp, m.groupdict()
|
||||||
# 别名匹配
|
# 别名匹配
|
||||||
aliases = comp.metadata.get("aliases", [])
|
for alias in comp.aliases:
|
||||||
for alias in aliases:
|
|
||||||
if text.startswith(alias):
|
if text.startswith(alias):
|
||||||
return comp, {}
|
return comp, {}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_event_handlers(self, event_type: str, *, enabled_only: bool = True) -> List[RegisteredComponent]:
|
def get_event_handlers(
|
||||||
"""获取特定事件类型的所有 event_handler,按 weight 降序排列。"""
|
self, event_type: str, *, enabled_only: bool = True, session_id: Optional[str] = None
|
||||||
handlers = []
|
) -> List[EventHandlerEntry]:
|
||||||
for comp in self._by_type.get("event_handler", {}).values():
|
"""查询指定事件类型的事件处理器组件。
|
||||||
if enabled_only and not comp.enabled:
|
|
||||||
|
Args:
|
||||||
|
event_type (str): 事件类型
|
||||||
|
enabled_only (bool): 是否仅返回启用的组件
|
||||||
|
session_id (Optional[str]): 可选的会话ID,若提供则考虑会话禁用状态
|
||||||
|
Returns:
|
||||||
|
handlers (List[EventHandlerEntry]): 符合条件的 EventHandler 组件列表,按 weight 降序排序
|
||||||
|
"""
|
||||||
|
handlers: List[EventHandlerEntry] = []
|
||||||
|
for comp in self._by_type.get(ComponentTypes.EVENT_HANDLER, {}).values():
|
||||||
|
if enabled_only and not self.check_component_enabled(comp, session_id):
|
||||||
continue
|
continue
|
||||||
if comp.metadata.get("event_type") == event_type:
|
if not isinstance(comp, EventHandlerEntry):
|
||||||
|
continue
|
||||||
|
if comp.event_type == event_type:
|
||||||
handlers.append(comp)
|
handlers.append(comp)
|
||||||
handlers.sort(key=lambda c: c.metadata.get("weight", 0), reverse=True)
|
handlers.sort(key=lambda c: c.weight, reverse=True)
|
||||||
return handlers
|
return handlers
|
||||||
|
|
||||||
def get_workflow_steps(self, stage: str, *, enabled_only: bool = True) -> List[RegisteredComponent]:
|
def get_workflow_handlers(
|
||||||
"""获取特定 workflow 阶段的所有步骤,按 priority 降序。"""
|
self, stage: str, *, enabled_only: bool = True, session_id: Optional[str] = None
|
||||||
steps = []
|
) -> List[WorkflowHandlerEntry]:
|
||||||
for comp in self._by_type.get("workflow_step", {}).values():
|
"""获取特定 workflow 阶段的所有步骤,按 priority 降序。
|
||||||
if enabled_only and not comp.enabled:
|
|
||||||
|
Args:
|
||||||
|
stage: workflow 阶段名称
|
||||||
|
enabled_only: 是否仅返回启用的组件
|
||||||
|
session_id: 可选的会话ID,若提供则考虑会话禁用状态
|
||||||
|
Returns:
|
||||||
|
handlers (List[WorkflowHandlerEntry]): 符合条件的 WorkflowHandler 组件列表,按 priority 降序排序
|
||||||
|
"""
|
||||||
|
handlers: List[WorkflowHandlerEntry] = []
|
||||||
|
for comp in self._by_type.get(ComponentTypes.WORKFLOW_HANDLER, {}).values():
|
||||||
|
if enabled_only and not self.check_component_enabled(comp, session_id):
|
||||||
continue
|
continue
|
||||||
if comp.metadata.get("stage") == stage:
|
if not isinstance(comp, WorkflowHandlerEntry):
|
||||||
steps.append(comp)
|
continue
|
||||||
steps.sort(key=lambda c: c.metadata.get("priority", 0), reverse=True)
|
if comp.stage == stage:
|
||||||
return steps
|
handlers.append(comp)
|
||||||
|
handlers.sort(key=lambda c: c.priority, reverse=True)
|
||||||
|
return handlers
|
||||||
|
|
||||||
def get_tools_for_llm(self, *, enabled_only: bool = True) -> List[Dict[str, Any]]:
|
def get_tools(self, *, enabled_only: bool = True, session_id: Optional[str] = None) -> List[ToolEntry]:
|
||||||
"""获取可供 LLM 使用的工具列表(openai function-calling 格式预览)。"""
|
"""查询所有工具组件。
|
||||||
result: List[Dict[str, Any]] = []
|
|
||||||
for comp in self.get_components_by_type("tool", enabled_only=enabled_only):
|
Args:
|
||||||
tool_def: Dict[str, Any] = {
|
enabled_only (bool): 是否仅返回启用的组件
|
||||||
"name": comp.full_name,
|
session_id (Optional[str]): 可选的会话ID,若提供则考虑会话禁用状态
|
||||||
"description": comp.metadata.get("description", ""),
|
Returns:
|
||||||
}
|
tools (List[ToolEntry]): 符合条件的 Tool 组件列表
|
||||||
# 从结构化参数或原始参数构建 parameters
|
"""
|
||||||
params = comp.metadata.get("parameters", [])
|
tools: List[ToolEntry] = []
|
||||||
params_raw = comp.metadata.get("parameters_raw", {})
|
for comp in self._by_type.get(ComponentTypes.TOOL, {}).values():
|
||||||
if params:
|
if enabled_only and not self.check_component_enabled(comp, session_id):
|
||||||
tool_def["parameters"] = params
|
continue
|
||||||
elif params_raw:
|
if isinstance(comp, ToolEntry):
|
||||||
tool_def["parameters"] = params_raw
|
tools.append(comp)
|
||||||
result.append(tool_def)
|
return tools
|
||||||
return result
|
|
||||||
|
|
||||||
# ──── 统计 ─────────────────────────────────────────────────
|
# ====== 统计信息 ======
|
||||||
|
def get_stats(self) -> StatusDict:
|
||||||
def get_stats(self) -> Dict[str, int]:
|
"""获取注册统计。
|
||||||
"""获取注册统计。"""
|
|
||||||
stats: Dict[str, int] = {"total": len(self._components)}
|
Returns:
|
||||||
|
stats (StatusDict): 组件统计信息,包括总数、各类型数量、插件数量等
|
||||||
|
"""
|
||||||
|
stats: StatusDict = {"total": len(self._components)} # type: ignore
|
||||||
for comp_type, type_dict in self._by_type.items():
|
for comp_type, type_dict in self._by_type.items():
|
||||||
stats[comp_type] = len(type_dict)
|
stats[comp_type.value] = len(type_dict)
|
||||||
stats["plugins"] = len(self._by_plugin)
|
stats["plugins"] = len(self._by_plugin)
|
||||||
return stats
|
return stats
|
||||||
|
|||||||
45
src/plugin_runtime/host/logger_bridge.py
Normal file
45
src/plugin_runtime/host/logger_bridge.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import logging as stdlib_logging
|
||||||
|
from src.plugin_runtime.protocol.errors import ErrorCode
|
||||||
|
from src.plugin_runtime.protocol.envelope import Envelope, LogBatchPayload
|
||||||
|
class RunnerLogBridge:
|
||||||
|
"""将 Runner 进程上报的批量日志重放到主进程的 Logger 中。
|
||||||
|
|
||||||
|
Runner 通过 ``runner.log_batch`` IPC 事件批量到达。
|
||||||
|
每条 LogEntry 被重建为一个真实的 :class:`logging.LogRecord` 并直接
|
||||||
|
调用 ``logging.getLogger(entry.logger_name).handle(record)``,
|
||||||
|
从而接入主进程已配置好的 structlog Handler 链。
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def handle_log_batch(self, envelope: Envelope) -> Envelope:
|
||||||
|
"""IPC 事件处理器:解析批量日志并重放到主进程 Logger。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
envelope: 方法名为 ``runner.log_batch`` 的 IPC 事件信封。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
空响应信封(事件模式下将被忽略)。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
batch = LogBatchPayload.model_validate(envelope.payload)
|
||||||
|
except Exception as exc:
|
||||||
|
return envelope.make_error_response(ErrorCode.E_BAD_PAYLOAD.value, str(exc))
|
||||||
|
|
||||||
|
for entry in batch.entries:
|
||||||
|
# 重建一个与原始日志尽量相符的 LogRecord
|
||||||
|
record = stdlib_logging.LogRecord(
|
||||||
|
name=entry.logger_name,
|
||||||
|
level=entry.level,
|
||||||
|
pathname="<runner>",
|
||||||
|
lineno=0,
|
||||||
|
msg=entry.message,
|
||||||
|
args=(),
|
||||||
|
exc_info=None,
|
||||||
|
)
|
||||||
|
record.created = entry.timestamp_ms / 1000.0
|
||||||
|
record.msecs = entry.timestamp_ms % 1000
|
||||||
|
if entry.exception_text:
|
||||||
|
record.exc_text = entry.exception_text
|
||||||
|
|
||||||
|
stdlib_logging.getLogger(entry.logger_name).handle(record)
|
||||||
|
|
||||||
|
return envelope.make_response(payload={"accepted": True, "count": len(batch.entries)})
|
||||||
Reference in New Issue
Block a user