Skip to content

fix(desktop): 修复间歇性重复渲染 assistant 回复的问题 - #70173

Closed
x7peeps wants to merge 1 commit into
NousResearch:mainfrom
x7peeps:fix/issue-70108-deduplicate-interim-assistant-render
Closed

fix(desktop): 修复间歇性重复渲染 assistant 回复的问题#70173
x7peeps wants to merge 1 commit into
NousResearch:mainfrom
x7peeps:fix/issue-70108-deduplicate-interim-assistant-render

Conversation

@x7peeps

@x7peeps x7peeps commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

背景

修复 #70108: Desktop 间歇性地将同一个 assistant 回复渲染为两个独立的消息气泡。

根因分析

当 gateway 发送 message.interim 将正在流式传输的 assistant 气泡密封(sealed)后,紧接着的 message.complete 在落地时会尝试找到已有的 assistant 消息进行合并。

原有的去重逻辑要求 response_previewed=true 才能将最终回复合并到已密封的 interim 气泡上。当 gateway 未设置 response_previewed 标志时(这是常见情况),即使最终回复文本与 interim 文本完全相同或是其前缀扩展,代码也会创建一个新的 assistant 消息气泡,导致同一回复在界面上出现两次。

数据库只持久化了一行 assistant 消息,所以重新加载(rehydration)后重复气泡消失——这证实了问题出在实时渲染路径上。

修复方式

use-message-stream/index.tscompleteAssistantMessage 中:

  • 移除 response_previewed 的前置要求:当 interimBoundaryPending=true 时,只要最终文本与已密封的 interim 文本相同或是其前缀扩展(finalText.startsWith(existingText)),就将最终回复合并到现有的 interim 气泡上。
  • 这覆盖了两种场景:
    1. response_previewed 的场景(continuation-budget 回退路径,fix(desktop, ink): don't wipe messages before final message #65919
    2. 没有 response_previewed 但最终文本与 interim 相同或扩展了 interim 的常见场景

测试变更

  • 更新 interim-sealing.test.tsx 中的同名文本测试:期望 1 个气泡(原期望 2 个)
  • 新增 settles a prefix-matched final onto the interim even without response_previewed 测试用例
  • 所有 40 个 use-message-stream 测试通过

验证

  • 代码变更已在本地验证(40/40 tests passed)
  • 遵循项目 AGENTS.md 贡献规范
  • 无破坏性变更

Closes #70108

…plicate bubbles

When message.interim seals an assistant bubble and message.complete arrives
with the same (or a superset of) text, the live UI must collapse them into
one bubble — the DB persists only one row, so rendering two is a duplicate.

Previously, the dedup path required response_previewed=true to settle onto
the interim.  Without that flag, the fallback created a second assistant
message even when the texts matched, causing intermittent duplicate rendering.

Fix: broaden the interimBoundaryPending guard to cover the common case where
the final text matches or extends the interim text, regardless of the
response_previewed flag.

Fix NousResearch#70108
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused reproduction and regression coverage.

Automated hermes-sweeper review found that current main already implements this behavioral fix, with a stricter guard:

  • Commit 53bdcacf17ddb7483633013e76697672fb3106e0 (fix(desktop): stop assistant reply rendering twice after a tool-call turn (#70232)) is on current main.
  • apps/desktop/src/app/session/hooks/use-message-stream/index.ts:549-572 settles non-previewed sealed interim messages when their text is continuous, while requiring existing.interim to avoid collapsing unrelated assistant segments.
  • apps/desktop/src/app/session/hooks/use-message-stream/interim-sealing.test.tsx:189-234 already covers identical and prefix-extended non-previewed completions plus the distinct-message safeguard.

Closing as implemented on main.

@teknium1 teknium1 closed this Jul 30, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop intermittently renders duplicate assistant replies while state.db contains one response

3 participants