本次提交包含多维度代码优化与功能增强: 1. 移除报告模块冗余导入与枚举,清理报表相关代码 2. 新增扫码登录支持方法与飞书适配器适配 3. 完善异常日志与健康检查信息 4. 扩展目录、配对管理、能力查询等接口 5. 优化出站管道与事务提交后钩子逻辑 6. 修复飞书消息解析与响应空值问题 7. 重构配置更新与服务账号创建逻辑 8. 统一传输错误分类契约与错误基类扩展
336 lines
12 KiB
Python
336 lines
12 KiB
Python
"""插件入口契约。
|
||
|
||
定义插件宿主 Protocol(``PluginHost``)与插件入口类型别名
|
||
(``CHANNEL_ENTRY``)。插件通过 ``CHANNEL_ENTRY`` 函数注册,由宿主在
|
||
loaded 阶段调用,返回 ``PluginManifest``。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import Callable
|
||
from typing import Any, Protocol, runtime_checkable
|
||
|
||
from yuxi.channels.contract.dtos.plugin import DomainEvent
|
||
from yuxi.channels.contract.dtos.route import MatchTier
|
||
from yuxi.channels.contract.plugin.capability import CapabilityProver
|
||
from yuxi.channels.contract.plugin.extension_point import (
|
||
ConfigSource,
|
||
EventSubscription,
|
||
StageSlot,
|
||
)
|
||
from yuxi.channels.contract.plugin.manifest import (
|
||
ChannelManifest,
|
||
PluginManifest,
|
||
)
|
||
from yuxi.channels.contract.ports.driven import (
|
||
AgentRunPort,
|
||
AuditLogRepositoryPort,
|
||
CachePort,
|
||
ChannelAccountRepositoryPort,
|
||
ChannelSessionRepositoryPort,
|
||
ConfigPort,
|
||
ConversationPort,
|
||
IdempotencyRepositoryPort,
|
||
IdentityResolverPort,
|
||
LoggerPort,
|
||
OutboxRepositoryPort,
|
||
PairingRepositoryPort,
|
||
PersistenceHealthPort,
|
||
PersistencePort,
|
||
QueuePort,
|
||
TracerPort,
|
||
UserIdentityRepositoryPort,
|
||
)
|
||
|
||
|
||
class PluginAdapter(Protocol):
|
||
"""插件适配器标记 Protocol。
|
||
|
||
标识 ``PluginHost.registerAdapter`` 接受的适配器实例类型。渠道插件
|
||
可注册的适配器包括 ``contract.plugin.adapters`` 中定义的 23 类适配器
|
||
协议(InboundAdapter / OutboundAdapter / SessionAdapter / RichMessageAdapter
|
||
/ StreamingAdapter / DirectoryAdapter / CommandAdapter / WizardAdapter
|
||
/ DoctorAdapter / WhitelistAdapter / ToolsAdapter / MessageOpsAdapter
|
||
/ StatusAdapter / MentionAdapter / IdentityResolverAdapter / LifecycleAdapter
|
||
/ LoginAdapter / ProbeableAdapter / ContentModerationAdapter / PullerAdapter
|
||
/ StreamConnectorAdapter / AttachmentUploadAdapter / WebhookTestAdapter),加
|
||
渠道上下文提供者端口(ChannelContextProviderPort),它们各自定义独立
|
||
的处理方法,无公共方法基类,故本 Protocol 作为类型边界用于静态检查
|
||
与文档化,不含公共方法声明。
|
||
"""
|
||
|
||
...
|
||
|
||
|
||
#: 路由匹配器类型别名。
|
||
#:
|
||
#: 接收绑定上下文,返回匹配的路由绑定或 ``None``。插件通过
|
||
#: ``registerMatcher`` 注入自定义匹配函数,供 ``RouteResolver`` 遍历
|
||
#: 层级时调用执行匹配逻辑。使用 ``Any`` 避免运行时解析
|
||
#: ``BindingContext``(TYPE_CHECKING 导入,见 ``TierMatcher``)。
|
||
Matcher = Callable[..., Any]
|
||
|
||
|
||
@runtime_checkable
|
||
class PluginHost(Protocol):
|
||
"""插件宿主 Protocol。
|
||
|
||
由宿主实现,向插件提供被驱动端口获取、扩展点注册、能力边界声明与
|
||
事件发布能力。使用 ``@runtime_checkable`` 装饰以支持 ``isinstance``
|
||
检查。
|
||
|
||
资源访问约束(INV-I7 / FF-ADAPTER-01 / FR-32):
|
||
- 插件 **不得** 直接访问宿主的 ``settings``、``logger``、数据库连接池、
|
||
Redis 客户端,**必须** 通过 ``PluginHost`` 提供的端口获取方法获取依赖。
|
||
- 插件 **不得** 修改宿主的中间件链、路由表、事件订阅器,**只能** 通过
|
||
扩展点注册方法注入自身逻辑。
|
||
- 插件能力声明 **必须** 通过 ``CapabilityProver`` 验证,验证失败 **不得**
|
||
启动插件。
|
||
|
||
插件必须通过 ``PluginHost`` 完成以下三类操作:
|
||
1. 端口获取:通过 ``getXxxPort()`` 方法获取被驱动端口,访问配置、日志、
|
||
持久化、缓存、追踪、队列、会话、Agent 运行与身份解析能力。
|
||
2. 扩展点注册:通过 ``registerXxx()`` 方法注册适配器、阶段槽位、事件订阅、
|
||
配置源与能力证明者,向宿主注入自身逻辑。
|
||
3. 能力边界声明:通过 ``declareXxx()`` 方法声明可访问端口、可注入管道与
|
||
资源配额,由宿主强制约束。
|
||
"""
|
||
|
||
def getConfigPort(self) -> ConfigPort:
|
||
"""获取配置端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getLoggerPort(self) -> LoggerPort:
|
||
"""获取日志端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getPersistencePort(self) -> PersistencePort:
|
||
"""获取持久化端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getCachePort(self) -> CachePort:
|
||
"""获取缓存端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getTracerPort(self) -> TracerPort:
|
||
"""获取追踪端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getQueuePort(self) -> QueuePort:
|
||
"""获取队列端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getConversationPort(self) -> ConversationPort:
|
||
"""获取会话端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getAgentRunPort(self) -> AgentRunPort:
|
||
"""获取 Agent 运行端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getIdentityResolverPort(self) -> IdentityResolverPort:
|
||
"""获取身份解析端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getChannelAccountRepositoryPort(self) -> ChannelAccountRepositoryPort:
|
||
"""获取渠道账户仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getChannelSessionRepositoryPort(self) -> ChannelSessionRepositoryPort:
|
||
"""获取渠道会话仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getPairingRepositoryPort(self) -> PairingRepositoryPort:
|
||
"""获取配对仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getAuditLogRepositoryPort(self) -> AuditLogRepositoryPort:
|
||
"""获取审计日志仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getOutboxRepositoryPort(self) -> OutboxRepositoryPort:
|
||
"""获取发件箱仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getUserIdentityRepositoryPort(self) -> UserIdentityRepositoryPort:
|
||
"""获取用户身份仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getIdempotencyRepositoryPort(self) -> IdempotencyRepositoryPort:
|
||
"""获取幂等性仓储端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def getPersistenceHealthPort(self) -> PersistenceHealthPort:
|
||
"""获取持久化健康端口。
|
||
|
||
@consistency: 无状态(stateless),返回注入的端口实例。
|
||
"""
|
||
...
|
||
|
||
def registerAdapter(self, adapter_type: str, adapter: PluginAdapter) -> None:
|
||
"""注册适配器。
|
||
|
||
参数:
|
||
adapter_type: 适配器类型。
|
||
adapter: 适配器实例。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册相同 adapter_type 会覆盖前一次的实例.
|
||
"""
|
||
...
|
||
|
||
def registerStageSlot(self, slot: StageSlot) -> None:
|
||
"""注册管道阶段槽位。
|
||
|
||
参数:
|
||
slot: 阶段槽位实例。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册相同锚点 + 优先级的 slot 会触发冲突策略.
|
||
"""
|
||
...
|
||
|
||
def registerEventSubscription(self, subscription: EventSubscription) -> None:
|
||
"""注册事件订阅。
|
||
|
||
参数:
|
||
subscription: 事件订阅实例。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册会创建多个订阅,同一事件被多次处理.
|
||
"""
|
||
...
|
||
|
||
def registerConfigSource(self, source: ConfigSource) -> None:
|
||
"""注册配置源。
|
||
|
||
参数:
|
||
source: 配置源实例。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册相同 key 会覆盖前一次的源.
|
||
"""
|
||
...
|
||
|
||
def registerCapabilityProver(self, prover: CapabilityProver) -> None:
|
||
"""注册能力证明者(FR-08 增强)。
|
||
|
||
注册 ``CapabilityProver`` 实例,供宿主对插件清单声明的静态能力
|
||
进行运行时证明。宿主在 ``initialized`` 阶段调用 ``proveCapability``
|
||
对每项能力进行证明,证明失败的插件 **不得** 启动(FR-32)。
|
||
|
||
参数:
|
||
prover: 能力证明 Protocol 实例。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册会覆盖前一次的 prover 实例.
|
||
"""
|
||
...
|
||
|
||
def registerMatchTier(self, tier: MatchTier) -> None:
|
||
"""注册路由匹配层级(FR-04)。
|
||
|
||
参数:
|
||
tier: 匹配层级配置。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册相同层级名会覆盖前一次的配置.
|
||
"""
|
||
...
|
||
|
||
def registerMatcher(self, name: str, matcher: Matcher) -> None:
|
||
"""注册匹配层级的匹配器(FR-04)。
|
||
|
||
为已注册的层级绑定匹配函数,供 ``RouteResolver`` 在遍历层级时调用
|
||
执行匹配逻辑。插件通过本方法注入自定义匹配规则。
|
||
|
||
参数:
|
||
name: 层级名称(必须已通过 ``registerMatchTier`` 注册)。
|
||
matcher: 匹配器函数,接收绑定上下文,返回路由绑定或 None。
|
||
|
||
@consistency: 无状态(stateless),仅写入注册表。
|
||
@idempotent: False — 重复注册相同 name 会覆盖前一次的 matcher.
|
||
"""
|
||
...
|
||
|
||
async def publishEvent(self, event: DomainEvent) -> None:
|
||
"""发布领域事件。
|
||
|
||
宿主负责将事件分发到订阅者。支持的事件类型:
|
||
PluginDiscovered/PluginLoaded/PluginStarted/PluginPaused/
|
||
PluginResumed/PluginStopped/PluginUnloaded/PluginFailed/
|
||
PairingApproved/PairingRevoked/ConfigChanged/ChannelDegraded/ChannelRecovered。
|
||
|
||
参数:
|
||
event: 领域事件。
|
||
|
||
@consistency: 最终一致(eventual),订阅者按各自策略处理。
|
||
@idempotent: True — 重复发布相同事件产生等价结果(订阅者需自行幂等).
|
||
"""
|
||
...
|
||
|
||
|
||
#: 插件入口类型别名。
|
||
#:
|
||
#: 每个渠道插件 **必须** 通过 ``CHANNEL_ENTRY`` 契约注册,**不得** 隐式全局
|
||
#: 注入(FR-32)。由宿主在 loaded 阶段调用,返回 ``PluginManifest``,包含
|
||
#: 元数据、适配器、阶段、事件订阅等。
|
||
#:
|
||
#: 签名 ``(host, manifest) -> PluginManifest``:宿主传入 discover 阶段从
|
||
#: ``manifest.json`` 解析的 ``ChannelManifest``,插件直接复用为返回清单的
|
||
#: ``manifest`` 字段,**不得** 在 entry.py 内重新构造声明性字段
|
||
#: (capabilities / config_schema / env_vars / accessible_ports /
|
||
#: resource_quota / lifecycle / credential_strategy 等),避免双真相源
|
||
#: (F-03)。插件仅负责适配器实例化、client 实例化、LifecycleHandler
|
||
#: 构造与 ``registerAdapter`` 调用。宿主在加载期校验返回清单的
|
||
#: ``manifest.id`` 与入参 ``manifest.id`` 一致。
|
||
CHANNEL_ENTRY = Callable[[PluginHost, ChannelManifest], PluginManifest]
|