2026-07-02 03:22:12 +08:00
|
|
|
|
"""出站管道上下文。
|
|
|
|
|
|
|
|
|
|
|
|
定义出站管道的可变局部上下文 ``OutboundContext``,携带请求从 sse-consume 到
|
|
|
|
|
|
deliver 各阶段产生的状态。上下文为 ``@dataclass``(不 frozen),阶段直接
|
|
|
|
|
|
修改字段以推进管道状态。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
|
|
from yuxi.channels.contract.dtos.capability import (
|
|
|
|
|
|
CapabilityResult,
|
|
|
|
|
|
ChannelCapabilities,
|
|
|
|
|
|
)
|
|
|
|
|
|
from yuxi.channels.contract.dtos.channel import ChannelType
|
|
|
|
|
|
from yuxi.channels.contract.dtos.common import Attachment
|
|
|
|
|
|
from yuxi.channels.contract.dtos.fence import FenceGeneration
|
|
|
|
|
|
from yuxi.channels.contract.dtos.outbound import (
|
|
|
|
|
|
FinalMessage,
|
|
|
|
|
|
FormattedMessage,
|
|
|
|
|
|
OutboundPayload,
|
|
|
|
|
|
RichMessage,
|
|
|
|
|
|
TrustedMessage,
|
|
|
|
|
|
)
|
|
|
|
|
|
from yuxi.channels.contract.dtos.outbox import MultiPartReceipt, OutboxEntry
|
|
|
|
|
|
from yuxi.channels.contract.dtos.streaming import StreamingCompleted
|
|
|
|
|
|
from yuxi.channels.contract.dtos.truncation import TruncationResult
|
|
|
|
|
|
from yuxi.channels.contract.errors import Error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
|
class OutboundContext:
|
|
|
|
|
|
"""出站管道可变局部上下文。
|
|
|
|
|
|
|
|
|
|
|
|
携带出站请求从 sse-consume 到 deliver 各阶段产生的状态,阶段按顺序填充
|
|
|
|
|
|
字段。字段按阶段分组,便于追溯状态来源。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# ---- 基础字段 ----
|
|
|
|
|
|
trace_id: str
|
|
|
|
|
|
request_id: str
|
|
|
|
|
|
channel_type: ChannelType
|
|
|
|
|
|
account_id: str
|
|
|
|
|
|
channel_session_id: str
|
|
|
|
|
|
agent_run_id: str
|
|
|
|
|
|
delivery_mode: str
|
|
|
|
|
|
trusted_sender_id: str
|
|
|
|
|
|
is_owner: bool = False
|
|
|
|
|
|
# 消息发送者角色(user | assistant | admin),outbox-persist 阶段据此
|
|
|
|
|
|
# 设置 Message 表 role 字段,管理员消息为 "admin"(FR-19)
|
|
|
|
|
|
sender_role: str = "assistant"
|
|
|
|
|
|
# 持久化策略(required | best_effort | none),由上游阶段(如
|
|
|
|
|
|
# AdminMessageService)按消息类型设置,outbox-persist 阶段据此决定
|
|
|
|
|
|
# 持久化行为(FR-22)。为 None 时回退到配置 durability_policy_default。
|
|
|
|
|
|
durability_policy: str | None = None
|
2026-07-07 18:54:33 +08:00
|
|
|
|
# 出站投递幂等键(可选)。跨重试稳定,供 deliver 阶段调用适配器
|
|
|
|
|
|
# ``sendMessage`` / ``sendMessageContinuation`` 时传入,实现渠道侧去重。
|
|
|
|
|
|
idempotency_key: str | None = None
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
# ---- 会话定位字段 ----
|
|
|
|
|
|
conversation_id: str | None = None
|
|
|
|
|
|
peer_id: str | None = None
|
|
|
|
|
|
message_id: str | None = None
|
|
|
|
|
|
channel_capabilities: ChannelCapabilities | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- trusted-inject 阶段请求上下文字段(FR-25) ----
|
|
|
|
|
|
# gateway_client_info 与 is_dry_run 由上游从实际请求上下文提取后填充,
|
|
|
|
|
|
# trusted-inject 阶段透传给 TrustedMessageGuard.buildContext,避免领域
|
|
|
|
|
|
# 服务硬编码(FR-25)。
|
|
|
|
|
|
gateway_client_info: dict[str, Any] | None = None
|
|
|
|
|
|
is_dry_run: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
# ---- sse-consume 阶段字段 ----
|
|
|
|
|
|
stream_chunks: list[str] = field(default_factory=list)
|
|
|
|
|
|
chunk_results: list[Any] = field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
# ---- load-build 阶段输入字段 ----
|
|
|
|
|
|
# 富消息源数据(FR-08),由上游阶段(如 AdminMessageService)填充,
|
|
|
|
|
|
# load-build 阶段将其包装为 RichMessageFields 装入 OutboundPayload。
|
|
|
|
|
|
rich_message: RichMessage | None = None
|
|
|
|
|
|
# 附件列表(FR-19),由上游阶段(如 AdminMessageService)填充,
|
|
|
|
|
|
# load-build / format 阶段据此装入出站负载供适配器渲染。
|
|
|
|
|
|
attachments: tuple[Attachment, ...] = ()
|
|
|
|
|
|
|
|
|
|
|
|
# ---- fence-check 阶段输入字段(FR-23,由入站管道捕获并透传) ----
|
|
|
|
|
|
fence_inbound_generation: int | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- fence-check 阶段字段 ----
|
|
|
|
|
|
fence_generation: FenceGeneration | None = None
|
|
|
|
|
|
fence_visible: bool = False
|
2026-07-09 13:51:58 +08:00
|
|
|
|
# 待标记可见的 fence 代际(可选)。由上游阶段填充,fence-check 阶段据此
|
|
|
|
|
|
# 将指定代际标记为可见,用于降级投递等场景的代际可见性控制。
|
|
|
|
|
|
generation_to_mark_visible: int | None = None
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
# ---- load-build 阶段字段 ----
|
|
|
|
|
|
outbound_payload: OutboundPayload | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- capability-verify 阶段字段 ----
|
|
|
|
|
|
capability_result: CapabilityResult | None = None
|
|
|
|
|
|
# 富消息能力校验结果(FR-08 增强),声明+证明双层校验。
|
|
|
|
|
|
rich_message_capability_result: CapabilityResult | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- format 阶段字段 ----
|
|
|
|
|
|
formatted_message: FormattedMessage | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- trusted-inject 阶段字段 ----
|
|
|
|
|
|
trusted_message: TrustedMessage | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- prefix 阶段字段 ----
|
|
|
|
|
|
final_message: FinalMessage | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- streaming 阶段字段 ----
|
|
|
|
|
|
streaming_started: bool = False
|
|
|
|
|
|
# 输入指示是否已启动(FR-13)。typing-indicator 阶段调用
|
|
|
|
|
|
# ``startTypingIndicator`` 成功后置 True,typing-stop 阶段据此
|
|
|
|
|
|
# 决定是否调用 ``stopTypingIndicator`` 释放输入指示资源。
|
|
|
|
|
|
typing_started: bool = False
|
|
|
|
|
|
streaming_completed: StreamingCompleted | None = None
|
2026-07-07 18:54:33 +08:00
|
|
|
|
# 流式中断时分片序号(可选)。流式投递被中断时记录已发送分片序号,
|
|
|
|
|
|
# 供 ``sendMessageContinuation`` 续发定位续发起点。
|
|
|
|
|
|
stream_aborted_at_chunk: int | None = None
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
# ---- truncation-check 阶段字段 ----
|
|
|
|
|
|
truncation_detected: bool = False
|
|
|
|
|
|
truncation_info: TruncationResult | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- outbox-persist 阶段字段 ----
|
|
|
|
|
|
outbox_entry: OutboxEntry | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- deliver 阶段字段 ----
|
|
|
|
|
|
delivered: bool = False
|
|
|
|
|
|
channel_msg_id: str | None = None
|
|
|
|
|
|
# 多部分消息回执列表(FR-22 多部分模型)。适配器实现 ``sendMessageMultiPart``
|
|
|
|
|
|
# 时由 deliver 阶段填充,``channel_msg_id`` 取首分片 ``platform_msg_id``。
|
|
|
|
|
|
# 单分片投递(``sendMessage``)时为 None。部分失败时仅含成功分片,由
|
|
|
|
|
|
# 下游阶段据长度判定部分失败。
|
|
|
|
|
|
multi_part_receipts: list[MultiPartReceipt] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
# ---- 降级标志字段 ----
|
|
|
|
|
|
degraded: bool = False
|
|
|
|
|
|
degraded_reason: Error | None = None
|