本次提交包含多项代码优化与规范修正: 1. 文档与注释优化:修正注释术语、补充注解与FR编号 2. 代码格式调整:统一空格、换行与缩进规范 3. 类型与接口完善:补充__all__导出、修正返回类型注解 4. 错误处理增强:新增领域错误类与校验逻辑 5. 依赖与导入调整:修复路径引用、统一时区导入 6. 协议与契约更新:完善接口文档与一致性注解
264 lines
9.5 KiB
Python
264 lines
9.5 KiB
Python
"""消息操作 DTO。
|
||
|
||
定义消息操作的枚举与不可变值对象,包括消息操作类型、消息操作能力、
|
||
消息操作定义与消息操作结果。所有 DTO 均为 ``dataclass(frozen=True)``,
|
||
仅依赖标准库与契约层内部类型,用于 FR-12 消息操作(编辑 / 撤回 / 反应 /
|
||
置顶 / 卡片更新等)的能力声明、操作定义、工具转换与结果回传。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
from enum import StrEnum
|
||
from typing import Any
|
||
|
||
from yuxi.channels.contract.dtos.channel import ChannelType
|
||
from yuxi.channels.contract.dtos.common import BatchOperationFailure, Operator
|
||
from yuxi.channels.contract.dtos.tools import ToolParameter
|
||
from yuxi.channels.contract.errors import ValidationError
|
||
|
||
|
||
class MessageOperation(StrEnum):
|
||
"""消息操作类型。
|
||
|
||
标识渠道侧支持的消息操作,用于消息操作能力声明与结果回传。继承
|
||
``str, Enum`` 以支持 JSON 序列化与字符串比较。
|
||
|
||
取值:
|
||
SEND: 发送消息。
|
||
EDIT: 编辑消息。
|
||
REACT: 添加反应。
|
||
UNREACT: 移除反应。
|
||
PIN: 置顶消息。
|
||
UNPIN: 取消置顶。
|
||
UPDATE_CARD: 更新卡片。
|
||
DELETE_CARD: 删除卡片。
|
||
"""
|
||
|
||
SEND = "send"
|
||
EDIT = "edit"
|
||
REACT = "react"
|
||
UNREACT = "unreact"
|
||
PIN = "pin"
|
||
UNPIN = "unpin"
|
||
UPDATE_CARD = "update_card"
|
||
DELETE_CARD = "delete_card"
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MessageOperationCapability:
|
||
"""消息操作能力。
|
||
|
||
描述渠道适配器支持的消息操作能力,包括是否支持反应、置顶与卡片更新,
|
||
用于能力声明与降级决策。
|
||
|
||
字段:
|
||
supports_reaction: 是否支持反应(默认 False)。
|
||
supports_pin: 是否支持置顶(默认 False)。
|
||
supports_card_update: 是否支持卡片更新(默认 False)。
|
||
has_side_effects: 操作是否产生不可逆副作用(编辑/撤回/置顶等修改
|
||
渠道侧状态的操作为 True,查询类操作为 False,默认 False)。
|
||
用于工具调用前置可信注入阶段决定是否强制校验可信发送者
|
||
(FR-25)。
|
||
requires_trusted_sender: 操作是否要求可信发送者声明。为 True 时,
|
||
非渠道 Agent Run(无可信发送者)**不得** 执行该操作,由
|
||
pre-tool-call 可信注入阶段强制校验(FR-25)。默认 True,对齐
|
||
PRD §FR-25 业务规则第 4 条"默认所有消息操作都需要可信发送者"。
|
||
"""
|
||
|
||
supports_reaction: bool = False
|
||
supports_pin: bool = False
|
||
supports_card_update: bool = False
|
||
has_side_effects: bool = False
|
||
requires_trusted_sender: bool = True
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MessageOperationDefinition:
|
||
"""消息操作定义。
|
||
|
||
描述单个消息操作的完整元信息,包括操作类型、工具名(不含统一前缀)、
|
||
描述、参数列表与操作粒度能力声明(副作用、可信发送者要求),由插件
|
||
的 ``getMessageOperations`` 返回,供消息操作执行器与工具转换机制
|
||
消费(FR-12)。
|
||
|
||
工具转换规则(PRD §FR-12 业务规则第 3 条):
|
||
- 定义中的 ``tool_name`` 在合并到 ``ChannelTool`` 时附加统一前缀
|
||
``MESSAGE_OPERATION_TOOL_PREFIX``(默认 ``渠道操作_``)。
|
||
- Agent 调用工具时通过工具名前缀路由到消息操作执行器,而非插件
|
||
的 ``executeTool``。
|
||
|
||
字段:
|
||
operation: 消息操作类型。
|
||
tool_name: 工具名(不含统一前缀,如 ``表情反应``)。
|
||
description: 工具描述,供 Agent 理解操作用途。
|
||
parameters: 参数列表(默认空 tuple)。
|
||
has_side_effects: 操作是否产生不可逆副作用(默认 False)。用于
|
||
工具调用前置可信注入阶段决定是否强制校验可信发送者(FR-25)。
|
||
requires_trusted_sender: 操作是否要求可信发送者声明(默认 True)。
|
||
为 True 时,非渠道 Agent Run(无可信发送者)**不得** 执行该
|
||
操作,由 pre-tool-call 可信注入阶段强制校验(FR-25)。默认
|
||
True 对齐 PRD §FR-25 业务规则第 4 条"默认所有消息操作都需要
|
||
可信发送者"。
|
||
"""
|
||
|
||
operation: MessageOperation
|
||
tool_name: str
|
||
description: str
|
||
parameters: tuple[ToolParameter, ...] = ()
|
||
has_side_effects: bool = False
|
||
requires_trusted_sender: bool = True
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MessageOperationResult:
|
||
"""消息操作结果(FR-12)。
|
||
|
||
描述一次消息操作的执行结果,包括操作类型、是否成功、结果数据与错误
|
||
信息,用于消息操作结果回传与审计。
|
||
|
||
字段:
|
||
operation: 消息操作类型。
|
||
success: 是否成功。
|
||
result_data: 结果数据(可选)。
|
||
error: 错误信息(可选)。
|
||
"""
|
||
|
||
operation: MessageOperation
|
||
success: bool
|
||
result_data: dict[str, Any] | None = None
|
||
error: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ResendMessageCmd:
|
||
"""消息重发命令(MSG-RESEND)。
|
||
|
||
由 ``MessageManagementPort.resendMessage`` 引用,基于原消息重新构造并发送
|
||
一条新消息,需记录操作人以满足审计要求(FR-12)。
|
||
|
||
字段:
|
||
channel_type: 渠道类型。
|
||
message_id: 原消息 ID。
|
||
operator: 操作人(审计用)。
|
||
target: 重发目标(可选,缺省时复用原消息目标)。
|
||
content_overrides: 内容覆盖(可选,按字段覆盖原消息内容)。
|
||
reason: 重发原因(审计用,可选)。
|
||
"""
|
||
|
||
channel_type: ChannelType
|
||
message_id: str
|
||
operator: Operator
|
||
target: str | None = None
|
||
content_overrides: dict[str, Any] | None = None
|
||
reason: str | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验必填字段非空(MSG-RESEND)。
|
||
|
||
``message_id`` 必须非空,在构造时即抛出 ``ValidationError``,
|
||
adapter 不再做该校验(INV-8)。
|
||
"""
|
||
if not self.message_id:
|
||
raise ValidationError("message_id", "must not be empty")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ResendMessageResult:
|
||
"""消息重发结果(MSG-RESEND)。
|
||
|
||
描述重发操作的新旧消息映射关系,``new_message_id`` 为新生成消息的 ID。
|
||
|
||
字段:
|
||
original_message_id: 原消息 ID。
|
||
new_message_id: 新消息 ID。
|
||
sent_at: 发送时间戳。
|
||
"""
|
||
|
||
original_message_id: str
|
||
new_message_id: str
|
||
sent_at: datetime
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BatchRecallCmd:
|
||
"""批量撤回命令(MSG-BATCH-RECALL)。
|
||
|
||
由 ``MessageManagementPort.batchRecallMessages`` 引用,逐条独立事务撤回
|
||
消息(模式 D),``message_ids`` 长度限制 1-500。
|
||
|
||
字段:
|
||
message_ids: 待撤回消息 ID 元组(必填,1-500)。
|
||
operator: 操作人(审计用)。
|
||
reason: 撤回原因(审计用,可选)。
|
||
"""
|
||
|
||
message_ids: tuple[str, ...]
|
||
operator: Operator
|
||
reason: str | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验必填字段非空与 ``message_ids`` 长度范围(MSG-BATCH-RECALL)。
|
||
|
||
``message_ids`` 必须非空且长度不超过 500(批量操作上限),
|
||
在构造时即抛出 ``ValidationError``,adapter 不再做该校验(INV-8)。
|
||
"""
|
||
if not self.message_ids:
|
||
raise ValidationError("message_ids", "must not be empty")
|
||
if len(self.message_ids) > 500:
|
||
raise ValidationError(
|
||
"message_ids",
|
||
"must not exceed 500 items",
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BatchRecallResult:
|
||
"""批量撤回结果(MSG-BATCH-RECALL)。
|
||
|
||
描述逐条独立事务撤回消息的执行结果,``failed`` 使用通用
|
||
``BatchOperationFailure``(``id`` 字段承载 message_id)。
|
||
``succeeded`` 为每条撤回结果 dict(含 message_id / channel_msg_id /
|
||
channel_recalled_at / im_side_recall_ack),供前端直接展示撤回详情。
|
||
|
||
字段:
|
||
total: 待撤回消息总数。
|
||
succeeded: 成功撤回的结果 dict 元组。
|
||
failed: 失败条目元组。
|
||
"""
|
||
|
||
total: int
|
||
succeeded: tuple[dict[str, Any], ...]
|
||
failed: tuple[BatchOperationFailure, ...]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ReadStatus:
|
||
"""消息已读状态。
|
||
|
||
描述 ``MessageOpsAdapter.getReadStatus`` 返回的消息已读回执信息,用于
|
||
业务方判断用户是否已读 AI 发送的重要通知。集合字段使用 tuple 以保证
|
||
frozen dataclass 的不可变语义。
|
||
|
||
字段:
|
||
channel_msg_id: 渠道侧消息 ID。
|
||
read_count: 已读用户数。
|
||
total_count: 目标用户总数。
|
||
read_user_ids: 已读用户 ID 元组(渠道支持返回明细时填充,
|
||
不支持时为空 tuple,调用方据 ``read_count`` 判定)。
|
||
read_at: 最近一次已读时间戳(可选,渠道不支持时为 ``None``)。
|
||
"""
|
||
|
||
channel_msg_id: str
|
||
read_count: int
|
||
total_count: int
|
||
read_user_ids: tuple[str, ...] = ()
|
||
read_at: datetime | None = None
|
||
|
||
|
||
# 消息操作工具统一前缀(PRD §FR-12 业务规则第 3 条)。
|
||
# 消息操作定义转换为渠道工具时附加本前缀,``ChannelToolExecutor`` 检测
|
||
# 前缀后路由到 ``MessageOperationExecutor`` 而非插件 ``executeTool``。
|
||
MESSAGE_OPERATION_TOOL_PREFIX = "渠道操作_"
|