Skip to content

feat(chat): expandable stream-retry details in GUI and WebUI, codex-style retry policy#190

Merged
su-fen merged 2 commits into
mainfrom
features
Jul 21, 2026
Merged

feat(chat): expandable stream-retry details in GUI and WebUI, codex-style retry policy#190
su-fen merged 2 commits into
mainfrom
features

Conversation

@su-fen

@su-fen su-fen commented Jul 21, 2026

Copy link
Copy Markdown
Member

概述

将 LLM 流式请求的重试层与 codex 对齐(6 次总尝试 = 5 次重试,200ms * 2^(n-1) * uniform(0.9, 1.1) 无上限退避),并在桌面端与 WebUI 双端的模型回复区提供可展开/折叠的"重试详情"块,逐条展示每一轮重试的报错内容。

桌面端(agent-gui)

  • streamRetry.ts:codex 式退避;onRetry 回调新增第三参 errorMessageonRetryRecovered 在重试后的尝试产出首个内容事件时触发
  • liveTranscriptStore / useLiveTranscriptController:新增 retryAttempts 实时状态,复用与 toolStatus 相同的逐帧合并 flush
  • agentRunner / runTextConversationTurn:两种聊天模式各自维护按网络尝试粒度的累积器(每次新尝试开始时清空)
  • RetryDetailsBlock(仿 ThinkingBlock 的折叠交互):渲染在活跃 live 轮次内,以及"尚无内容"的兜底行(文本模式重试最常见的场景)

WebUI(agent-gateway/web)

  • 数据搭载在现有 tool_status 事件新增的可选 retryAttempts 字段上(Rust 信封白名单放行;Go 网关本就对 data JSON 做通用透传,无 proto / Go 改动);语义:字段缺失/null = 列表不变,空数组 = 清空
  • transcriptStore 镜像该列表,run 边界(run_started / run_finished / reset)自动清零
  • GatewayTranscript 在等待气泡与 live 回复行两处(互斥)渲染镜像的 RetryDetailsBlock,i18n 文案与桌面端一致

测试与验证

  • 新增/更新测试:stream-retry 回调契约(含 errorMessage 透传)、bridge 事件去重与搭载形状、Rust 信封透传、web store 生命周期(更新/保持/清空/run 边界重置)
  • GUI 前端 1079/1079、Web 369/369、Rust envelope 5/5 全部通过;两端 tsc --noEmit 干净;cargo check 通过;scripts/check-mirror.mjs 87 文件字节镜像校验通过
  • 另经 22-agent 多切片审查 + 三视角对抗验证:确认无既有代码误伤;审查发现的 React key 撞车问题已在本 PR 内修复(改用下标 key)

需要评审者知晓

  1. 重试策略全局生效:标题生成、压缩摘要等后台模型请求同样从"最多 3 次尝试、约 1.5s 退避"变为"最多 6 次尝试、约 6.2s 累计退避"(对齐 codex 的本意,但这些流程没有重试 UI)
  2. 已知限制:WebUI 在重试进行中途连接/重连时,状态文字可从 activity 快照恢复,但重试详情列表要等下一次重试事件才出现(activity/snapshot proto 未携带该字段,补齐需三端 proto 联动,收益甚微)

🤖 Generated with Claude Code

su-fen and others added 2 commits July 21, 2026 13:58
…tyle retry policy

Align the LLM stream-retry layer with codex (6 total attempts = 5 retries,
uncapped 200ms*2^(n-1)*uniform(0.9,1.1) backoff) and surface each retry
attempt's error in an expandable "重试详情" block on the live reply, on both
the desktop app and the WebUI.

Desktop (agent-gui):
- streamRetry.ts: codex backoff, onRetry now carries the failing attempt's
  errorMessage, onRetryRecovered fires when a retried attempt commits
- liveTranscriptStore/useLiveTranscriptController: retryAttempts live state
  with the same coalesced per-frame flush as toolStatus
- agentRunner/runTextConversationTurn: per-network-attempt accumulators
  (cleared at each fresh attempt) wired through both chat modes
- RetryDetailsBlock (RoundContent.tsx, modeled after ThinkingBlock), rendered
  in the active live round and in the no-rounds-yet fallback row

WebUI (agent-gateway/web):
- retryAttempts rides the existing tool_status chat event (Rust envelope
  passes the field through; Go ingress already merges data generically):
  null/absent = unchanged, empty array = clear
- transcriptStore mirrors the list with run-boundary resets; GatewayTranscript
  renders the mirrored RetryDetailsBlock in the pending bubble and under the
  live assistant row

Tests: stream-retry callback contract incl. errorMessage, bridge event
dedupe/ride-along shape, Rust envelope pass-through, web store lifecycle
(update/keep/clear/run-boundary resets).

Known limitation: a WebUI client that (re)connects mid-retry restores the
status text from the activity snapshot but sees retry details only from the
next retry event (activity/snapshot protos don't carry the list).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@su-fen
su-fen merged commit 8d11857 into main Jul 21, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant