新增六边形架构核心代码包,包含: 1. 协议适配器层:HTTP/SMTP/IMAP/SSH/GRPC等多协议适配器实现 2. 认证插件体系:基础认证、API密钥、HMAC等多类型认证插件 3. 执行编排框架:工具执行器、上下文构建、运行时治理组件 4. 用例端口与DTO:定义领域服务端口与数据传输对象 5. 厂商集成包框架:支持第三方系统集成扩展 6. 基础设施装配层:实现依赖注入与服务装配 所有代码遵循六边形架构设计原则,实现端口与适配器解耦,支持动态扩展与自动发现。
125 lines
5.3 KiB
Python
125 lines
5.3 KiB
Python
"""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()
|