"""出站 DTO。 定义出站管道的富消息与最终输出值对象,包括富消息按钮、富消息选项、 富消息、富消息字段、出站负载、格式化消息、可信消息与最终消息。 所有 DTO 均为 ``dataclass(frozen=True)``,仅依赖标准库与契约层内部 类型,用于出站消息的格式化、可信注入与最终投递。集合字段使用 tuple 以保证 frozen dataclass 的不可变语义。 """ from __future__ import annotations from dataclasses import dataclass from typing import Any from yuxi.channels.contract.dtos.common import Attachment, MessageFormat from yuxi.channels.contract.errors import ValidationError @dataclass(frozen=True) class RichMessageButton: """富消息按钮。 描述富消息中的可点击按钮,由适配器按渠道协议渲染为对应交互元素。 字段: label: 按钮显示文本。 action: 按钮动作标识。 value: 按钮动作携带的值(可选)。 """ label: str action: str value: str | None = None @dataclass(frozen=True) class RichMessageOption: """富消息选项。 描述富消息中的可选项,用于下拉 / 列表类交互组件。 字段: label: 选项显示文本。 value: 选项值。 """ label: str value: str @dataclass(frozen=True) class RichMessageSelect: """富消息下拉选择器组件。 字段: name: 表单字段名。 label: 显示标签。 options: 可选项列表。 placeholder: 占位文本,可选。 default_value: 默认值,可选。 """ name: str label: str options: tuple[RichMessageOption, ...] placeholder: str | None = None default_value: str | None = None @dataclass(frozen=True) class RichMessageDatePicker: """富消息日期选择器组件。 字段: name: 表单字段名。 label: 显示标签。 placeholder: 占位文本,可选。 default_value: 默认日期(ISO 格式),可选。 min_date: 最小可选日期(ISO 格式),可选。 max_date: 最大可选日期(ISO 格式),可选。 """ name: str label: str placeholder: str | None = None default_value: str | None = None min_date: str | None = None max_date: str | None = None @dataclass(frozen=True) class RichMessageCheckbox: """富消息复选框组件。 字段: name: 表单字段名。 label: 显示标签。 options: 可选项列表。 default_values: 默认选中值列表。 """ name: str label: str options: tuple[RichMessageOption, ...] default_values: tuple[str, ...] = () @dataclass(frozen=True) class RichMessageInput: """富消息输入框组件。 字段: name: 表单字段名。 label: 显示标签。 placeholder: 占位文本,可选。 default_value: 默认值,可选。 max_length: 最大输入长度,可选。 input_type: 输入类型(text/number/email 等),默认 text。 """ name: str label: str placeholder: str | None = None default_value: str | None = None max_length: int | None = None input_type: str = "text" @dataclass(frozen=True) class RichMessage: """富消息。 描述结构化的富消息内容,包括标题、正文、描述、图片、视频与交互元素 (按钮 / 选项),由适配器按渠道协议渲染。集合字段使用 tuple 以 保证不可变。 字段: text: 正文文本。 title: 标题(可选)。 description: 描述(可选)。 image_url: 图片 URL(可选,向后兼容)。 video_url: 视频 URL(可选,FR-50)。 attachments: 多附件列表(承载多图、文件等,FR-50)。 buttons: 按钮列表(默认空 tuple)。 options: 选项列表(默认空 tuple)。 metadata: 渠道侧元数据(可选)。 selects: 下拉选择器组件列表。 date_pickers: 日期选择器组件列表。 checkboxes: 复选框组件列表。 inputs: 输入框组件列表。 """ text: str title: str | None = None description: str | None = None image_url: str | None = None video_url: str | None = None attachments: tuple[Attachment, ...] = () buttons: tuple[RichMessageButton, ...] = () options: tuple[RichMessageOption, ...] = () metadata: dict[str, Any] | None = None selects: tuple[RichMessageSelect, ...] = () date_pickers: tuple[RichMessageDatePicker, ...] = () checkboxes: tuple[RichMessageCheckbox, ...] = () inputs: tuple[RichMessageInput, ...] = () @dataclass(frozen=True) class RichMessageFields: """富消息字段。 包装富消息与其回退格式,用于出站负载携带富消息渲染信息,当渠道 不支持富消息时按回退格式渲染。 字段: rich_message: 富消息内容。 fallback_format: 回退格式(默认 MARKDOWN)。 """ rich_message: RichMessage fallback_format: MessageFormat = MessageFormat.MARKDOWN @dataclass(frozen=True) class OutboundPayload: """出站负载。 描述出站管道的统一负载,关联 Agent 运行 ID,携带流式分块、可选的 富消息字段与附件列表,用于驱动出站消息的格式化与投递。 字段: agent_run_id: 关联的 Agent 运行 ID。 stream_chunks: 流式分块列表(默认空 tuple)。 rich_message_fields: 富消息字段(可选)。 attachments: 附件列表(默认空 tuple,FR-51)。 """ agent_run_id: str | None = None stream_chunks: tuple[str, ...] = () rich_message_fields: RichMessageFields | None = None attachments: tuple[Attachment, ...] = () @dataclass(frozen=True) class FormattedMessage: """格式化消息。 描述经格式化后的消息内容,携带文本、格式、可选的富消息与附件列表, 用于适配器按渠道协议渲染最终消息。 字段: content: 消息文本内容。 format: 消息格式(默认 TEXT)。 rich_message: 渲染后的渠道原生富消息对象(可选,由 RichMessageAdapter.renderRichMessage 产生,类型由具体适配器 决定)。 attachments: 附件列表(默认空 tuple,FR-51)。 """ content: str format: MessageFormat = MessageFormat.TEXT rich_message: Any = None attachments: tuple[Attachment, ...] = () @dataclass(frozen=True) class TrustedMessage: """可信消息。 描述经服务端强制注入发送者身份与所有者标记的消息,用于可信注入 边界(FR-25),确保渠道侧无法伪造发送者。 字段: content: 消息文本内容。 sender_id: 发送者 ID(服务端强制注入)。 is_owner: 是否为会话所有者(服务端强制注入,默认 False)。 rich_message: 渲染后的渠道原生富消息对象(可选,从 FormattedMessage 透传,FR-08)。 """ content: str sender_id: str is_owner: bool = False rich_message: Any = None def __post_init__(self) -> None: """校验必填字段非空。 ``content`` 与 ``sender_id`` 必须非空,在构造时即抛出 ``ValidationError``,避免空消息内容或空发送者身份传播到渠道 适配器(INV-8)。 """ if not self.content: raise ValidationError("content", "must not be empty") if not self.sender_id: raise ValidationError("sender_id", "must not be empty") @dataclass(frozen=True) class FinalMessage: """最终消息。 描述出站管道最终投递至渠道适配器的消息,携带文本、发送者 ID 与 可选前缀,前缀用于所有者标记等场景。 字段: content: 消息文本内容。 sender_id: 发送者 ID。 prefix: 前缀(可选,如所有者标记)。 rich_message: 渲染后的渠道原生富消息对象(可选,从 TrustedMessage 透传,FR-08)。 """ content: str sender_id: str prefix: str | None = None rich_message: Any = None def __post_init__(self) -> None: """校验必填字段非空。 ``content`` 与 ``sender_id`` 必须非空,在构造时即抛出 ``ValidationError``,避免空消息内容或空发送者身份投递到渠道 (INV-8)。 """ if not self.content: raise ValidationError("content", "must not be empty") if not self.sender_id: raise ValidationError("sender_id", "must not be empty") @dataclass(frozen=True) class BatchSendItem: """批量发送单条结果。 描述 ``OutboundAdapter.batchSendMessages`` 单条投递结果,与 ``MultiPartReceipt`` 区别:``MultiPartReceipt`` 描述**同一条**逻辑 消息拆分为多个分片的回执,``BatchSendItem`` 描述**同一负载**投递到 多个 peer 的逐条结果。 字段: peer_id: 接收目标 ID。 channel_msg_id: 渠道侧消息 ID(成功时非空,失败时为空字符串)。 success: 是否投递成功。 error: 失败时的错误信息(可选,成功时为 ``None``)。 """ peer_id: str channel_msg_id: str success: bool error: str | None = None @dataclass(frozen=True) class BatchSendResult: """批量发送结果。 描述 ``OutboundAdapter.batchSendMessages`` 的聚合结果,包含逐条结果 与汇总统计。集合字段使用 tuple 以保证 frozen dataclass 的不可变语义。 字段: total: 总投递目标数。 succeeded: 成功的目标 ID 元组。 failed: 失败条目元组(含目标 ID 与错误信息)。 items: 逐条结果元组(含成功与失败)。 """ total: int succeeded: tuple[str, ...] failed: tuple[BatchSendItem, ...] items: tuple[BatchSendItem, ...]