From cc5dc9266c5d8ac55cbb42d45429fdf62728e87e Mon Sep 17 00:00:00 2001 From: whatevertogo Date: Fri, 13 Mar 2026 04:37:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=99=A8=E5=88=86=E5=8F=91=E5=92=8C=E6=8F=92=E4=BB=B6=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=A2=9E=E5=BC=BA=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7=E5=92=8C=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../astrbot_sdk/runtime/handler_dispatcher.py | 61 ++++++++++++++- src-new/astrbot_sdk/runtime/loader.py | 75 ++++++++++++++++++- src-new/astrbot_sdk/runtime/peer.py | 73 +++++++++++++++++- 3 files changed, 205 insertions(+), 4 deletions(-) diff --git a/src-new/astrbot_sdk/runtime/handler_dispatcher.py b/src-new/astrbot_sdk/runtime/handler_dispatcher.py index d35e0b677..a8501c53a 100644 --- a/src-new/astrbot_sdk/runtime/handler_dispatcher.py +++ b/src-new/astrbot_sdk/runtime/handler_dispatcher.py @@ -1,4 +1,63 @@ -"""插件侧 handler 调度器。 +"""处理器分发模块。 + +定义 HandlerDispatcher 类,负责将能力调用分发到具体的处理器函数。 +支持参数注入、流式执行、错误处理和生命周期回调。 + +核心职责: + - 根据处理器 ID 查找处理器 + - 构建处理器参数(支持类型注解注入) + - 执行处理器并处理结果 + - 处理异步生成器流式结果 + - 统一的错误处理 + +参数注入优先级: + 1. 按类型注解注入(支持 Optional[Type]) + 2. 按参数名注入(兼容无类型注解) + 3. 从 legacy_args 注入(命令参数等) + +支持的注入类型: + - MessageEvent: 消息事件 + - Context: 运行时上下文 + +与旧版对比: + 旧版 HandlerExecutor: + - 从 star_handlers_registry 获取处理器 + - 直接调用 handler(event, **args) + - 无参数注入支持 + - 通过 JSON-RPC notification 发送流式结果 + - 错误通过 JSON-RPC error 响应 + + 新版 HandlerDispatcher: + - 从 LoadedHandler 映射获取处理器 + - 支持类型注解注入 (MessageEvent, Context) + - 支持参数名注入 (event, ctx, context) + - 支持 legacy_args 注入 + - 支持 Optional[Type] 类型 + - 支持默认值 + - 统一的错误处理和 on_error 回调 + +处理器签名兼容: + # 旧版签名 + def handler(event: AstrMessageEvent) -> str: + return "result" + + # 新版签名(类型注入) + async def handler(event: MessageEvent, ctx: Context) -> None: + await event.reply("result") + + # 新版签名(名字注入) + async def handler(event, ctx) -> None: + await ctx.platform.send(event.session_id, "result") + + # 流式处理器 + async def streaming_handler(event: MessageEvent): + yield "chunk 1" + yield "chunk 2" + +结果处理: + - PlainTextResult: 调用 event.reply() + - str: 调用 event.reply() + - dict with "text": 调用 event.reply(str(item["text"])) `HandlerDispatcher` 把运行时收到的 `handler.invoke` 请求转成真实 Python 调用。 它的职责只包括参数注入、legacy 返回值兼容和错误回调;不负责 handler 发现或 diff --git a/src-new/astrbot_sdk/runtime/loader.py b/src-new/astrbot_sdk/runtime/loader.py index a3902702b..45f5b1ea8 100644 --- a/src-new/astrbot_sdk/runtime/loader.py +++ b/src-new/astrbot_sdk/runtime/loader.py @@ -1,4 +1,77 @@ -"""插件发现、环境准备和组件加载。 +"""插件加载模块。 + +定义插件发现、环境管理和加载的核心逻辑。 +支持新旧两种 Star 组件的兼容加载。 + +核心概念: + PluginSpec: 插件规范,描述插件的基本信息 + PluginDiscoveryResult: 插件发现结果,包含成功和跳过的插件 + PluginEnvironmentManager: 插件虚拟环境管理器 + LoadedHandler: 加载后的处理器,包含描述符和可调用对象 + LoadedPlugin: 加载后的插件,包含处理器和实例 + +插件发现流程: + 1. 扫描 plugins_dir 下的子目录 + 2. 检查 plugin.yaml 和 requirements.txt + 3. 解析 manifest_data 获取插件信息 + 4. 验证必要字段(name, components, runtime.python) + 5. 返回 PluginDiscoveryResult + +环境管理流程: + 1. 检查 .venv 目录是否存在 + 2. 检查 Python 版本是否匹配 + 3. 检查指纹是否变化(requirements 内容) + 4. 必要时重建虚拟环境 + 5. 使用 uv 安装依赖 + +插件加载流程: + 1. 将插件目录添加到 sys.path + 2. 遍历 components 列表 + 3. 动态导入组件类 + 4. 判断是否为新版 Star + 5. 创建实例(新版直接实例化,旧版传入 legacy_context) + 6. 扫描处理器方法 + 7. 构建 HandlerDescriptor + +新旧 Star 组件兼容: + 新版 Star: + - 继承自 Star 基类 + - __astrbot_is_new_star__ 返回 True + - 无参构造函数 + - 通过 @handler 装饰器注册处理器 + + 旧版 Star: + - 不继承或 __astrbot_is_new_star__ 返回 False + - 需要 legacy_context 参数 + - 通过 @xxx_handler 装饰器注册处理器 + - 使用 extras_configs 传递配置 + +与旧版对比: + 旧版 StarManager: + - 通过 plugin.yaml 发现插件 + - 动态导入组件类并实例化 + - 注册到 star_handlers_registry + - 使用 functools.partial 绑定实例 + - 无环境管理 + - 无指纹缓存 + + 新版 loader.py: + - PluginSpec 描述插件规范 + - PluginEnvironmentManager 管理虚拟环境 + - load_plugin() 加载并解析组件 + - LoadedHandler 封装处理器和描述符 + - 支持新旧 Star 组件兼容 + - 支持环境指纹缓存 + +plugin.yaml 格式: + name: my_plugin + author: author_name + desc: Plugin description + version: 1.0.0 + runtime: + python: "3.11" + components: + - class: my_plugin.main:MyComponent `loader` 是 runtime 与插件代码之间的边界层,负责三件事: diff --git a/src-new/astrbot_sdk/runtime/peer.py b/src-new/astrbot_sdk/runtime/peer.py index 2afcf8c0c..57eb73679 100644 --- a/src-new/astrbot_sdk/runtime/peer.py +++ b/src-new/astrbot_sdk/runtime/peer.py @@ -1,4 +1,73 @@ -"""运行时协议对等端。 +"""协议对等端模块。 + +定义 Peer 类,封装双向传输通道上的消息收发、初始化握手、能力调用、 +流式事件转发与取消处理。这里的 peer 指"通信对端/本端"这一网络协议概念, +而不是业务上的用户、群聊或会话对象。 + +核心职责: + - 消息序列化/反序列化 + - 初始化握手协议 + - 能力调用(同步/流式) + - 取消处理 + - 连接生命周期管理 +消息处理: + 入站: + ResultMessage -> 唤醒等待的 Future + EventMessage -> 投递到流式队列 + InitializeMessage -> 调用 initialize_handler + InvokeMessage -> 创建任务调用 invoke_handler + CancelMessage -> 取消对应的任务 + + 出站: + initialize() -> InitializeMessage + invoke() -> InvokeMessage(stream=False) + invoke_stream() -> InvokeMessage(stream=True) + cancel() -> CancelMessage + +与旧版对比: + 旧版 JSON-RPC: + - 分离的 JSONRPCClient 和 JSONRPCServer + - 通过 method 字段区分操作类型 + - 使用 JSONRPCRequest/Response 消息类型 + - 流式通过独立的 notification 实现 + - 无统一的取消机制 + + 新版 Peer: + - 统一的 Peer 抽象,既是客户端也是服务端 + - 通过 type 字段区分消息类型 + - 使用 InitializeMessage/InvokeMessage/EventMessage 等 + - 流式通过 EventMessage(phase=delta) 实现 + - 统一的 CancelMessage 取消机制 + +使用示例: + # 作为客户端发起调用 + peer = Peer(transport=transport, peer_info=PeerInfo(...)) + await peer.start() + output = await peer.initialize(handlers) + result = await peer.invoke("llm.chat", {"prompt": "hello"}) + + # 作为服务端处理调用 + peer.set_invoke_handler(my_handler) + await peer.start() + +消息处理流程: + 入站消息: + ResultMessage -> 唤醒等待的 Future + EventMessage -> 投递到流式队列 + InitializeMessage -> 调用 _initialize_handler + InvokeMessage -> 创建任务调用 _invoke_handler + CancelMessage -> 取消对应的任务 + + 出站消息: + initialize() -> InitializeMessage + invoke() -> InvokeMessage(stream=False) + invoke_stream() -> InvokeMessage(stream=True) + cancel() -> CancelMessage + +取消机制: + - CancelToken 用于检查取消状态 + - 入站任务在收到 CancelMessage 时被取消 + - 早到取消:在任务执行前检查 cancel_token,避免竞态条件 `Peer` 把 `Transport` 和 v4 协议消息模型接起来,负责: @@ -344,7 +413,7 @@ class Peer: if isinstance(exc, AstrBotError): error = exc else: - error = AstrBotError.protocol_error(f"协议消息处理失败: {exc}") + error = AstrBotError.protocol_error(f"无法解析协议消息: {exc}") await self._fail_connection(error) raise error from exc