ForcePilot/backend/package/yuxi/channels/contract/dtos/message_ops.py

266 lines
9.6 KiB
Python
Raw Normal View History

"""消息操作 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: 删除卡片
RECALL: 撤回消息24 小时内多渠道通用
"""
SEND = "send"
EDIT = "edit"
REACT = "react"
UNREACT = "unreact"
PIN = "pin"
UNPIN = "unpin"
UPDATE_CARD = "update_card"
DELETE_CARD = "delete_card"
RECALL = "recall"
@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 = "渠道操作_"