"""IntegrationOperationRegistry:厂商操作分派注册表。 实现 ``core/contracts.py::IntegrationOperationRegistry`` 端口(结构化子类型)。 按 ``(operation, source_type)`` 二元组分派 handler。支持 discover / preview_tools / create_tools 三类操作。 handler 签名(``OperationHandler``):``(source_type: str, system_config: dict[str, Any]) -> Awaitable[Any]``, 2 参数纯 dict,不依赖 ORM/DB。Token/解密/DB 由 use_cases 层预处理后注入 ``system_config``。 source_type 解析职责由 use_cases 层完成(端口注释 L842-L844),本注册表只接收 已解析的 source_type。草稿持久化职责由 use_cases 层统一处理(端口注释 L846-L847)。 键冲突策略:同一 ``(operation, source_type)`` 重复注册且 handler 不同时抛 ``DomainValidationError``,与 framework 层注册表策略一致。 """ from __future__ import annotations from collections.abc import Awaitable, Callable from typing import Any from yuxi.external_systems.exceptions import ( DomainValidationError, IntegrationOperationNotRegisteredError, ) from yuxi.external_systems.integrations.schemas import GeneratedToolsDraft # handler 类型别名:与端口签名一致,2 参数 + Awaitable 返回 OperationHandler = Callable[ [str, dict[str, Any]], Awaitable[Any], ] # 允许的 operation 集合 _ALLOWED_OPERATIONS = frozenset({"discover", "preview_tools", "create_tools"}) class IntegrationOperationRegistry: """厂商操作分派注册表,实现 core/contracts.py::IntegrationOperationRegistry 端口。 实现 Protocol 的 3 个分派方法(discover / preview_tools / create_tools), 并额外提供 register / get / list_operations / list_source_types / clear 供框架内部使用(与 AdapterRegistry 模式一致)。 """ _handlers: dict[tuple[str, str], OperationHandler] = {} @classmethod def register(cls, operation: str, source_type: str, handler: OperationHandler) -> None: """注册 (operation, source_type) 对应的 handler。 键冲突抛 DomainValidationError。 operation 限定为 discover / preview_tools / create_tools 三者之一。 """ if operation not in _ALLOWED_OPERATIONS: raise DomainValidationError(f"不支持的集成操作: {operation},仅允许 {_ALLOWED_OPERATIONS}") key = (operation, source_type) existing = cls._handlers.get(key) if existing is not None and existing is not handler: raise DomainValidationError( f"集成操作 handler 冲突: (operation={operation}, source_type={source_type}) " f"已注册 {getattr(existing, '__name__', existing)}," f"尝试注册 {getattr(handler, '__name__', handler)}" ) cls._handlers[key] = handler @classmethod def get(cls, operation: str, source_type: str) -> OperationHandler: """获取 handler,未注册抛 IntegrationOperationNotRegisteredError。""" handler = cls._handlers.get((operation, source_type)) if handler is None: raise IntegrationOperationNotRegisteredError( f"集成操作未注册: (operation={operation}, source_type={source_type})" ) return handler @classmethod async def discover(cls, source_type: str, system_config: dict[str, Any]) -> list[dict[str, Any]]: """分派 discover 操作(实现端口)。""" handler = cls.get("discover", source_type) return await handler(source_type, system_config) @classmethod async def preview_tools(cls, source_type: str, system_config: dict[str, Any]) -> list[dict[str, Any]]: """分派 preview_tools 操作(实现端口)。""" handler = cls.get("preview_tools", source_type) return await handler(source_type, system_config) @classmethod async def create_tools(cls, source_type: str, system_config: dict[str, Any]) -> GeneratedToolsDraft: """分派 create_tools 操作(实现端口)。 返回 GeneratedToolsDraft,由 use_cases 层统一持久化。 """ handler = cls.get("create_tools", source_type) result = await handler(source_type, system_config) if isinstance(result, GeneratedToolsDraft): return result # 容错:handler 返回 dict 时构造 GeneratedToolsDraft(便于渐进迁移) if isinstance(result, dict): return GeneratedToolsDraft.model_validate(result) raise DomainValidationError( f"create_tools handler 返回类型非法: 期望 GeneratedToolsDraft / dict,实际 {type(result).__name__}" ) @classmethod def list_operations(cls, source_type: str) -> list[str]: """列出指定 source_type 已注册的操作(供调试与前端展示)。""" return sorted(op for (op, st) in cls._handlers if st == source_type) @classmethod def list_source_types(cls) -> list[str]: """列出所有已注册 handler 涉及的 source_type(去重)。""" seen: dict[str, None] = {} for _op, st in cls._handlers: if st not in seen: seen[st] = None return list(seen.keys()) @classmethod def clear(cls) -> None: """清空注册表,仅供测试使用。""" cls._handlers.clear()