ForcePilot/backend/package/yuxi/external_systems/integrations/operation_registry.py
Kris 74709ba2d3 feat: 完成外部系统限界上下文核心代码实现
新增六边形架构核心代码包,包含:
1. 协议适配器层:HTTP/SMTP/IMAP/SSH/GRPC等多协议适配器实现
2. 认证插件体系:基础认证、API密钥、HMAC等多类型认证插件
3. 执行编排框架:工具执行器、上下文构建、运行时治理组件
4. 用例端口与DTO:定义领域服务端口与数据传输对象
5. 厂商集成包框架:支持第三方系统集成扩展
6. 基础设施装配层:实现依赖注入与服务装配

所有代码遵循六边形架构设计原则,实现端口与适配器解耦,支持动态扩展与自动发现。
2026-06-20 22:12:42 +08:00

125 lines
5.3 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.

"""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()