Files
AskaEth 215eedf8e9 feat(ws): tool_execution.end 带可选 resultSummary(Y1,答应安卓端的那条)
- 形状:data.resultSummary(可选)。与回看面 toolCalls[].resultSummary 代码级同源:同一个常量
  (500 rune)与同一个截断函数,摘要由 **loop 侧**生成(trace.RedactText + renderToolResultSummary)
  再随 StreamEvent 下发,gateway 只搬运不加工(与回看面同口径)
- 缺席语义:结果真拿到才带;失败/取消/超时/未落图 ⇒ 整键缺席(绝不用空串顶位)
- 线上帧证据:真 WS + 真 JWT + 真 loop ⇒ 客户端收到 resultSummary;同源钉用真图快照反读逐字比对
- 3 个变异(空摘要也写键/去掉脱敏/失败当结果)
⚠️ 待裁:失败路径现按派工书「整键缺席」,但回看面对失败给的是 error 摘要 ⇒ 实时无结果区、事后有;
   若要实时也显示失败原因,一行可改(toolEndFrameSummary 的 return "")
2026-10-06 21:48:48 +08:00

126 lines
6.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package model
// StreamKind 枚举补全流式过程中产生的结构化事件种类。
// 它对应 OpenClaw 的 AssistantMessageEvent 片段
// (文本 / 思考 / tool_call)外加一个工具执行进度事件,
// 因此前端可以把每个片段渲染到自己的界面元素(正文、折叠
// 思考块、工具卡片),而不是累积成一条不透明的 token
// 流。
type StreamKind string
const (
// StreamKindTextStart 打开正文文本片段。一次响应的单个文本
// 片段由 TextStart/TextEnd 包起来;增量填充其中。
StreamKindTextStart StreamKind = "text_start"
// StreamKindTextDelta 是一个正文文本 token 片段。
StreamKindTextDelta StreamKind = "text_delta"
// StreamKindTextEnd 关闭正文文本片段。
StreamKindTextEnd StreamKind = "text_end"
// StreamKindThinkingStart 打开一个思维链片段(前端
// 折叠块)。保留:上报推理的适配器会发出它。
StreamKindThinkingStart StreamKind = "thinking_start"
// StreamKindThinkingDelta 是一个推理 token 片段。
StreamKindThinkingDelta StreamKind = "thinking_delta"
// StreamKindThinkingEnd 关闭一个推理片段。
StreamKindThinkingEnd StreamKind = "thinking_end"
// StreamKindToolCallStart 打开一次工具调用的流式形态。
StreamKindToolCallStart StreamKind = "tool_call_start"
// StreamKindToolCallDelta 是工具参数的一个片段(例如
// Anthropic tool_use 块增量式的 input_json)。
StreamKindToolCallDelta StreamKind = "tool_call_delta"
// StreamKindToolCallEnd 关闭一个工具调用块。
StreamKindToolCallEnd StreamKind = "tool_call_end"
// StreamKindToolExecutionStart 打开模型所请求工具的
// 执行(design/04 §1.1:进入执行)。它由工具循环
// 发出,而不是模型适配器;Phase 携带 running 标签。
StreamKindToolExecutionStart StreamKind = "tool_execution_start"
// StreamKindToolExecutionUpdate 报告所请求工具运行期间的
// 中间进度或部分输出(design/04 §1.1)。每次执行允许
// 多帧;线格式映射已定义,等工具能上报执行中进度时
// 就会有生产者。
StreamKindToolExecutionUpdate StreamKind = "tool_execution_update"
// StreamKindToolExecutionEnd 以终态阶段(done 或 error)
// 关闭一次工具执行。它由工具循环在 Invoke
// 返回后发出。
StreamKindToolExecutionEnd StreamKind = "tool_execution_end"
// StreamKindTurnStart 在 loop 的运行边界打开一个助手回合
// (design/04 §1.1 turn.start)。它不携带负载:帧外壳的
// 会话/回合标识在客户端一侧把该回合包起来。
StreamKindTurnStart StreamKind = "turn_start"
// StreamKindTurnEnd 在 loop 的运行边界关闭一个助手回合,
// 无论运行如何收场(最终答复、护栏停止、被终止的工具
// 循环、轮次耗尽或错误)。
StreamKindTurnEnd StreamKind = "turn_end"
)
// 工具执行 phase 标签(design/04 §1.1 细粒度 phase)。取值与 design/19 §1
// 的节点 status 枚举(running/done/error)一致:start/update 帧带 running,
// end 帧带 done 或 error,前端与实时流程图据此驱动工具节点的状态。
const (
// ToolPhaseRunning 标记工具进入执行或仍在执行中。
ToolPhaseRunning = "running"
// ToolPhaseDone 标记工具执行成功结束。
ToolPhaseDone = "done"
// ToolPhaseError 标记工具执行失败结束。
ToolPhaseError = "error"
)
// StreamEvent 是流式补全里的单个结构化事件。它取代
// 原先的纯文本增量,成为 Adapter.ChatStream 投递的单位,
// 因此消费者能区分正文文本、工具调用(以及将来还有推理
// 片段),并按 Index/ID 跟踪各自。
type StreamEvent struct {
// Kind 对事件分类(见 StreamKind)。
Kind StreamKind
// Delta 携带增量负载:TextDelta 时为正文文本,ThinkingDelta
// 时为推理文本,ToolCallDelta 时为 partial_argument 片段。
// 工具执行事件绝不把阶段或工具名塞进这里——
// 那些住在结构化的 Phase/Tool 字段里。
Delta string
// Index 是事件所属内容块的零基索引(提供商上报时)。
// 前端用它关联一个片段的
// start/delta/end 事件。
Index int
// ID 是某次工具调用的提供商块/索引标识(例如 Anthropic 的
// tool_use 块 id)。文本片段为空。工具执行事件复用它,
// 好让客户端能把一次执行与它的 tool_call.* 帧关联起来。
ID string
// Tool 是 tool_execution.* 事件上被调用工具的名字。
// 其它所有种类为空。
Tool string
// Phase 是 tool_execution.* 事件上的细粒度执行标签
// (ToolPhase*):start/update 时为 running,end 时为 done 或 error。
// 其它所有种类为空。
Phase string
// ResultSummary 是 tool_execution_end 事件上**已脱敏**的工具结果摘要,
// 供客户端在工具卡上渲染「这次调用拿到了什么」。它的取值与客户端**回看**
// 同一次调用所见的 `toolCalls[].resultSummary`(图节点 props.result_summary)
// 同源同口径 —— 同一份放行表、同一份凭据形状清洗(loop.toolFrameResultSummary)。
//
// 🔴 缺席语义:只有结果**真正拿到**时才非空;失败/取消/超时(Invoke 返回
// error)一律留空 ⇒ 下行帧让该键**整键缺席**,绝不用空串顶位、也绝不把
// 一次失败说成「拿到了结果」。其它所有种类为空。
ResultSummary string
// StreamID 标识某一帧所属的那一次模型流(一次 ChatStream 调用)。
// loop 在流边界打上它,使同一条流式消息的每一帧
// 共享它,而多步回合后续的模型调用会拿到新 id;
// 消费者据此把它与 TurnID 区分开——后者
// 在整个回合中保持不变。
StreamID string
// SessionID 是该流式事件所属的会话。它由 loop 在回合
// 边界打上(loop.streamStep),那里读取 Run 启动时所用的
// 会话;模型适配器跑在 loop 之下、没有
// 会话概念,所以留零值。路由该事件的消费者(WS hub)
// 读它来定位正确的会话。
SessionID string
// TurnID 是事件所属的助手回合。与 SessionID 一样,它由
// loop 打上,那里读取调用方用
// loop.WithTurnID 附上的回合标识;未附回合 id 时为零,所以消费者必须
// 把这两个字段当作尽力而为的关联,而不是硬保证。
TurnID string
}