2026-07-02 03:22:12 +08:00
|
|
|
|
"""插件入口契约。
|
|
|
|
|
|
|
|
|
|
|
|
定义插件宿主 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
|
2026-07-02 18:27:06 +08:00
|
|
|
|
from yuxi.channels.contract.plugin.capability import CapabilityProver
|
2026-07-02 03:22:12 +08:00
|
|
|
|
from yuxi.channels.contract.plugin.extension_point import (
|
|
|
|
|
|
ConfigSource,
|
|
|
|
|
|
EventSubscription,
|
|
|
|
|
|
StageSlot,
|
|
|
|
|
|
)
|
2026-07-06 20:49:35 +08:00
|
|
|
|
from yuxi.channels.contract.plugin.manifest import (
|
|
|
|
|
|
ChannelManifest,
|
|
|
|
|
|
PluginManifest,
|
|
|
|
|
|
)
|
2026-07-02 03:22:12 +08:00
|
|
|
|
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
|
2026-07-02 18:27:06 +08:00
|
|
|
|
/ StreamConnectorAdapter / AttachmentUploadAdapter / WebhookTestAdapter),加
|
2026-07-02 03:22:12 +08:00
|
|
|
|
渠道上下文提供者端口(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 运行与身份解析能力。
|
2026-07-02 18:27:06 +08:00
|
|
|
|
2. 扩展点注册:通过 ``registerXxx()`` 方法注册适配器、阶段槽位、事件订阅、
|
|
|
|
|
|
配置源与能力证明者,向宿主注入自身逻辑。
|
2026-07-02 03:22:12 +08:00
|
|
|
|
3. 能力边界声明:通过 ``declareXxx()`` 方法声明可访问端口、可注入管道与
|
|
|
|
|
|
资源配额,由宿主强制约束。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def getConfigPort(self) -> ConfigPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取配置端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getLoggerPort(self) -> LoggerPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取日志端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getPersistencePort(self) -> PersistencePort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取持久化端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getCachePort(self) -> CachePort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取缓存端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getTracerPort(self) -> TracerPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取追踪端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getQueuePort(self) -> QueuePort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取队列端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getConversationPort(self) -> ConversationPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取会话端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getAgentRunPort(self) -> AgentRunPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取 Agent 运行端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getIdentityResolverPort(self) -> IdentityResolverPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取身份解析端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getChannelAccountRepositoryPort(self) -> ChannelAccountRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取渠道账户仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getChannelSessionRepositoryPort(self) -> ChannelSessionRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取渠道会话仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getPairingRepositoryPort(self) -> PairingRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取配对仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getAuditLogRepositoryPort(self) -> AuditLogRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取审计日志仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getOutboxRepositoryPort(self) -> OutboxRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取发件箱仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getUserIdentityRepositoryPort(self) -> UserIdentityRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取用户身份仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getIdempotencyRepositoryPort(self) -> IdempotencyRepositoryPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取幂等性仓储端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def getPersistenceHealthPort(self) -> PersistenceHealthPort:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""获取持久化健康端口。
|
|
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),返回注入的端口实例。
|
|
|
|
|
|
"""
|
2026-07-02 03:22:12 +08:00
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def registerAdapter(self, adapter_type: str, adapter: PluginAdapter) -> None:
|
|
|
|
|
|
"""注册适配器。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
adapter_type: 适配器类型。
|
|
|
|
|
|
adapter: 适配器实例。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册相同 adapter_type 会覆盖前一次的实例.
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def registerStageSlot(self, slot: StageSlot) -> None:
|
|
|
|
|
|
"""注册管道阶段槽位。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
slot: 阶段槽位实例。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册相同锚点 + 优先级的 slot 会触发冲突策略.
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def registerEventSubscription(self, subscription: EventSubscription) -> None:
|
|
|
|
|
|
"""注册事件订阅。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
subscription: 事件订阅实例。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册会创建多个订阅,同一事件被多次处理.
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def registerConfigSource(self, source: ConfigSource) -> None:
|
|
|
|
|
|
"""注册配置源。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 配置源实例。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册相同 key 会覆盖前一次的源.
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
2026-07-02 18:27:06 +08:00
|
|
|
|
def registerCapabilityProver(self, prover: CapabilityProver) -> None:
|
|
|
|
|
|
"""注册能力证明者(FR-08 增强)。
|
|
|
|
|
|
|
|
|
|
|
|
注册 ``CapabilityProver`` 实例,供宿主对插件清单声明的静态能力
|
|
|
|
|
|
进行运行时证明。宿主在 ``initialized`` 阶段调用 ``proveCapability``
|
|
|
|
|
|
对每项能力进行证明,证明失败的插件 **不得** 启动(FR-32)。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
prover: 能力证明 Protocol 实例。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册会覆盖前一次的 prover 实例.
|
2026-07-02 18:27:06 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
2026-07-02 03:22:12 +08:00
|
|
|
|
def registerMatchTier(self, tier: MatchTier) -> None:
|
|
|
|
|
|
"""注册路由匹配层级(FR-04)。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
tier: 匹配层级配置。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册相同层级名会覆盖前一次的配置.
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
def registerMatcher(self, name: str, matcher: Matcher) -> None:
|
|
|
|
|
|
"""注册匹配层级的匹配器(FR-04)。
|
|
|
|
|
|
|
|
|
|
|
|
为已注册的层级绑定匹配函数,供 ``RouteResolver`` 在遍历层级时调用
|
|
|
|
|
|
执行匹配逻辑。插件通过本方法注入自定义匹配规则。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
name: 层级名称(必须已通过 ``registerMatchTier`` 注册)。
|
|
|
|
|
|
matcher: 匹配器函数,接收绑定上下文,返回路由绑定或 None。
|
|
|
|
|
|
|
2026-07-03 19:18:13 +08:00
|
|
|
|
@consistency: 无状态(stateless),仅写入注册表。
|
|
|
|
|
|
@idempotent: False — 重复注册相同 name 会覆盖前一次的 matcher.
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
async def publishEvent(self, event: DomainEvent) -> None:
|
|
|
|
|
|
"""发布领域事件。
|
|
|
|
|
|
|
|
|
|
|
|
宿主负责将事件分发到订阅者。支持的事件类型:
|
|
|
|
|
|
PluginDiscovered/PluginLoaded/PluginStarted/PluginPaused/
|
|
|
|
|
|
PluginResumed/PluginStopped/PluginUnloaded/PluginFailed/
|
|
|
|
|
|
PairingApproved/PairingRevoked/ConfigChanged/ChannelDegraded/ChannelRecovered。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
event: 领域事件。
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
@consistency: 最终一致(eventual),订阅者按各自策略处理。
|
|
|
|
|
|
@idempotent: True — 重复发布相同事件产生等价结果(订阅者需自行幂等).
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#: 插件入口类型别名。
|
|
|
|
|
|
#:
|
|
|
|
|
|
#: 每个渠道插件 **必须** 通过 ``CHANNEL_ENTRY`` 契约注册,**不得** 隐式全局
|
|
|
|
|
|
#: 注入(FR-32)。由宿主在 loaded 阶段调用,返回 ``PluginManifest``,包含
|
|
|
|
|
|
#: 元数据、适配器、阶段、事件订阅等。
|
2026-07-06 20:49:35 +08:00
|
|
|
|
#:
|
|
|
|
|
|
#: 签名 ``(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]
|