"""提及 DTO。 定义消息提及的不可变值对象,包括提及类型、隐式提及类型、提及上下文、入站 提及策略、入站提及事实与提及决策。所有 DTO 均为 ``dataclass(frozen=True)``, 仅依赖标准库,用于提及检测与提及策略决策。集合字段使用 tuple 以保证 frozen dataclass 的不可变语义。 """ from __future__ import annotations from dataclasses import dataclass from enum import StrEnum from yuxi.channels.contract.errors import ValidationError class MentionType(StrEnum): """提及类型。 标识消息中提及机器人的方式,用于提及检测与策略决策。继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: EXPLICIT: 显式 @提及。 IMPLICIT: 隐式提及(FR-29)。 NONE: 无提及。 """ EXPLICIT = "explicit" IMPLICIT = "implicit" NONE = "none" class ImplicitMentionKind(StrEnum): """隐式提及类型。 标识隐式提及的具体形式,用于提及策略的精细化匹配。继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: REPLY_TO_BOT: 回复机器人。 QUOTED_BOT: 引用机器人。 BOT_THREAD_PARTICIPANT: 机器人话题参与者。 NATIVE: 原生 @ 提及。 """ REPLY_TO_BOT = "reply_to_bot" QUOTED_BOT = "quoted_bot" BOT_THREAD_PARTICIPANT = "bot_thread_participant" NATIVE = "native" class MentionTargetKind(StrEnum): """提及目标类型。 标识被提及目标的种类(用户 / Agent / Bot),用于多 Agent 协作场景的 提及目标区分。继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: USER: 用户。 AGENT: Agent。 BOT: Bot。 """ USER = "user" AGENT = "agent" BOT = "bot" @dataclass(frozen=True) class MentionTarget: """提及目标。 描述消息中提及的目标,包括目标 ID、目标种类与显示名称,用于多 Agent 协作场景的提及目标传递。集合字段使用 tuple 以保证 frozen dataclass 的不可变语义。 字段: id: 目标 ID。 kind: 目标种类。 display_name: 显示名称(可选,默认 None)。 """ id: str kind: MentionTargetKind display_name: str | None = None def __post_init__(self) -> None: """校验 id 非空。 ``id`` 必须非空,在构造时即抛出 ``ValidationError``,避免空目标 ID 导致提及目标无法定位(INV-8)。 """ if not self.id: raise ValidationError("id", "must not be empty") @dataclass(frozen=True) class MentionContext: """提及上下文。 描述消息中提及的上下文信息,包括提及类型、被提及用户列表与隐式提及 类型。集合字段使用 tuple 以保证不可变。 字段: type: 提及类型。 mentioned_users: 被提及用户 ID 列表(默认空 tuple)。 implicit_kind: 隐式提及类型(FR-29),仅当 ``type`` 为 IMPLICIT 时由渠道适配器填充,描述隐式提及的具体形式。EXPLICIT / NONE 时为 None。 mentioned_targets: 被提及目标列表(默认空 tuple,推荐字段), 携带结构化的提及目标(用户 / Agent / Bot),用于多 Agent 协作。 target_agent_ids: 目标 Agent ID 列表(默认空 tuple,便捷访问), 从 ``mentioned_targets`` 中筛选 ``kind == AGENT`` 的目标 ID。 迁移说明(§20.2 coexist → migrate → delete):``mentioned_users`` 为 向后兼容字段(INV-4),在共存期保留;``mentioned_targets`` 为推荐字段, 新代码应优先使用;``target_agent_ids`` 提供对 Agent ID 的便捷访问, 由 ``mentioned_targets`` 中 ``kind == AGENT`` 的目标 ID 组成。 """ type: MentionType mentioned_users: tuple[str, ...] = () implicit_kind: ImplicitMentionKind | None = None mentioned_targets: tuple[MentionTarget, ...] = () target_agent_ids: tuple[str, ...] = () @dataclass(frozen=True) class InboundMentionPolicy: """入站提及策略。 描述入站消息的提及策略,包括允许的隐式提及类型。集合字段使用 tuple 以保证不可变。 字段: allowed_implicit_kinds: 允许的隐式提及类型列表(默认空 tuple)。 """ allowed_implicit_kinds: tuple[ImplicitMentionKind, ...] = () @dataclass(frozen=True) class InboundMentionFact: """入站提及事实。 描述入站消息的提及检测结果,包括是否可检测、是否被提及、是否有任意提及 与检测到的隐式提及类型。集合字段使用 tuple 以保证不可变。 字段: can_detect: 是否可检测提及。 is_mentioned: 是否被提及。 has_any_mention: 是否有任意提及。 implicit_kinds: 检测到的隐式提及类型列表(默认空 tuple)。 """ can_detect: bool is_mentioned: bool has_any_mention: bool implicit_kinds: tuple[ImplicitMentionKind, ...] = () @dataclass(frozen=True) class MentionDecision: """提及决策。 描述提及策略的最终决策结果,用于管道下游决定是否处理消息。 字段: bypass_mention_requirement: 是否绕过提及要求(默认 False)。 skip_message: 是否跳过消息(默认 False)。 is_mentioned: 是否被提及(默认 False)。 """ bypass_mention_requirement: bool = False skip_message: bool = False is_mentioned: bool = False