Skip to content

fix(ai-chat): stop 100% CPU freeze during streaming (#1205)#1221

Merged
datlechin merged 4 commits into
mainfrom
fix/ai-chat-streaming-freeze-1205
May 11, 2026
Merged

fix(ai-chat): stop 100% CPU freeze during streaming (#1205)#1221
datlechin merged 4 commits into
mainfrom
fix/ai-chat-streaming-freeze-1205

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

Fixes #1205. The AI chat panel pegged the main thread at 100% CPU and froze the app whenever a response streamed in. The cascade per 150 ms flush: existing + text rebuilt the assistant turn's text block (O(n²) over the response), plainText was recomputed from blocks on every observer tick, and the result fed into Markdown(text) keyed by \.offset, which made MarkdownUI re-parse the full markdown tree every flush. Schema prefetch and per-turn attachment resolution also ran on the main actor.

  • Token append moved off the message blocks into an @ObservationIgnored buffer on the view model, published through a streamingTick counter so only the streaming bubble re-renders per token
  • plainText is now a cached stored property maintained by appendText, not recomputed from blocks
  • ChatContentBlock is a struct with a stable UUID id; the message-view ForEach keys on it, so finalized blocks are not re-created when a sibling changes
  • The streaming bubble is its own subview (AIStreamingBubbleView) and renders plain Text; Markdown only runs once a block is committed at end-of-stream or tool boundary
  • The redundant onChange(of: messages.last?.plainText) scroll trigger is gone; .defaultScrollAnchor(.bottom) + the messages.count trigger handle auto-scroll
  • prepTask is now Task.detached(priority: .userInitiated); only hops to MainActor for capturePromptContext and messages.dropLast() snapshots

CHANGELOG entry added under Fixed.

Test plan

  • Build green (xcodebuild, Debug)
  • SwiftLint --strict clean on touched files
  • AIChatViewModelMentionsTests passes (9/9)
  • AnthropicProviderParserTests, GeminiProviderParserTests, OpenAICompatibleProviderParserTests pass
  • Manual: send a short prompt with Include schema on, confirm response streams without UI hang and final text renders as Markdown
  • Manual: send a tool-using prompt in Agent mode, confirm the assistant turn shows tool blocks + final text after each roundtrip
  • Manual: cancel mid-stream, confirm partial text is preserved on the assistant turn and Stop ends streaming cleanly
  • Manual: trigger a streaming error (revoke API key mid-stream), confirm error banner shows and empty turn is removed

Known follow-up

There is a separate UX issue where, after an Agent-mode tool roundtrip, a fresh assistant turn can be left with empty blocks if the model produces no text after the tool call — the message then sticks on the typing indicator. That is pre-existing (same path existed before this refactor) and is out of scope for this PR; it deserves its own fix to either prune the empty turn or render an explicit "completed" state.

@datlechin datlechin merged commit f2ac637 into main May 11, 2026
2 checks passed
@datlechin datlechin deleted the fix/ai-chat-streaming-freeze-1205 branch May 11, 2026 12:52
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.

[macos] 一执行AI对话就进程无响应

1 participant