ForcePilot/backend/package/yuxi/channels/contract/plugin/entry.py

336 lines
12 KiB
Python
Raw Normal View History

"""插件入口契约。
定义插件宿主 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]