mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-10-25 09:52:11 +08:00
fix(provider): filter abort_signal from payloads to avoid JSON serialize error
`abort_signal` (asyncio.Event) is passed via **kwargs into payloads during tool_call streaming, causing "Object of type Event is not JSON serializable" when the OpenAI client tries to serialize the request body. Regression test added: test_prepare_chat_payload_strips_non_json_serializable_kwargs
This commit is contained in:
@@ -44,12 +44,22 @@ class PluginLoader(ABC):
|
||||
|
||||
### 2. InProcessPluginLoader 实现
|
||||
|
||||
**决定**:Python 模块直接导入,插件实例化后存入 `Dict[str, PluginInstance]`。
|
||||
> **⚠️ 架构约束**:核心加载逻辑在 Rust FFI,Python 仅做胶水层
|
||||
|
||||
**决定**:`PluginLoader` trait 定义在 Python,`load/unload/reload` 实现调用 Rust FFI(`load_plugin()`/`unload_plugin()`)。
|
||||
|
||||
```python
|
||||
class InProcessPluginLoader(PluginLoader):
|
||||
async def load(self, plugin_id, config, data_dirs):
|
||||
# 调用 Rust FFI: _core.load_plugin(config)
|
||||
result = await rust_ffi.load_plugin(plugin_id, config, data_dirs)
|
||||
return PluginInstance(plugin_id, result.instance, ...)
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 无进程通信开销,性能最优
|
||||
- 与项目 "Rust 核心 + Python 胶水层" 架构一致
|
||||
- 插件需遵循 `ABPPluginProtocol` 接口约定
|
||||
- 核心加载逻辑在 Rust(线程安全、错误隔离)
|
||||
- Python 胶水层仅做类型转换和聚合
|
||||
- 符合 config.yaml `rust_core` 规范
|
||||
|
||||
### 3. 工具发现架构
|
||||
|
||||
@@ -70,17 +80,20 @@ ToolRegistry
|
||||
|
||||
### 4. FFI 绑定链路
|
||||
|
||||
**决定**:Python → Rust `_core.so` → ABP PluginLoader。
|
||||
> **⚠️ 禁止 ctypes**:所有 FFI 必须通过 PyO3(rust-ffi.md 规范)
|
||||
|
||||
**决定**:Python → PyO3 `_core.so` → ABP PluginLoader。
|
||||
|
||||
```
|
||||
Python PluginManager
|
||||
→ ctypes / cffi 调用 _core.so
|
||||
Python PluginRegistry (_internal/)
|
||||
→ PyO3 调用 _core.so
|
||||
→ Rust abp_plugin_loader_* 函数
|
||||
→ 返回 Python 对象(通过 .pyi 类型提示)
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- Rust 源码待提交,当前仅使用编译产物
|
||||
- PyO3 是 Rust 官方 Python 绑定方案
|
||||
- `rust-ffi.md` 明确禁止 ctypes
|
||||
- `_core.pyi` 提供类型检查
|
||||
- anyio 异步调用通过 `run_in_executor` 封装
|
||||
|
||||
@@ -94,9 +107,9 @@ Python PluginManager
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. **Phase 1**:PluginLoader trait + InProcessPluginLoader(对现有 OutOfProcess 透明)
|
||||
2. **Phase 2**:tools/list + ToolRegistry
|
||||
3. **Phase 3**:FFI 绑定 + config.yaml 集成
|
||||
1. **Phase 1**:PluginLoader trait + Python 聚合层(调用 Rust FFI)
|
||||
2. **Phase 2**:tools/list + ToolRegistry(Rust FFI + Python 聚合)
|
||||
3. **Phase 3**:PyO3 FFI 绑定 + config.yaml 集成
|
||||
4. **Phase 4**:测试覆盖 + 文档
|
||||
|
||||
## Open Questions
|
||||
|
||||
@@ -26,17 +26,17 @@ ABP 插件加载器抽象接口,定义进程内/外插件的统一加载方式
|
||||
- **AND** 重新加载并初始化插件
|
||||
|
||||
### Requirement: In-Process Plugin Loading
|
||||
进程内插件直接加载为 Python 模块,由 Python 胶水层管理生命周期。
|
||||
进程内插件由 Rust FFI 核心管理生命周期,Python 胶水层仅做聚合。
|
||||
|
||||
#### Scenario: In-Process Plugin Invocation
|
||||
- **WHEN** 调用进程内插件方法(如 `handle_event`)
|
||||
- **THEN** 直接通过 Python 对象调用
|
||||
- **AND** 不经过序列化/反序列化
|
||||
- **AND** 无进程通信开销
|
||||
- **THEN** 通过 Rust FFI `load_plugin()` 加载
|
||||
- **AND** Python PluginRegistry 聚合插件实例
|
||||
- **AND** 调用通过 Rust FFI 转发
|
||||
|
||||
#### Scenario: In-Process Plugin Context
|
||||
- **WHEN** 插件实例化时
|
||||
- **THEN** 主进程传入 context 对象
|
||||
- **THEN** Rust 核心传入 context 对象
|
||||
- **AND** 传入 user_config(来自握手)
|
||||
- **AND** 传入 data_dirs(数据目录路径)
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
- **AND** 跳过该工具(不注册)
|
||||
|
||||
### Requirement: ToolRegistry
|
||||
> **架构约束**:核心路由在 Rust FFI(`tool_router.rs`),Python 层仅做聚合
|
||||
|
||||
中心化工具注册表,支持跨插件工具发现和调用。
|
||||
|
||||
#### Scenario: Register Tool
|
||||
@@ -35,18 +37,17 @@
|
||||
|
||||
#### Scenario: Discover All Tools
|
||||
- **WHEN** 调用 `registry.list_tools()`
|
||||
- **THEN** 返回所有插件注册的工具
|
||||
- **THEN** 聚合所有插件注册的工具(通过 Rust FFI `list_tools()`)
|
||||
- **AND** 每个工具标记来源 plugin_id
|
||||
|
||||
#### Scenario: Call Tool
|
||||
- **WHEN** 调用 `registry.call_tool(tool_name, args)`
|
||||
- **THEN** 查找工具所属插件
|
||||
- **AND** 转发调用到对应插件
|
||||
- **THEN** 转发调用到 Rust FFI `route_tool_call()`
|
||||
- **AND** 返回执行结果
|
||||
|
||||
#### Scenario: Tool Not Found
|
||||
- **WHEN** 调用不存在的工具
|
||||
- **THEN** 抛出 `ToolNotFoundError` 错误
|
||||
- **THEN** Rust FFI 抛出 `ToolNotFoundError` 错误
|
||||
- **AND** 错误码 -32203
|
||||
|
||||
### Requirement: Cross-Plugin Tool Discovery
|
||||
|
||||
@@ -1,45 +1,49 @@
|
||||
## 1. PluginLoader Trait & InProcessPluginLoader
|
||||
## 1. PluginLoader Trait & Python 聚合层
|
||||
|
||||
- [ ] 1.1 Define `PluginLoader` abstract base class in `astrbot/core/plugin/loader.py`
|
||||
> **架构边界**:核心加载逻辑在 Rust FFI,Python 胶水层仅做类型转换和聚合
|
||||
|
||||
- [ ] 1.1 Define `PluginLoader` abstract base class in `astrbot/_internal/protocols/abp/loader.py`
|
||||
- [ ] 1.2 Define `PluginInstance` dataclass (plugin_id, instance, metadata)
|
||||
- [ ] 1.3 Implement `InProcessPluginLoader.load()` (Python module import)
|
||||
- [ ] 1.4 Implement `InProcessPluginLoader.unload()` (remove from registry)
|
||||
- [ ] 1.5 Implement `InProcessPluginLoader.reload()` (reload module)
|
||||
- [ ] 1.6 Create `PluginRegistry` class (Dict[str, PluginInstance] + singleton)
|
||||
- [ ] 1.7 Add `register()` and `unregister()` methods to PluginRegistry
|
||||
- [ ] 1.3 Define `PluginRegistry` class in `astrbot/_internal/protocols/abp/registry.py` (Python 聚合层,调用 Rust FFI)
|
||||
- [ ] 1.4 Add `register()` and `unregister()` methods to PluginRegistry
|
||||
- [ ] 1.5 Add `load_plugin()` / `unload_plugin()` wrapper calling Rust FFI
|
||||
|
||||
## 2. Tool Discovery & Registry
|
||||
|
||||
- [ ] 2.1 Create `astrbot/core/plugin/tool_registry.py`
|
||||
> **架构边界**:`tool_router.rs` 核心在 Rust,Python 层仅做聚合和转发
|
||||
|
||||
- [ ] 2.1 Create `astrbot/_internal/protocols/abp/tool_registry.py`
|
||||
- [ ] 2.2 Implement `ToolDef` dataclass (name, description, parameters schema)
|
||||
- [ ] 2.3 Implement `ToolRegistry.register(plugin_id, tools)` with Schema validation
|
||||
- [ ] 2.4 Implement `ToolRegistry.list_tools()` (aggregate all plugins)
|
||||
- [ ] 2.5 Implement `ToolRegistry.call_tool(tool_name, args)`
|
||||
- [ ] 2.3 Implement `ToolRegistry.register(plugin_id, tools)` with Schema validation (Python)
|
||||
- [ ] 2.4 Implement `ToolRegistry.list_tools()` (aggregate all plugins via Rust FFI `list_tools()`)
|
||||
- [ ] 2.5 Implement `ToolRegistry.call_tool(tool_name, args)` (delegate to Rust FFI `route_tool_call()`)
|
||||
- [ ] 2.6 Implement `tools/list` JSON-RPC endpoint in plugin base class
|
||||
- [ ] 2.7 Add JSON Schema Draft-07 validation (jsonschema library)
|
||||
|
||||
## 3. FFI Bindings
|
||||
|
||||
> **⚠️ 禁止使用 ctypes**:所有 FFI 必须通过 PyO3 绑定(rust-ffi.md 规范)
|
||||
|
||||
- [ ] 3.1 Audit existing `_core.pyi` for missing ABP types
|
||||
- [ ] 3.2 Add `plugin_loader_*` FFI function signatures to rust-ffi.md (if missing)
|
||||
- [ ] 3.3 Implement Python → Rust plugin loader calls via ctypes
|
||||
- [ ] 3.4 Add async wrapper using `run_in_executor` for FFI calls
|
||||
- [ ] 3.5 Update `plugin_manager.py` to use FFI bindings
|
||||
- [ ] 3.3 Implement Python → Rust plugin loader calls via **PyO3** binding
|
||||
- [ ] 3.4 Add async wrapper using `run_in_executor` for PyO3 FFI calls
|
||||
- [ ] 3.5 Update `astrbot/_internal/protocols/abp/manager.py` to use FFI bindings
|
||||
|
||||
## 4. Configuration Integration
|
||||
|
||||
- [ ] 4.1 Define `plugins` section in `config.yaml` schema
|
||||
- [ ] 4.2 Implement `PluginConfig` dataclass parsing
|
||||
- [ ] 4.3 Integrate PluginRegistry initialization into AstrBot startup
|
||||
- [ ] 4.4 Add connection pooling for Unix Socket transport (task 3.4 from original)
|
||||
- [ ] 4.4 Add connection pooling for Unix Socket transport
|
||||
- [ ] 4.5 Write integration test for config → plugin loading flow
|
||||
|
||||
## 5. Testing
|
||||
|
||||
- [ ] 5.1 Write unit tests for PluginLoader trait
|
||||
- [ ] 5.2 Write unit tests for InProcessPluginLoader
|
||||
- [ ] 5.2 Write unit tests for Python PluginRegistry (mock Rust FFI)
|
||||
- [ ] 5.3 Write unit tests for ToolRegistry
|
||||
- [ ] 5.4 Write integration tests for in-process plugin loading
|
||||
- [ ] 5.4 Write integration tests for plugin loading (requires Rust FFI stub)
|
||||
- [ ] 5.5 Write integration tests for tools/list + tools/call flow
|
||||
- [ ] 5.6 Run `ruff check .` and `ruff format .`
|
||||
- [ ] 5.7 Run `uvx ty check` for type validation
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-27
|
||||
@@ -1,73 +0,0 @@
|
||||
## Context
|
||||
|
||||
AstrBot 当前使用 Star 插件系统,插件与核心紧耦合。ABP 协议需要实现:
|
||||
- 插件作为独立服务(进程内/外)
|
||||
- 主进程不读取插件配置文件,通过协议握手交换
|
||||
- 数据目录由主进程分配
|
||||
- 支持 Stdio/Unix Socket/HTTP 三种传输方式
|
||||
- Rust 核心实现协议逻辑(`astrbot/rust/src/abp/`)
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 实现 ABP 协议核心:握手、消息路由、生命周期管理
|
||||
- 支持进程内插件(Python 直接调用)和进程外插件(独立进程)
|
||||
- 支持 Stdio/Unix Socket/HTTP 传输层
|
||||
- 实现工具调用(tools/list, tools/call)和消息处理(plugin.handle_event)
|
||||
- 实现事件订阅系统(plugin.subscribe, plugin.notify)
|
||||
|
||||
**Non-Goals:**
|
||||
- 不实现 MCP 协议(独立协议)
|
||||
- 不实现 A2A/ACP 协议(独立协议)
|
||||
- 不在 Python 层重复核心逻辑
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Rust 核心实现 vs Python 实现
|
||||
|
||||
**决定**:核心协议逻辑在 Rust 中实现(`astrbot/rust/src/abp/`),Python 只做 FFI 胶水层。
|
||||
|
||||
**理由**:
|
||||
- ABP 协议是高性能路径,消息路由需要低延迟
|
||||
- 与项目 "Rust 核心 + Python 胶水层" 架构一致
|
||||
- 多语言插件需要稳定的 Rust FFI 接口
|
||||
|
||||
**替代方案**:
|
||||
- Python 实现(被否定):性能不足,违反项目架构
|
||||
- Go 实现(被否定):引入新语言栈,增加维护成本
|
||||
|
||||
### 2. 传输层选择
|
||||
|
||||
**决定**:支持 Stdio(进程启动)、Unix Socket(本地进程间)、HTTP/SSE(远程)三种。
|
||||
|
||||
**理由**:
|
||||
- Stdio:最简单的跨语言方案,适合单次请求
|
||||
- Unix Socket:低延迟,适合本地进程外插件
|
||||
- HTTP/SSE:支持远程插件和 Webhook 通知
|
||||
|
||||
### 3. 插件配置管理
|
||||
|
||||
**决定**:主进程不读取插件配置文件,配置通过握手 `initialize` 传递。
|
||||
|
||||
**理由**:
|
||||
- 零侵入性:插件无需了解 AstrBot 目录结构
|
||||
- 统一管理:所有配置通过 WebUI 管理
|
||||
- 安全:主进程控制插件可见的配置
|
||||
|
||||
### 4. JSON-RPC 2.0 vs 自定义协议
|
||||
|
||||
**决定**:采用 JSON-RPC 2.0。
|
||||
|
||||
**理由**:
|
||||
- 成熟标准,生态丰富
|
||||
- 多语言支持(Python、Rust、Go 等)
|
||||
- 易于调试和测试
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| Rust FFI 接口不稳定 | Python 胶水层需要频繁调整 | 接口版本化,保持向后兼容 |
|
||||
| 进程外插件通信延迟 | 工具调用性能下降 | 使用 Unix Socket 代替 HTTP |
|
||||
| 插件崩溃影响主进程 | 系统稳定性下降 | 进程隔离 + 超时控制 |
|
||||
| 配置 Schema 同步 | WebUI 表单与实际不一致 | 握手时交换 Schema |
|
||||
@@ -1,35 +0,0 @@
|
||||
## Why
|
||||
|
||||
AstrBot 需要一个统一的插件协议来实现插件的加载、消息处理、工具调用和生命周期管理。当前的 Star 插件系统与核心紧耦合,插件无法独立运行,缺乏标准化接口。ABP 协议将插件系统从核心中解耦,实现插件的零侵入性和跨语言支持。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **新增 ABP 协议层**:标准化的插件通信协议(JSON-RPC 2.0)
|
||||
- **新增插件加载器**:支持进程内(`in_process`)和进程外(`out_of_process`)两种模式
|
||||
- **新增传输层**:Stdio、Unix Socket、HTTP/SSE 三种传输方式
|
||||
- **新增插件生命周期管理**:初始化、启动、停止、重载、配置更新
|
||||
- **新增工具调用机制**:标准化的 `tools/list` 和 `tools/call` 接口
|
||||
- **新增消息处理**:通过 `plugin.handle_event` 处理平台事件
|
||||
- **新增事件订阅**:插件可订阅 LLM 请求等系统事件
|
||||
- **Rust 核心实现**:协议核心逻辑在 Rust 运行时(`astrbot/rust/src/`)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `abp-protocol`: ABP 协议核心 - 握手、消息路由、生命周期管理
|
||||
- `abp-transport`: ABP 传输层 - Stdio/Unix Socket/HTTP 实现
|
||||
- `abp-plugin-loader`: ABP 插件加载器 - 进程内/外插件管理
|
||||
- `abp-tool-router`: ABP 工具路由 - 跨插件工具发现和调用
|
||||
- `abp-event-system`: ABP 事件系统 - 事件订阅和通知
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `agent-message` (待定): 如果需要与 Agent 消息流程整合,可能需要调整
|
||||
|
||||
## Impact
|
||||
|
||||
- **新增目录**:`astrbot/rust/src/abp/` - Rust ABP 核心实现
|
||||
- **新增 Python 胶水层**:`astrbot/core/plugin/` - Python FFI 胶水代码
|
||||
- **配置文件变更**:`config.yaml` 中新增 `plugins` 配置项
|
||||
- **依赖**:`jsonrpc-derive`(Rust)、`jsonrpc-core`(Rust)
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Event Subscription
|
||||
插件 SHALL 支持订阅系统事件。
|
||||
|
||||
#### Scenario: Subscribe to Event
|
||||
- **WHEN** 主进程发送 `plugin.subscribe` 请求,包含 event_type
|
||||
- **THEN** 插件注册订阅
|
||||
- **AND** 主进程在事件发生时通知插件
|
||||
|
||||
#### Scenario: Unsubscribe from Event
|
||||
- **WHEN** 主进程发送 `plugin.unsubscribe` 请求
|
||||
- **THEN** 插件取消订阅
|
||||
- **AND** 不再接收该类型事件通知
|
||||
|
||||
#### Scenario: Event Subscription Failed
|
||||
- **WHEN** 订阅的事件类型不支持
|
||||
- **THEN** 插件返回 `-32207` (Event Subscribe Failed) 错误码
|
||||
|
||||
### Requirement: Event Notification
|
||||
插件 SHALL 支持接收和发送事件通知。
|
||||
|
||||
#### Scenario: Receive Event Notification
|
||||
- **WHEN** 主进程有事件发生(llm_request、tool_called 等)
|
||||
- **THEN** 主进程发送 `plugin.notify` 通知到插件
|
||||
- **AND** 包含 event_type 和 data
|
||||
|
||||
#### Scenario: Send Event from Plugin
|
||||
- **WHEN** 插件需要通知主进程
|
||||
- **THEN** 插件发送 `plugin.notify` 到主进程
|
||||
- **AND** 主进程路由到对应处理器
|
||||
|
||||
### Requirement: Supported Event Types
|
||||
插件 SHALL 支持以下事件类型。
|
||||
|
||||
#### Scenario: LLM Request Event
|
||||
- **WHEN** LLM 开始处理请求
|
||||
- **THEN** 主进程通知订阅者,包含请求 ID 和 prompt
|
||||
|
||||
#### Scenario: Tool Called Event
|
||||
- **WHEN** 工具被调用
|
||||
- **THEN** 主进程通知订阅者,包含工具名称和参数
|
||||
|
||||
#### Scenario: Message Received Event
|
||||
- **WHEN** 收到用户消息
|
||||
- **THEN** 主进程通知订阅者,包含消息内容
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: In-Process Plugin Loading
|
||||
进程内插件直接加载为 Python 模块,由 Rust 核心调用。
|
||||
|
||||
#### Scenario: Load In-Process Plugin
|
||||
- **WHEN** 配置指定 `load_mode: "in_process"`
|
||||
- **THEN** 主进程直接导入插件模块
|
||||
- **AND** 创建插件实例,传入 context、user_config、data_dirs
|
||||
|
||||
#### Scenario: In-Process Plugin Invocation
|
||||
- **WHEN** 调用插件方法
|
||||
- **THEN** 直接通过 Python 对象调用
|
||||
- **AND** 不经过序列化/反序列化
|
||||
|
||||
### Requirement: Out-of-Process Plugin Loading
|
||||
进程外插件作为独立进程运行,通过传输层通信。
|
||||
|
||||
#### Scenario: Load Out-of-Process Plugin
|
||||
- **WHEN** 配置指定 `load_mode: "out_of_process"`
|
||||
- **THEN** 主进程启动插件子进程
|
||||
- **AND** 建立传输层连接(Stdio/Unix Socket/HTTP)
|
||||
|
||||
#### Scenario: Plugin Process Management
|
||||
- **WHEN** 插件进程异常退出
|
||||
- **THEN** 主进程记录错误日志
|
||||
- **AND** 通知相关系统组件
|
||||
- **AND** 可选:自动重启插件
|
||||
|
||||
### Requirement: Plugin Configuration
|
||||
插件配置通过配置文件声明,不包含敏感信息和内部配置。
|
||||
|
||||
#### Scenario: Plugin Config Declaration
|
||||
- **WHEN** 用户在 AstrBot 配置中声明插件
|
||||
- **THEN** 仅包含 name、load_mode、command、args、transport、url
|
||||
- **AND** 不包含插件内部配置
|
||||
|
||||
#### Scenario: User Config Delivery
|
||||
- **WHEN** 插件初始化时
|
||||
- **THEN** 主进程通过握手将 user_config 传递给插件
|
||||
- **AND** user_config 来自 secrets.yaml 或 WebUI
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Initialize Handshake
|
||||
AstrBot 主进程 SHALL 发送 `initialize` 请求到插件,交换协议版本、客户端信息和配置。
|
||||
|
||||
#### Scenario: Successful Initialization
|
||||
- **WHEN** 插件进程启动并准备就绪
|
||||
- **THEN** 主进程发送 `initialize` 请求,包含 `protocolVersion`、`clientInfo`、`capabilities`、`pluginConfig`、`dataDirs`
|
||||
- **AND** 插件返回 `initialized` 响应,包含 `protocolVersion`、`serverInfo`、`capabilities`、`configSchema`、`metadata`
|
||||
|
||||
#### Scenario: Version Mismatch
|
||||
- **WHEN** 插件不支持客户端的协议版本
|
||||
- **THEN** 插件返回 `-32211` (Version Mismatch) 错误码
|
||||
|
||||
### Requirement: Plugin Lifecycle Management
|
||||
主进程 SHALL 管理插件的启动、停止、重载和配置更新。
|
||||
|
||||
#### Scenario: Start Plugin
|
||||
- **WHEN** 主进程需要激活插件
|
||||
- **THEN** 发送 `plugin.start` 请求
|
||||
- **AND** 插件进入工作状态
|
||||
|
||||
#### Scenario: Stop Plugin
|
||||
- **WHEN** 主进程需要停用插件
|
||||
- **THEN** 发送 `plugin.stop` 请求
|
||||
- **AND** 插件清理资源并进入空闲状态
|
||||
|
||||
#### Scenario: Reload Plugin
|
||||
- **WHEN** 插件需要热重载
|
||||
- **THEN** 发送 `plugin.reload` 请求
|
||||
- **AND** 插件重新初始化但保持进程
|
||||
|
||||
#### Scenario: Config Update
|
||||
- **WHEN** 用户更新插件配置
|
||||
- **THEN** 主进程发送 `plugin.config_update` 通知
|
||||
- **AND** 插件更新内部配置
|
||||
|
||||
### Requirement: Plugin Error Handling
|
||||
插件 SHALL 返回标准错误码用于问题诊断。
|
||||
|
||||
#### Scenario: Plugin Not Found
|
||||
- **WHEN** 请求的插件不存在
|
||||
- **THEN** 返回 `-32200` (Plugin Not Found) 错误码
|
||||
|
||||
#### Scenario: Plugin Not Ready
|
||||
- **WHEN** 请求时插件未完成初始化
|
||||
- **THEN** 返回 `-32201` (Plugin Not Ready) 错误码
|
||||
|
||||
#### Scenario: Plugin Crashed
|
||||
- **WHEN** 插件进程异常退出
|
||||
- **THEN** 主进程检测到并返回 `-32202` (Plugin Crashed) 错误码
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Tool Listing
|
||||
主进程 SHALL 支持通过 `tools/list` 获取插件提供的工具列表。
|
||||
|
||||
#### Scenario: List Available Tools
|
||||
- **WHEN** 主进程发送 `tools/list` 请求
|
||||
- **THEN** 插件返回可用工具定义列表
|
||||
- **AND** 每个工具包含 name、description、parameters
|
||||
|
||||
### Requirement: Tool Calling
|
||||
主进程 SHALL 支持通过 `tools/call` 调用插件提供的工具。
|
||||
|
||||
#### Scenario: Call Tool with Arguments
|
||||
- **WHEN** 主进程发送 `tools/call` 请求,包含 tool name 和 arguments
|
||||
- **THEN** 插件执行对应工具
|
||||
- **AND** 返回工具执行结果(content 数组)
|
||||
|
||||
#### Scenario: Tool Not Found
|
||||
- **WHEN** 请求的工具名称不存在
|
||||
- **THEN** 插件返回 `-32203` (Tool Not Found) 错误码
|
||||
|
||||
#### Scenario: Tool Call Failed
|
||||
- **WHEN** 工具执行过程中发生错误
|
||||
- **THEN** 插件返回 `-32204` (Tool Call Failed) 错误码
|
||||
- **AND** 错误信息包含原因
|
||||
|
||||
### Requirement: Tool Result Format
|
||||
工具调用结果 SHALL 符合标准格式。
|
||||
|
||||
#### Scenario: Successful Tool Result
|
||||
- **WHEN** 工具成功执行
|
||||
- **THEN** 返回 result,包含 content 数组
|
||||
- **AND** content 每个元素包含 type 和 text/image/url
|
||||
|
||||
### Requirement: Tool Metadata
|
||||
工具定义 SHALL 包含完整的元数据用于 LLM 函数调用。
|
||||
|
||||
#### Scenario: Tool Definition Structure
|
||||
- **WHEN** 插件声明工具
|
||||
- **THEN** 必须包含:name (string)、description (string)、parameters (JSON Schema)
|
||||
- **AND** parameters 必须符合 JSON Schema Draft-07 格式
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Stdio Transport
|
||||
插件通过标准输入/输出进行 JSON-RPC 通信。
|
||||
|
||||
#### Scenario: Stdio Message Format
|
||||
- **WHEN** 发送 JSON-RPC 消息
|
||||
- **THEN** 每条消息为单行 JSON,不包含长度前缀
|
||||
- **AND** 消息之间以换行符分隔
|
||||
|
||||
### Requirement: Unix Socket Transport
|
||||
进程间通过 Unix Socket 进行通信,使用 Content-Length 协议。
|
||||
|
||||
#### Scenario: Unix Socket Message Format
|
||||
- **WHEN** 发送 JSON-RPC 消息
|
||||
- **THEN** 消息前添加 `Content-Length: <bytes>\r\n\r\n` 前缀
|
||||
- **AND** 消息体为 UTF-8 编码的 JSON
|
||||
|
||||
#### Scenario: Unix Socket Connection Lifecycle
|
||||
- **WHEN** 主进程连接插件 Unix Socket
|
||||
- **THEN** 建立持久连接
|
||||
- **AND** 双向复用同一连接发送请求/响应
|
||||
|
||||
### Requirement: HTTP/SSE Transport
|
||||
远程插件使用 HTTP 进行请求,SSE 进行服务端推送。
|
||||
|
||||
#### Scenario: HTTP Request
|
||||
- **WHEN** 主进程发送 HTTP 请求到插件
|
||||
- **THEN** 使用 POST 方法,Content-Type 为 `application/json`
|
||||
- **AND** 请求体为 JSON-RPC 请求对象
|
||||
|
||||
#### Scenario: SSE Event Stream
|
||||
- **WHEN** 插件需要推送通知
|
||||
- **THEN** 使用 Server-Sent Events 格式
|
||||
- **AND** 事件类型为 `plugin.notify`
|
||||
@@ -1,70 +0,0 @@
|
||||
## 1. Rust Core Infrastructure
|
||||
|
||||
- [x] 1.1 Create `astrbot/rust/src/abp/` directory structure (already exists with abp.rs)
|
||||
- [x] 1.2 Add ABP dependencies to `astrbot/rust/Cargo.toml` (tokio, serde, etc. already present)
|
||||
- [x] 1.3 Define ABP error types and codes (-32700 to -32211)
|
||||
|
||||
## 2. ABP Protocol Core (abp-protocol)
|
||||
|
||||
- [x] 2.1 Implement Initialize handshake (C→P: protocolVersion, clientInfo, capabilities, pluginConfig, dataDirs)
|
||||
- [x] 2.2 Implement Initialize Response (P→C: protocolVersion, serverInfo, capabilities, configSchema, metadata)
|
||||
- [x] 2.3 Implement Plugin lifecycle methods (plugin.start, plugin.stop, plugin.reload, plugin.config_update)
|
||||
- [x] 2.4 Implement plugin.error_handler (PluginNotFound, PluginNotReady, PluginCrashed - via error codes)
|
||||
|
||||
## 3. Transport Layer (abp-transport)
|
||||
|
||||
- [x] 3.1 Implement Stdio transport (single-line JSON messages) - in Python transport.py
|
||||
- [x] 3.2 Implement Unix Socket transport (Content-Length framing) - in Rust abp.rs and Python
|
||||
- [x] 3.3 Implement HTTP/SSE transport (POST requests, SSE streams) - in Rust abp.rs and Python
|
||||
- [ ] 3.4 Add connection pooling for Unix Socket
|
||||
|
||||
## 4. Plugin Loader (abp-plugin-loader)
|
||||
|
||||
- [ ] 4.1 Implement PluginLoader trait
|
||||
- [ ] 4.2 Implement InProcessPluginLoader (Python module loading)
|
||||
- [x] 4.3 Implement OutOfProcessPluginLoader (process spawning + transport)
|
||||
- [x] 4.4 Implement plugin config parsing (name, load_mode, command, args, transport, url) - in Rust abp.rs
|
||||
- [x] 4.5 Implement data directory allocation (dataDirs.root, dataDirs.plugin_data, dataDirs.temp)
|
||||
|
||||
## 5. Tool Router (abp-tool-router)
|
||||
|
||||
- [ ] 5.1 Implement tools/list endpoint (return tool definitions)
|
||||
- [x] 5.2 Implement tools/call endpoint (execute tool with arguments) - in Rust abp.rs
|
||||
- [x] 5.3 Implement tool result formatting (content array with type/text) - in Rust abp.rs
|
||||
- [ ] 5.4 Add tool schema validation (JSON Schema Draft-07)
|
||||
- [ ] 5.5 Implement cross-plugin tool discovery
|
||||
|
||||
## 6. Event System (abp-event-system)
|
||||
|
||||
- [x] 6.1 Implement plugin.subscribe (register event subscription) - Python client
|
||||
- [x] 6.2 Implement plugin.unsubscribe (remove event subscription) - Python client
|
||||
- [x] 6.3 Implement plugin.notify (bidirectional event notification) - Python client
|
||||
- [x] 6.4 Define event types (llm_request, tool_called, message_received) - in Rust abp.rs
|
||||
- [x] 6.5 Implement event routing (P→C and C→P notifications) - Python client
|
||||
|
||||
## 7. Python Glue Layer
|
||||
|
||||
- [x] 7.1 Create `astrbot/core/plugin/` Python package
|
||||
- [ ] 7.2 Implement FFI bindings to Rust ABP core (_core.so)
|
||||
- [x] 7.3 Create PluginManager Python class (wraps Rust PluginLoader)
|
||||
- [x] 7.4 Create PluginClient Python class (wraps transport)
|
||||
- [x] 7.5 Add type stubs in `astrbot/rust/_core.pyi`
|
||||
|
||||
## 8. Integration & Configuration
|
||||
|
||||
- [ ] 8.1 Add plugins section to config.yaml schema
|
||||
- [ ] 8.2 Implement PluginRegistry (plugin discovery and registration)
|
||||
- [ ] 8.3 Add ABP initialization to AstrBot startup
|
||||
- [ ] 8.4 Integrate with existing Star plugin system (backward compatibility)
|
||||
- [ ] 8.5 Add WebUI support for plugin configuration
|
||||
|
||||
## 9. Testing
|
||||
|
||||
- [ ] 9.1 Write unit tests for ABP protocol core
|
||||
- [ ] 9.2 Write integration tests for transport layer
|
||||
- [ ] 9.3 Write tests for plugin loading (in-process and out-of-process)
|
||||
- [ ] 9.4 Write tests for tool router
|
||||
- [ ] 9.5 Write tests for event system
|
||||
- [x] 9.6 Run `ruff check .` and `ruff format .`
|
||||
- [ ] 9.7 Run `uvx ty check` for type validation
|
||||
- [x] 9.8 Run `cargo check` and `cargo fmt` for Rust code
|
||||
Reference in New Issue
Block a user