feat: 添加协议模块及消息定义,详细说明新版协议与旧版的差异

This commit is contained in:
whatevertogo
2026-03-13 02:37:13 +08:00
parent bfe5b21f6b
commit 3c158760f9
4 changed files with 523 additions and 0 deletions

View File

@@ -1,3 +1,58 @@
"""协议模块。
定义 AstrBot SDK 的消息协议和描述符,用于插件与核心之间的通信。
所有消息均使用 Pydantic 定义,确保类型安全和序列化一致性。
架构说明:
旧版:
- 使用标准 JSON-RPC 2.0 协议
- 消息类型较少: Request, SuccessResponse, ErrorResponse
- 特定请求类型绑定了 AstrMessageEventModel 事件模型
- 使用 dataclass 和 pydantic 混合定义
新版:
- 全新的自描述协议,使用 `type` 字段区分消息类型
- 更丰富的消息类型: Initialize, Result, Invoke, Event, Cancel
- 强大的描述符系统: HandlerDescriptor, CapabilityDescriptor
- 多种触发器类型: Command, Message, Event, Schedule
- 纯 Pydantic 定义,支持严格验证
- 提供 LegacyAdapter 实现新旧协议互操作
协议消息流程:
1. Initialize: 握手建立连接,交换能力和处理器信息
2. Invoke: 调用远程能力
3. Event: 流式事件通知 (started/delta/completed/failed)
4. Result: 调用结果返回
5. Cancel: 取消正在进行的调用
与旧版对比:
旧版 JSON-RPC 消息:
{
"jsonrpc": "2.0",
"id": "xxx",
"method": "call_handler",
"params": {"handler_full_name": "...", "event": {...}}
}
新版协议消息:
{
"type": "invoke",
"id": "xxx",
"capability": "handler.invoke",
"input": {"handler_id": "...", "event": {...}}
}
TODO: (功能完善):
- 添加消息签名验证支持,确保消息来源可信
- 添加消息压缩支持,减少大数据传输开销
- 添加批量消息支持 (BatchMessage),提高传输效率
- 添加消息追踪 ID (trace_id) 支持,便于日志关联
- CapabilityDescriptor 缺少 rate_limit 限流配置
- HandlerDescriptor 缺少 timeout 超时配置
- 缺少心跳消息 (HeartbeatMessage) 支持
- 缺少健康检查消息 (HealthCheckMessage) 支持
"""
from .descriptors import CapabilityDescriptor, HandlerDescriptor, Permissions
from .messages import (
CancelMessage,

View File

@@ -1,3 +1,41 @@
"""描述符模块。
定义处理器和能力的描述符,用于声明式注册和发现。
描述符类型概览:
HandlerDescriptor: 处理器描述符,描述一个事件处理函数
CapabilityDescriptor: 能力描述符,描述一个可调用的远程能力
Permissions: 权限配置,控制处理器的访问权限
Trigger: 触发器联合类型,支持多种触发方式
触发器类型:
CommandTrigger: 命令触发器,响应特定命令(如 /help
MessageTrigger: 消息触发器,响应匹配正则或关键词的消息
EventTrigger: 事件触发器,响应特定类型的事件
ScheduleTrigger: 定时触发器,按 cron 或间隔时间执行
与旧版对比:
旧版:
- 处理器元信息分散在 handshake 响应中
- 使用 event_type 整数区分事件类型
- 缺少声明式的触发器定义
- 配置通过 extras_configs 字典传递
新版:
- 使用 HandlerDescriptor 统一描述处理器
- 使用字符串 event_type更语义化
- 支持多种触发器类型,声明式定义
- 使用 Pydantic 模型,类型安全
TODO:
- HandlerDescriptor 缺少 timeout 超时配置
- HandlerDescriptor 缺少 retry 重试配置
- CapabilityDescriptor 缺少 rate_limit 限流配置
- ScheduleTrigger 缺错时错过执行的处理策略
- 缺少 HandlerGroupDescriptor 处理器组描述符
- 缺少 DependencyDescriptor 依赖声明
"""
from __future__ import annotations
from typing import Annotated, Any, Literal
@@ -10,11 +48,36 @@ class _DescriptorBase(BaseModel):
class Permissions(_DescriptorBase):
"""权限配置,控制处理器的访问权限。
与旧版对比:
旧版: 通过 extras_configs 字典配置
{"require_admin": true, "level": 1}
新版: 使用 Permissions 模型,类型安全
Attributes:
require_admin: 是否需要管理员权限
level: 权限等级,数值越高权限越大
"""
require_admin: bool = False
level: int = 0
class CommandTrigger(_DescriptorBase):
"""命令触发器,响应特定命令。
与旧版对比:
旧版: 使用 @command_handler("help") 装饰器注册
新版: 使用 CommandTrigger 声明式定义,支持别名
Attributes:
type: 触发器类型,固定为 "command"
command: 命令名称(不含前缀,如 "help"
aliases: 命令别名列表
description: 命令描述,用于帮助文档
"""
type: Literal["command"] = "command"
command: str
aliases: list[str] = Field(default_factory=list)
@@ -22,6 +85,19 @@ class CommandTrigger(_DescriptorBase):
class MessageTrigger(_DescriptorBase):
"""消息触发器,响应匹配正则或关键词的消息。
与旧版对比:
旧版: 使用 @regex_handler(r"pattern") 或 @message_handler 装饰器
新版: 使用 MessageTrigger 声明式定义,支持正则、关键词和平台过滤
Attributes:
type: 触发器类型,固定为 "message"
regex: 正则表达式模式,匹配消息文本
keywords: 关键词列表,消息包含任一关键词即触发
platforms: 目标平台列表,为空表示所有平台
"""
type: Literal["message"] = "message"
regex: str | None = None
keywords: list[str] = Field(default_factory=list)
@@ -29,11 +105,37 @@ class MessageTrigger(_DescriptorBase):
class EventTrigger(_DescriptorBase):
"""事件触发器,响应特定类型的事件。
与旧版对比:
旧版: 使用整数 event_type如 3 表示消息事件
新版: 使用字符串 event_type"message""3",更灵活
Attributes:
type: 触发器类型,固定为 "event"
event_type: 事件类型,字符串形式(如 "message""notice"
"""
type: Literal["event"] = "event"
event_type: str
class ScheduleTrigger(_DescriptorBase):
"""定时触发器,按 cron 表达式或固定间隔执行。
与旧版对比:
旧版: 使用 @scheduled("0 * * * *") 装饰器
新版: 使用 ScheduleTrigger 声明式定义
Attributes:
type: 触发器类型,固定为 "schedule"
cron: cron 表达式(如 "0 9 * * *" 表示每天 9 点)
interval_seconds: 执行间隔(秒)
Note:
cron 和 interval_seconds 必须且只能有一个非空。
"""
type: Literal["schedule"] = "schedule"
cron: str | None = None
interval_seconds: int | None = None
@@ -51,9 +153,38 @@ Trigger = Annotated[
CommandTrigger | MessageTrigger | EventTrigger | ScheduleTrigger,
Field(discriminator="type"),
]
"""触发器联合类型,使用 type 字段作为判别器自动解析具体类型。"""
class HandlerDescriptor(_DescriptorBase):
"""处理器描述符,描述一个事件处理函数的元信息。
与旧版对比:
旧版 handshake 响应中的处理器信息:
{
"event_type": 3,
"handler_full_name": "plugin.handler",
"handler_name": "handler",
"handler_module_path": "plugin",
"desc": "描述",
"extras_configs": {"priority": 0, "require_admin": false}
}
新版 HandlerDescriptor:
{
"id": "plugin.handler",
"trigger": {"type": "event", "event_type": "message"},
"priority": 0,
"permissions": {"require_admin": false, "level": 0}
}
Attributes:
id: 处理器唯一标识,通常是 "模块.函数名" 格式
trigger: 触发器配置,决定何时执行该处理器
priority: 优先级,数值越大越先执行
permissions: 权限配置,控制谁可以触发该处理器
"""
id: str
trigger: Trigger
priority: int = 0
@@ -61,6 +192,25 @@ class HandlerDescriptor(_DescriptorBase):
class CapabilityDescriptor(_DescriptorBase):
"""能力描述符,描述一个可调用的远程能力。
与旧版对比:
旧版: 无独立的能力描述,通过 method 名称隐式定义
新版: 使用 CapabilityDescriptor 显式声明能力,支持 JSON Schema 验证
能力命名规范:
- 使用 "namespace.action" 格式,如 "llm.chat""db.set"
- 内置能力以 "internal." 开头,如 "internal.legacy.call_context_function"
Attributes:
name: 能力名称,格式为 "namespace.action"
description: 能力描述,用于文档和调试
input_schema: 输入参数的 JSON Schema用于验证
output_schema: 输出结果的 JSON Schema用于验证
supports_stream: 是否支持流式响应
cancelable: 是否支持取消
"""
name: str
description: str
input_schema: dict[str, Any] | None = None

View File

@@ -1,3 +1,37 @@
"""旧版协议适配器模块。
提供旧版 JSON-RPC 协议与新版协议之间的双向转换。
支持旧版插件与新版核心的互操作。
主要功能:
- 将旧版 JSON-RPC 请求转换为新版 InvokeMessage
- 将旧版 JSON-RPC 响应转换为新版 ResultMessage
- 将旧版 handshake 转换为新版 InitializeMessage
- 将新版消息转换回旧版格式(用于与旧版核心通信)
转换映射表:
旧版 method -> 新版 capability
------------------------------------------------
handshake -> InitializeMessage
call_handler -> handler.invoke
call_context_function -> internal.legacy.call_context_function
handler_stream_start -> EventMessage(phase="started")
handler_stream_update -> EventMessage(phase="delta")
handler_stream_end -> EventMessage(phase="completed"/"failed")
cancel -> CancelMessage
注意事项:
- 旧版 handshake 的 metadata 信息可能丢失部分字段
- 新版触发器的详细信息在转换时可能丢失
- 使用 LEGACY_HANDSHAKE_METADATA_KEY 保留原始握手数据
TODO:
- 添加旧版消息版本检测和兼容性警告
- 添加消息转换日志记录,便于调试
- 支持自定义转换规则扩展
- 添加转换性能监控
"""
from __future__ import annotations
import json
@@ -17,10 +51,19 @@ from .messages import (
)
LEGACY_JSONRPC_VERSION = "2.0"
"""旧版 JSON-RPC 协议版本。"""
LEGACY_CONTEXT_CAPABILITY = "internal.legacy.call_context_function"
"""旧版上下文函数调用的能力名称。"""
LEGACY_HANDSHAKE_METADATA_KEY = "legacy_handshake_payload"
"""在 InitializeMessage.metadata 中存储原始握手数据的键。"""
LEGACY_PLUGIN_KEYS_METADATA_KEY = "legacy_plugin_keys"
"""在 InitializeMessage.metadata 中存储原始插件键列表的键。"""
LEGACY_ADAPTER_MESSAGE_EVENT = 3
"""默认的事件类型,用于无法识别的旧版处理器。"""
class _LegacyMessageBase(BaseModel):
@@ -28,12 +71,29 @@ class _LegacyMessageBase(BaseModel):
class LegacyErrorData(_LegacyMessageBase):
"""旧版 JSON-RPC 错误数据。
Attributes:
code: 错误码,整数类型(旧版规范)
message: 错误消息
data: 附加错误数据
"""
code: int = -32000
message: str
data: Any | None = None
class LegacyRequest(_LegacyMessageBase):
"""旧版 JSON-RPC 请求。
Attributes:
jsonrpc: 协议版本,固定为 "2.0"
id: 请求 ID可选
method: 方法名称
params: 参数字典
"""
jsonrpc: Literal["2.0"] = LEGACY_JSONRPC_VERSION
id: str | None = None
method: str
@@ -46,20 +106,56 @@ class _LegacyResponse(_LegacyMessageBase):
class LegacySuccessResponse(_LegacyResponse):
"""旧版 JSON-RPC 成功响应。
Attributes:
result: 返回结果
"""
result: Any = Field(default_factory=dict)
class LegacyErrorResponse(_LegacyResponse):
"""旧版 JSON-RPC 错误响应。
Attributes:
error: 错误数据
"""
error: LegacyErrorData
LegacyMessage = LegacyRequest | LegacySuccessResponse | LegacyErrorResponse
"""旧版 JSON-RPC 消息联合类型。"""
LegacyToV4Message = (
InitializeMessage | InvokeMessage | ResultMessage | EventMessage | CancelMessage
)
"""旧版消息转换后的新版消息类型。"""
class LegacyAdapter:
"""旧版协议适配器,提供新旧协议之间的双向转换。
使用场景:
1. 旧版插件连接新版核心:将旧版 JSON-RPC 转换为新版协议
2. 新版插件连接旧版核心:将新版协议转换为旧版 JSON-RPC
3. 测试和迁移:验证协议转换的正确性
转换规则:
- handshake <-> InitializeMessage
- call_handler <-> InvokeMessage(capability="handler.invoke")
- call_context_function <-> InvokeMessage(capability="internal.legacy...")
- handler_stream_* <-> EventMessage
- cancel <-> CancelMessage
Attributes:
protocol_version: 新版协议版本号
legacy_peer_name: 默认的对等节点名称
legacy_peer_role: 默认的对等节点角色
legacy_peer_version: 默认的对等节点版本
"""
def __init__(
self,
*,

View File

@@ -1,3 +1,49 @@
"""协议消息定义模块。
定义 AstrBot SDK 的核心消息类型,所有消息均继承自 Pydantic BaseModel。
消息类型概览:
InitializeMessage: 握手初始化消息,包含 Peer 信息和处理器列表
ResultMessage: 调用结果消息,包含成功/失败状态和输出数据
InvokeMessage: 能力调用消息,指定目标能力和输入参数
EventMessage: 流式事件消息,用于流式调用的状态通知
CancelMessage: 取消消息,用于取消正在进行的调用
消息生命周期:
握手阶段:
Plugin -> Core: InitializeMessage (注册处理器)
Core -> Plugin: ResultMessage (确认或拒绝)
调用阶段:
Plugin -> Core: InvokeMessage (调用能力)
Core -> Plugin: ResultMessage (返回结果)
或者 (流式):
Core -> Plugin: EventMessage (started -> delta* -> completed/failed)
取消阶段:
Plugin -> Core: CancelMessage (取消调用)
与旧版对比:
旧版 JSON-RPC:
- 使用 method 字段区分操作类型
- 使用 jsonrpc: "2.0" 标识协议版本
- 错误码为整数 (如 -32000)
- 无专门的取消消息类型
新版协议:
- 使用 type 字段区分消息类型
- 使用 protocol_version 字段标识版本
- 错误码为字符串 (如 "internal_error")
- 有专门的 CancelMessage 取消消息
TODO:
- 添加消息过期时间 (expires_at) 支持
- 添加消息优先级 (priority) 支持
- 添加消息重试计数 (retry_count) 支持
- ErrorPayload 缺少 stack_trace 字段(调试用)
- InitializeMessage 缺少 authentication 认证字段
"""
from __future__ import annotations
import json
@@ -13,6 +59,19 @@ class _MessageBase(BaseModel):
class ErrorPayload(_MessageBase):
"""错误载荷,用于 ResultMessage 和 EventMessage 中传递错误信息。
与旧版 JSON-RPC 错误对比:
旧版: code 为整数,如 -32000
新版: code 为字符串,如 "internal_error"
Attributes:
code: 错误码,字符串类型,便于语义化错误分类
message: 错误消息,人类可读的错误描述
hint: 错误提示,可选的解决方案或建议
retryable: 是否可重试,标识该错误是否可通过重试解决
"""
code: str
message: str
hint: str = ""
@@ -20,12 +79,55 @@ class ErrorPayload(_MessageBase):
class PeerInfo(_MessageBase):
"""对等节点信息,标识消息发送方的身份。
与旧版对比:
旧版: 通过 handshake params 中的 plugin_name 隐式传递
新版: 显式的 PeerInfo 结构,支持 plugin 和 core 两种角色
Attributes:
name: 节点名称,通常是插件 ID 或核心标识
role: 节点角色,"plugin""core"
version: 节点版本号,可选
"""
name: str
role: Literal["plugin", "core"]
version: str | None = None
class InitializeMessage(_MessageBase):
"""初始化消息,用于建立连接时交换信息。
与旧版 JSON-RPC handshake 对比:
旧版:
{
"jsonrpc": "2.0",
"id": "xxx",
"method": "handshake",
"params": {}
}
响应包含插件元信息和处理器列表
新版:
{
"type": "initialize",
"id": "xxx",
"protocol_version": "1.0",
"peer": {"name": "...", "role": "plugin", "version": "..."},
"handlers": [...],
"metadata": {...}
}
Attributes:
type: 消息类型,固定为 "initialize"
id: 消息 ID用于关联响应
protocol_version: 协议版本号
peer: 发送方节点信息
handlers: 注册的处理器描述符列表
metadata: 扩展元数据,可存储插件配置等信息
"""
type: Literal["initialize"] = "initialize"
id: str
protocol_version: str
@@ -35,12 +137,46 @@ class InitializeMessage(_MessageBase):
class InitializeOutput(_MessageBase):
"""初始化输出,作为 InitializeMessage 的响应数据。
与旧版对比:
旧版: handshake 响应中包含完整的插件信息
新版: 仅返回对等方信息和能力列表,更简洁
Attributes:
peer: 接收方(核心)节点信息
capabilities: 核心提供的能力描述符列表
metadata: 扩展元数据
"""
peer: PeerInfo
capabilities: list[CapabilityDescriptor] = Field(default_factory=list)
metadata: dict[str, Any] = Field(default_factory=dict)
class ResultMessage(_MessageBase):
"""结果消息,用于返回能力调用的结果。
与旧版 JSON-RPC 响应对比:
旧版成功响应:
{"jsonrpc": "2.0", "id": "xxx", "result": {...}}
旧版错误响应:
{"jsonrpc": "2.0", "id": "xxx", "error": {"code": -32000, "message": "..."}}
新版成功结果:
{"type": "result", "id": "xxx", "success": true, "output": {...}}
新版失败结果:
{"type": "result", "id": "xxx", "success": false, "error": {...}}
Attributes:
type: 消息类型,固定为 "result"
id: 关联的请求 ID
kind: 结果类型,可选,如 "initialize_result" 标识初始化结果
success: 是否成功
output: 成功时的输出数据
error: 失败时的错误信息
"""
type: Literal["result"] = "result"
id: str
kind: str | None = None
@@ -50,6 +186,34 @@ class ResultMessage(_MessageBase):
class InvokeMessage(_MessageBase):
"""调用消息,用于请求执行远程能力。
与旧版 JSON-RPC 请求对比:
旧版:
{
"jsonrpc": "2.0",
"id": "xxx",
"method": "call_handler",
"params": {"handler_full_name": "...", "event": {...}}
}
新版:
{
"type": "invoke",
"id": "xxx",
"capability": "handler.invoke",
"input": {"handler_id": "...", "event": {...}},
"stream": false
}
Attributes:
type: 消息类型,固定为 "invoke"
id: 请求 ID用于关联响应
capability: 目标能力名称,格式为 "namespace.action"
input: 调用输入参数
stream: 是否期望流式响应,若为 True 将收到 EventMessage 序列
"""
type: Literal["invoke"] = "invoke"
id: str
capability: str
@@ -58,6 +222,32 @@ class InvokeMessage(_MessageBase):
class EventMessage(_MessageBase):
"""事件消息,用于流式调用的状态通知。
流式调用生命周期:
1. started: 调用开始,所有字段为空
2. delta: 数据增量更新,包含 data 字段
3. completed: 调用完成,包含 output 字段
4. failed: 调用失败,包含 error 字段
与旧版 JSON-RPC 通知对比:
旧版使用独立的 method 区分:
- handler_stream_start
- handler_stream_update
- handler_stream_end
新版使用统一的 EventMessage通过 phase 字段区分:
{"type": "event", "id": "xxx", "phase": "delta", "data": {...}}
Attributes:
type: 消息类型,固定为 "event"
id: 关联的请求 ID
phase: 事件阶段started/delta/completed/failed
data: 增量数据,仅 delta 阶段有效
output: 最终输出,仅 completed 阶段有效
error: 错误信息,仅 failed 阶段有效
"""
type: Literal["event"] = "event"
id: str
phase: Literal["started", "delta", "completed", "failed"]
@@ -97,6 +287,18 @@ class EventMessage(_MessageBase):
class CancelMessage(_MessageBase):
"""取消消息,用于取消正在进行的调用。
与旧版对比:
旧版: 使用 {"jsonrpc": "2.0", "method": "cancel", "params": {"reason": "..."}}
新版: 专门的 CancelMessage 类型,语义更明确
Attributes:
type: 消息类型,固定为 "cancel"
id: 要取消的请求 ID
reason: 取消原因,默认为 "user_cancelled"
"""
type: Literal["cancel"] = "cancel"
id: str
reason: str = "user_cancelled"
@@ -105,9 +307,29 @@ class CancelMessage(_MessageBase):
ProtocolMessage = (
InitializeMessage | ResultMessage | InvokeMessage | EventMessage | CancelMessage
)
"""协议消息联合类型,所有有效消息类型的联合。"""
def parse_message(payload: str | bytes | dict[str, Any]) -> ProtocolMessage:
"""解析协议消息。
从原始载荷(字符串、字节或字典)解析为对应的 ProtocolMessage 类型。
根据 "type" 字段自动识别消息类型并验证。
Args:
payload: 原始消息载荷,支持 JSON 字符串、字节或字典
Returns:
解析后的协议消息对象
Raises:
ValueError: 未知的消息类型
Example:
>>> msg = parse_message('{"type": "invoke", "id": "1", "capability": "test"}')
>>> isinstance(msg, InvokeMessage)
True
"""
if isinstance(payload, bytes):
payload = payload.decode("utf-8")
if isinstance(payload, str):