Skip to content

perf(ios/chat): coalesce assistant stream tokens#3460

Closed
romgenie wants to merge 3 commits into
OpenCoven:mainfrom
romgenie:fix/pr-3455-flush-coalescer
Closed

perf(ios/chat): coalesce assistant stream tokens#3460
romgenie wants to merge 3 commits into
OpenCoven:mainfrom
romgenie:fix/pr-3455-flush-coalescer

Conversation

@romgenie

Copy link
Copy Markdown
Member

Supersedes #3455, whose OpenCoven-owned head branch cannot be updated by maintainers (maintainer_can_modify: false; direct push returns HTTP 403). This branch retains the original performance commit by @BunsDev and adds the review correction.

Behavior

  • Buffers assistant chunks and flushes at a ~50ms cadence instead of causing an observed-array update and UI/persistence notification per token.
  • Flushes buffered text before send-stream resume/resync and on every failed/no-resumable resume attempt, so the final coalescing window is never dropped.

Validation

  • pnpm typecheck
  • pnpm check:tests-wired
  • pnpm test:mobile (81 files)

The normal PR CI is enabled because this replacement targets main.

BunsDev and others added 2 commits July 18, 2026 17:55
Streaming applied every assistant chunk immediately: mutate the message
then onChange() on EVERY token. Because messages is an observed array on
an @observable ChatThread, each mutate reassigns messages[idx] and
invalidates the whole message list — so a fast stream (tokens arriving
faster than a frame) drove a per-token render and scroll storm.

Introduce a small @mainactor StreamCoalescer that buffers chunk text and
flushes on a ~50ms cadence (~20 UI updates/sec) instead of per token. All
terminal paths (.done/.error, end of the send loop, and the resume loop)
flush unconditionally, so the final text is always complete; a single
coalescer instance is shared across the send + resume streams so a
resumed turn keeps the same buffer.

Verified: xcodebuild build (scheme CovenCave, iOS Simulator) succeeds.
Ensure a paused stream drains its final buffered assistant chunk within the coalescing interval rather than waiting for a later frame or terminal event.

Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
@romgenie

Copy link
Copy Markdown
Member Author

Follow-up pushed in 469081d8: the coalescer previously drained only when another SSE frame arrived, so a final chunk could remain buffered indefinitely while a stream paused. It now schedules one 50 ms main-actor flush, while all terminal/recovery paths still drain idempotently and cancel the pending task.

Validated locally with pnpm typecheck, pnpm check:tests-wired, and pnpm test:mobile (81 files). The exact-head CI rollup is green (Frontend build, E2E, cross-environment, Rust, sidecar runtime on Ubuntu and Windows, and CodeQL); no unresolved review threads or merge conflicts remain.

@romgenie

Copy link
Copy Markdown
Member Author

Validation complete on the final PR head: all required CI checks passed (Frontend build, E2E, Rust, cross-environment, both sidecar-runtime jobs, and CodeQL); review-thread audit found no unresolved threads or review findings. This replacement is mergeable and ready for maintainer review. It preserves @BunsDev's original performance commit and includes the buffered-text recovery fix from #3455's review.

@romgenie romgenie closed this Jul 19, 2026
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.

2 participants