Skip to content

fix: reduce streaming stutter in the web chat - #1085

Merged
wbxl2000 merged 4 commits into
mainfrom
fix-streaming-stutter
Jun 25, 2026
Merged

fix: reduce streaming stutter in the web chat#1085
wbxl2000 merged 4 commits into
mainfrom
fix-streaming-stutter

Conversation

@wbxl2000

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — investigated from a user report of stuttery streaming in LAN mode. Supersedes #1083 (renamed branch).

Problem

When using the server-hosted web UI (especially over LAN), the assistant stream looks stuttery: tokens appear word-by-word, and it gets worse as a session grows longer. Two independent causes were identified:

  1. Frontend render storm. Every streamed token synchronously triggered a full re-render (whole-session messagesToTurns rebuild + full Markdown re-parse + scroll reflow). When the model streams fast, hundreds of redundant renders per second saturate the main thread, so the stream looks slow.
  2. Server TCP batching. Each token is a small WebSocket frame sent with Nagle's algorithm enabled; combined with the client's delayed ACK, small frames get bunched into ~40 ms clusters, so even a short "hi" reply looks like it pops out word-by-word.

What changed

  • Web (apps/kimi-web): coalesce high-frequency streaming events (assistantDelta, agentDelta, toolOutput, taskProgress) onto the next animation frame so Vue commits at most one render per frame. Lifecycle / control-flow events still apply immediately (so turn-end cleanup is never delayed, even in a background tab). Added eventBatcher.ts (pure, tested) and wired it into the WS event pipeline.
    • Flush any pending deltas before applying a session snapshot, so stale queued deltas are not appended on top of an authoritative snapshot (avoids duplication on resync).
    • Flush any pending deltas before re-subscribing to a session's events, so the transcript is current before the subscribe cursor is read.
  • Server (packages/server): call socket.setNoDelay(true) on the upgraded WebSocket socket so small delta frames are sent immediately instead of being held by Nagle's algorithm.

The logging needed to confirm the arrival cadence already exists: enabling the opt-in debug trace (?debug=1 or localStorage["kimi-web.debug"]="1") records every inbound frame's arrival timestamp into a bounded ring buffer, so no new logging was added.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works. (apps/kimi-web/test/event-batcher.test.ts)
  • Ran gen-changesets skill, or this PR needs no changeset. (added two changesets)
  • Ran gen-docs skill, or this PR needs no doc update. (no user-facing doc change)

@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 35726c7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@moonshot-ai/server Patch
@moonshot-ai/kimi-web Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 24, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@35726c7
npx https://pkg.pr.new/@moonshot-ai/kimi-code@35726c7

commit: 35726c7

@wbxl2000

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17e8f1b654

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Coalesce high-frequency render events onto the next animation frame;
// everything else is applied immediately. See createEventBatcher /
// processEvent above.
enqueueEvent({ appEvent, meta });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Flush queued stream events before session teardown

When a user archives a streaming session and the archive response returns before the next animation frame, this newly queued assistantDelta/toolOutput can survive archiveSession -> forgetSession, which unsubscribes and deletes the per-session maps. The later rAF drain still calls reduceAppEvent for that session, recreating entries such as messagesBySession[sessionId]/lastSeqBySession after teardown; if that id is fetched again, hasLoadedMessages() can then treat the stale/empty cache as authoritative and skip the snapshot. Please flush or discard pending events for the session before clearing it.

Useful? React with 👍 / 👎.

@wbxl2000

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 35726c7021

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@wbxl2000
wbxl2000 merged commit f1fad72 into main Jun 25, 2026
8 checks passed
@wbxl2000
wbxl2000 deleted the fix-streaming-stutter branch June 25, 2026 05:06
@github-actions github-actions Bot mentioned this pull request Jun 25, 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.

1 participant