Skip to content

Decode SSE chunks incrementally - #1068

Merged
PeterDaveHello merged 1 commit into
ChatGPTBox-dev:masterfrom
PeterDaveHello:perf/incremental-sse-decoding
Sep 11, 2026
Merged

PeterDaveHello merged 1 commit into
ChatGPTBox-dev:masterfrom
PeterDaveHello:perf/incremental-sse-decoding

Conversation

@PeterDaveHello

@PeterDaveHello PeterDaveHello commented Sep 11, 2026

Copy link
Copy Markdown
Member

Summary

  • Keep one streaming TextDecoder per SSE parser instance.
  • Decode only newly received bytes instead of rebuilding and decoding the entire pending byte buffer for every chunk.
  • Reset decoder state together with the parser state.

Why

The parser currently copies pending bytes and decodes the full accumulated buffer again whenever a new chunk arrives. For fragmented or long SSE lines, this repeats work and allocations.

Streaming decode preserves incomplete UTF-8 sequences across chunk boundaries while allowing the parser to keep only the unconsumed decoded text.

Copilot AI lite review requested due to automatic review settings September 11, 2026 14:31
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: bc94fe68-6f22-49db-9688-3aa4c408e41b

📥 Commits

Reviewing files that changed from the base of the PR and between e73d3a1 and 223cea5.

📒 Files selected for processing (1)
  • tests/unit/utils/eventsource-parser.test.mjs

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The eventsource parser now uses one streaming TextDecoder for incoming chunks. Resetting creates a new decoder. Tests cover UTF-8 splits, BOM and replacement bytes, and decoder reset behavior.

Changes

Eventsource decoding

Layer / File(s) Summary
Incremental decoder and parser state
src/utils/eventsource-parser.mjs, tests/unit/utils/eventsource-parser.test.mjs
The parser decodes chunks with a streaming TextDecoder, clears decoder state during reset(), and manages the text buffer by character position. Tests use explicit event expectations and cover split UTF-8 input, BOM and replacement bytes, and reset behavior.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Refactor

Merge Risk: ⚪ Minimal · up to 61f63

Incremental SSE decoding preserves chunk-boundary behavior and reset isolation with direct regression coverage. No actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: incremental decoding of SSE chunks.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Decode SSE chunks incrementally with a streaming TextDecoder

✨ Enhancement 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Incrementally decode incoming SSE bytes with one streaming decoder per parser.
• Retain only unconsumed text, avoiding repeated byte copies and full-buffer decoding.
• Reset decoder state and verify UTF-8 boundaries and reset isolation.
Diagram

graph TD
  A["Byte Chunks"] --> B["Streaming Decoder"] --> C["Text Buffer"] --> D["SSE Parser"] --> E["Parsed Events"]
  F["Parser Reset"] --> B
Loading
High-Level Assessment

The parser-scoped streaming TextDecoder is the best fit for the synchronous feed API. Manually retaining incomplete UTF-8 byte tails would duplicate decoder logic, while TextDecoderStream would require an asynchronous stream architecture; neither improves on the proposed approach.

Files changed (2) +27 / -6

Enhancement (1) +3 / -6
eventsource-parser.mjsDecode SSE input incrementally with persistent decoder state +3/-6

Decode SSE input incrementally with persistent decoder state

• Replaces accumulated raw-byte copying and repeated full-buffer decoding with a parser-scoped streaming TextDecoder. The parser now retains only unconsumed decoded text, and reset creates a fresh decoder to discard pending byte sequences.

src/utils/eventsource-parser.mjs

Tests (1) +24 / -0
eventsource-parser.test.mjsCover fragmented UTF-8 decoding and decoder reset behavior +24/-0

Cover fragmented UTF-8 decoding and decoder reset behavior

• Adds coverage for splitting multilingual and emoji data at every byte boundary. Also verifies that reset discards an incomplete UTF-8 sequence before parsing subsequent input.

tests/unit/utils/eventsource-parser.test.mjs

@qodo-code-review

qodo-code-review Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit 61f630c ⚖️ Balanced

Results up to commit e73d3a1 ⚖️ Balanced


No changes from previous review

Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are fully reviewed with no unresolved blocking issues.

Pull request overview

Updates SSE parsing to decode UTF-8 data incrementally while preserving decoder state across chunks.

Changes:

  • Reuses a streaming TextDecoder.
  • Resets decoder state with parser state.
  • Adds split-sequence and reset tests.
File summaries
File Description
tests/unit/utils/eventsource-parser.test.mjs Tests split UTF-8 sequences and decoder reset behavior.
src/utils/eventsource-parser.mjs Implements incremental decoding and decoder reset handling.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

  • Streaming SSE decode: src/utils/eventsource-parser.mjs now keeps one TextDecoder and decodes only newly received bytes (decoder.decode(chunk, { stream: true })) instead of accumulating and re-decoding the whole byte buffer on every feed. The parallel bytes tracking (and its TextEncoder re-encode bookkeeping on partial consumption) is removed entirely.
  • Decoder reset: reset() reassigns decoder = new TextDecoder(), discarding any buffered incomplete UTF-8 sequence together with the cleared buffer.
  • Tests: a byte-boundary test asserting data: 台灣🙂 café\n\n parses identically when split at every byte position, and a test that reset() drops pending decoder bytes so a subsequent clean event parses without residue.

I verified the refactor is behavior-preserving: all 21 unit tests pass (including the existing every-chunk-boundary and CRLF-split tests), and the byte-boundary test also holds at every real split of multi-byte content against the old implementation — the change is a genuine CPU/allocation win (drops the O(chunks × full-buffer) re-decodes) with the streaming decoder carrying the incomplete UTF-8 state that the old bytes array had to re-track manually. The only caller, src/utils/fetch-sse.mjs, feeds Uint8Array chunks from reader.read(), which is exactly what TextDecoder.decode expects. Lint and Prettier are clean on both changed files.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Copilot AI review requested due to automatic review settings September 11, 2026 15:01

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No unresolved issues were identified, and the changes have corresponding test coverage.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@PeterDaveHello
PeterDaveHello force-pushed the perf/incremental-sse-decoding branch from 223cea5 to 61f630c Compare September 11, 2026 15:10

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

This run covers the delta since the prior Pullfrog review (e73d3a1223cea5, "Strengthen SSE parser regression coverage"), which touches only tests:

  • Replaced the self-referential const expected = parseChunks(stream) baselines in the every-chunk-boundary and UTF-8 byte-boundary tests with hard-coded event arrays, so both tests can now actually fail on a parser regression instead of always matching the code under test.
  • Added a test that pending SSE data survives a chunk split when the stream begins with a UTF-8 BOM (exercises the streaming decoder's BOM handling at stream start).
  • Added a test that an invalid UTF-8 byte (0xff) is replaced with U+FFFD mid-stream without losing the partially-buffered event across the split.

I verified all 1049 unit tests pass via npm test, traced the hand-written expected arrays against the parser's parse/emit semantics (they match), and confirmed both new tests pin genuine behavior of the streaming decoder (BOM-stripping and U+FFFD replacement interaction with the pending-data path). The hard-coded expectations close the tautology noted on the earlier review, and the new tests are meaningful coverage for the incremental-decode change.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 61f630c

@PeterDaveHello
PeterDaveHello merged commit 5911812 into ChatGPTBox-dev:master Sep 11, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants