新增大量渠道适配器相关的协议、策略、工具类与基础设施代码,包括: 1. 多协议定义:认证、消息、配置、网关等核心接口 2. 策略模块:上下文、群聊、去重、防抖等业务策略 3. 工具集:重试、去重、文本分块、消息格式化等SDK工具 4. 基础设施:外部进程管理、事件广播、熔断机制等 5. 账户与管道系统:账户管理、消息处理管道实现 6. 运行时服务:状态收集、维护任务、日志等后台服务
114 lines
3.6 KiB
Python
114 lines
3.6 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 _BUILTIN_ADAPTERS
|
||
|
||
|
||
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]
|
||
|
||
_BUILTIN_ADAPTERS[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"),
|
||
)
|
||
|
||
@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]
|
||
|
||
_BUILTIN_ADAPTERS[self.channel_id] = cls
|
||
return cls
|