本次提交对频道模块进行了全面重构并新增多项核心功能: 1. 优化适配器状态获取逻辑,修复状态返回空值问题 2. 新增4种频道异常类型,完善错误处理体系 3. 大幅精简Mixin类,移除冗余的抽象方法定义 4. 重构适配器注册系统,统一注册入口并新增内置适配器加载方法 5. 扩展插件系统,新增更多元数据配置项支持 6. 新增线程类型、会话范围等模型定义,扩展事件类型枚举 7. 优化用户映射逻辑,使用PostgreSQL upsert避免重复创建 8. 新增历史消息注入模块,支持多格式历史格式化与缓存管理 9. 新增线程能力配置与各平台预置适配配置 10. 新增线程绑定管理器,支持多类型线程绑定生命周期管理 11. 重构__init__.py,整理导出模块与类型 12. 扩展基础适配器类,新增凭证解析、状态存储等核心方法 13. 重写消息路由器,支持按频道加载策略、安全校验与多命令处理 14. 新增/history、/context、/summary等交互命令实现 15. 优化消息记录与统计逻辑,完善路由调度链路
129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from yuxi.channels.base import BaseChannelAdapter
|
||
from yuxi.channels.capabilities import ChannelCapabilities
|
||
from yuxi.channels.meta import ChannelMeta
|
||
from yuxi.channels.registry import _register_builtin
|
||
|
||
|
||
def channel_plugin(
|
||
cls: type[BaseChannelAdapter] | None = None,
|
||
*,
|
||
channel_id: str | None = None,
|
||
capabilities: ChannelCapabilities | None = None,
|
||
meta: ChannelMeta | None = None,
|
||
order: int = 100,
|
||
) -> type[BaseChannelAdapter]:
|
||
"""渠道插件注册装饰器 — 组合入口, 替代 @register_builtin_adapter
|
||
|
||
用法:
|
||
|
||
@channel_plugin(
|
||
capabilities=ChannelCapabilities(...),
|
||
meta=ChannelMeta(id="telegram", label="Telegram"),
|
||
)
|
||
class TelegramAdapter(BaseChannelAdapter):
|
||
...
|
||
|
||
当用作无参装饰器时, 从类属性自动推断 channel_id:
|
||
|
||
@channel_plugin
|
||
class TelegramAdapter(BaseChannelAdapter):
|
||
channel_id = "telegram"
|
||
"""
|
||
|
||
def _decorate(_cls: type[BaseChannelAdapter]) -> type[BaseChannelAdapter]:
|
||
cid = channel_id or getattr(_cls, "channel_id", None)
|
||
if not cid:
|
||
raise ValueError(
|
||
f"channel_plugin requires channel_id for {_cls.__name__}. "
|
||
"Set channel_id as a ClassVar or pass it to the decorator."
|
||
)
|
||
|
||
if capabilities is not None:
|
||
_cls.capabilities = capabilities # type: ignore[attr-defined]
|
||
if meta is not None:
|
||
_cls.meta = meta # type: ignore[attr-defined]
|
||
|
||
_register_builtin(cid, _cls)
|
||
return _cls
|
||
|
||
if cls is not None:
|
||
return _decorate(cls)
|
||
|
||
return _decorate
|
||
|
||
|
||
class ChannelPlugin:
|
||
"""渠道插件组合入口 — 聚合协议和能力声明的便利构造器
|
||
|
||
为渠道开发者提供声明式 API,在适配器类上组合配置:
|
||
|
||
plugin = ChannelPlugin(
|
||
channel_id="telegram",
|
||
channel_type=ChannelType.TELEGRAM,
|
||
capabilities=ChannelCapabilities(
|
||
chat_types=["direct", "group", "thread"],
|
||
polls=True, reactions=True, edit=True,
|
||
supports_streaming=True,
|
||
streaming_modes=["off", "partial", "block", "progress"],
|
||
),
|
||
meta=ChannelMeta(id="telegram", label="Telegram"),
|
||
pairing={"auto_pair": True},
|
||
conversation_bindings={"max_bindings": 5},
|
||
agent_prompt="You are a Telegram bot",
|
||
)
|
||
|
||
@plugin.register
|
||
class TelegramAdapter(BaseChannelAdapter):
|
||
...
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
*,
|
||
channel_id: str,
|
||
channel_type: Any = None,
|
||
capabilities: ChannelCapabilities | None = None,
|
||
meta: ChannelMeta | None = None,
|
||
order: int = 100,
|
||
pairing: dict | None = None,
|
||
conversation_bindings: dict | None = None,
|
||
agent_prompt: str | None = None,
|
||
messaging: dict | None = None,
|
||
directory: Any = None,
|
||
):
|
||
self.channel_id = channel_id
|
||
self.channel_type = channel_type
|
||
self.capabilities = capabilities
|
||
self.meta = meta or ChannelMeta(id=channel_id, label=channel_id)
|
||
self.order = order
|
||
self.pairing = pairing or {}
|
||
self.conversation_bindings = conversation_bindings or {}
|
||
self.agent_prompt = agent_prompt
|
||
self.messaging = messaging or {}
|
||
self.directory = directory
|
||
|
||
def register(self, cls: type[BaseChannelAdapter]) -> type[BaseChannelAdapter]:
|
||
if self.capabilities is not None:
|
||
cls.capabilities = self.capabilities # type: ignore[attr-defined]
|
||
if self.meta is not None:
|
||
cls.meta = self.meta # type: ignore[attr-defined]
|
||
if self.channel_type is not None:
|
||
cls.channel_type = self.channel_type # type: ignore[attr-defined]
|
||
if self.pairing:
|
||
cls.pairing = self.pairing # type: ignore[attr-defined]
|
||
if self.conversation_bindings:
|
||
cls.conversation_bindings = self.conversation_bindings # type: ignore[attr-defined]
|
||
if self.agent_prompt is not None:
|
||
cls.agent_prompt = self.agent_prompt # type: ignore[attr-defined]
|
||
if self.messaging:
|
||
cls.messaging = self.messaging # type: ignore[attr-defined]
|
||
if self.directory is not None:
|
||
cls.directory = self.directory # type: ignore[attr-defined]
|
||
|
||
_register_builtin(self.channel_id, cls)
|
||
return cls
|