跪拜 Guibai
← All articles
Interviews · GitHub

The Five-Layer Pipeline That Turns LLM Tokens Into Streaming Text

By 嘟嘟0717 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Every LLM API that supports streaming uses the same SSE-over-HTTP pattern, but the edge cases—truncated JSON across packet boundaries, the `[DONE]` sentinel, multi-byte character splitting—are what turn a demo into something that doesn't silently drop characters. Understanding the buffer-and-retry mechanism means you can implement streaming for any provider without relying on a library that hides these failure modes.

Summary

Streaming output from an LLM is not a single operation but a five-layer pipeline that transforms raw network bytes into characters on a screen. Each call to `reader.read()` pulls a chunk of binary data that may contain zero, one, or multiple lines of SSE-formatted text, and a single JSON line can be split across two network packets. A `buffer` variable catches these truncated fragments so they can be stitched together on the next iteration instead of being discarded.

The pipeline runs inside a `while` loop: decode the binary chunk, prepend any buffered residue, split on newlines, filter for lines starting with `data:`, strip that prefix with `slice(6)`, check for the `[DONE]` sentinel, and parse the remaining JSON to extract `delta.content`. A `try-catch` block catches incomplete JSON and stores it back into the buffer with the `data:` prefix restored, ensuring the format stays consistent for the next round.

Two assignment strategies separate streaming from non-streaming modes. Streaming uses `content.value += delta` to append each token incrementally, while non-streaming calls `response.json()` once and assigns the full `message.content` with `=`. The `[DONE]` flag is a plain-text signal embedded in the SSE stream, not a TCP-level close, so the code must manually set `done = true` to exit the while loop.

Takeaways
Streaming output is a five-layer pipeline: read binary, decode to text, split and filter lines, strip the SSE prefix, and parse JSON to extract delta.content.
A single chunk from reader.read() can contain zero, one, or multiple SSE lines, and a JSON line can be split across two network packets.
The buffer variable stores incomplete JSON from a failed parse and prepends it to the next chunk so no data is permanently lost.
catch must restore the `data:` prefix when buffering, or the fragment won't match the filter on the next iteration and will be discarded.
The [DONE] flag is a plain-text SSE message, not a stream close; the code must manually set done = true to exit the while loop.
Streaming uses delta.content with += to append tokens; non-streaming uses message.content with = to assign the full response at once.
TextDecoder with {stream: true} handles multi-byte UTF-8 characters that span chunk boundaries without corruption.
Conclusions

The entire streaming implementation hinges on a single design decision: never discard data on error. The try-catch block is not for error recovery but for deferred assembly, treating a parse failure as a signal that the message is incomplete rather than malformed.

The buffer variable is empty in the common case and only populated when a truncation occurs, which means the happy path pays no overhead for the safety net.

Restoring the `data:` prefix inside the catch block is a subtle detail that, if missed, causes silent data loss—the fragment won't pass the filter on the next iteration and disappears without an error.

The [DONE] check and the reader.read() done flag are two independent termination conditions that must both be handled because the server can signal completion either through the stream or through an in-band message.

Concepts & terms
SSE (Server-Sent Events)
A protocol where the server pushes data to the client over a single HTTP connection using newline-delimited text frames prefixed with `data:`. Empty lines separate messages, and lines starting with a colon are comments or keep-alive heartbeats.
delta vs. message
In streaming mode, each chunk contains a `delta` object with only the newly generated tokens. In non-streaming mode, the full response is returned once as a `message` object containing the entire generated text.
MTU (Maximum Transmission Unit)
The largest packet size a network protocol can transmit, typically around 1500 bytes for Ethernet. A single SSE line exceeding this size will be split across multiple packets, causing JSON truncation at the application layer.
TextDecoder with stream: true
A mode that tells the decoder to expect multi-byte characters to potentially span multiple chunks. It internally buffers incomplete byte sequences so that a UTF-8 character split across two reads is correctly assembled rather than producing a replacement character.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗