Cracking the Streaming JSON Problem: How a Developer Built "SoFar" to Tame LLM Outputs in Real-Time

Share
Cracking the Streaming JSON Problem: How a Developer Built "SoFar" to Tame LLM Outputs in Real-Time

Executive Overview

The rise of Large Language Models (LLMs) has fundamentally transformed modern software development, introducing powerful generative capabilities into applications ranging from customer support bots to dynamic UI generators. However, integrating these probabilistic text engines into deterministic application architectures remains a notoriously friction-filled endeavor.

Consider a deceptively simple use case: building a recipe application where an LLM is prompted to return structured JSON data, and the user interface (UI) populates dynamically as the response streams in token by token. In theory, this provides a snappy, highly responsive user experience. In practice, standard JavaScript execution collapses almost immediately.

Because LLMs generate text token by token—predicting the next logical sequence of characters based on probability—the intermediate outputs sent over the wire are invariably malformed JSON fragments. A standard JSON.parse() invocation will throw a syntax error on every single chunk until the final closing bracket is delivered. Developers are historically forced into a frustrating dilemma: wait until the entire generation completes (thereby defeating the entire purpose of streaming), or deploy brittle, ad-hoc Regular Expressions (RegEx) to close unclosed brackets and guess at the missing data, only to watch those band-aids shatter upon encountering the first escaped quote or nested array.

Frustrated by this persistent engineering bottleneck, a developer has introduced a lightweight, zero-dependency open-source library named SoFar. Weighing in at a minuscule 425 bytes gzipped, SoFar approaches the problem with a radical design philosophy: never throw an error, and never hallucinate or invent data. By cleanly balancing robust fallback scanning with native JavaScript parsing speed, SoFar provides a reliable bridge between the chaotic, streaming nature of generative AI and the rigid syntax demands of client-side applications.


Detailed Chronology: The Genesis of a Modern Web Development Bottleneck

The Promise and Peril of Streaming LLM Responses

To understand the engineering challenge SoFar solves, one must examine the mechanics of HTTP streaming in modern web applications. When an application communicates with an LLM provider (such as OpenAI, Anthropic, or an open-source model running on a local cluster via Ollama), the response is typically delivered using Server-Sent Events (SSE) or chunked transfer encoding.

For plain text generation—like an essay or a chat message—streaming is straightforward: every chunk of text arriving from the network can be immediately appended to the DOM. But when developers demand structured data (such as JSON schemas for UI controls, data tables, or dynamic forms), the rules change entirely.

JSON is an unforgiving syntax. A single missing quotation mark, a trailing comma, or an unclosed brace renders the entire document invalid. When an LLM streams a JSON object, the developer’s event handler witnesses a chaotic evolution of fragments:

"title": "Pad Th
"title": "Pad Thai", "ingr
"title": "Pad Thai", "ingredients": ["rice noo

Attempting to run JSON.parse() on these intermediate strings results in an immediate exception. For complex applications, developers were trapped between two deeply flawed paradigms:

  1. The Waiting Game: Abandon streaming entirely, wait for the [DONE] signal from the LLM, parse the complete JSON string, and then render the UI. This introduces noticeable latency, making the application feel sluggish and unresponsive.
  2. The RegEx Hack: Write custom regular expression routines to guess missing quotes, append closing brackets, and clean up dangling commas. These scripts invariably fail when parsing edge cases like escaped characters ("), nested arrays of objects, or unicode escape sequences.

Engineering the Third Option

Recognizing that neither waiting nor guessing was a viable enterprise-grade solution, the creator of SoFar sought a third architectural pillar: honesty combined with resilience.

The design criteria were strict:

  • Never throw: The parser must always return a usable state or gracefully degrade without crashing the UI thread.
  • Never invent: If a boolean value is mid-stream as "ok": tru, the parser must not guess that it evaluates to true; it must return the last known valid state ().
  • Zero Dependencies: The library must be feather-light, completely self-contained, and easily integrated into any frontend or backend JavaScript runtime.
  • Leverage Native Engines: Avoid writing a bespoke, secondary JSON parser from scratch. Native engines like V8’s JSON.parse() are hyper-optimized, fast, and secure. The challenge was simply figuring out how to feed them valid inputs derived from incomplete data.

Supporting Context & Metrics: Under the Hood of SoFar

To achieve high performance without bloating bundle sizes, SoFar bypasses heavy AST (Abstract Syntax Tree) generation, opting instead for a single-pass character scanner operating in $O(n)$ time complexity.

JSON.parse throws on every token your LLM streams. Here's a 425-byte fix.

How SoFar’s Scanning Mechanism Operates

The library scans the incoming text buffer from left to right, maintaining awareness of structural boundaries by tracking three primary lexical contexts: strings, keys, and nested containers (objects and arrays).

  1. Cut Points and Snapshots: As the scanner reads the buffer, it records "cut points"—moments where structural tokens safely open or close. At each cut point, the library stores a snapshot of the current stack state. Crucially, this is not a heavy deep copy; the stack is maintained as a string of closing brackets/braces required to balance the document at that exact moment.
  2. Attempt 1 (Graceful Closure): When a parse request is triggered, SoFar checks if the stream is currently terminated inside a string. If so, it closes the string, appends the necessary closing brackets for all open containers, and passes the synthesized string to JSON.parse(). This handles the vast majority of real-world streaming scenarios, such as strings cut mid-word, arrays cut mid-element, or objects cut mid-value.
  3. Attempt 2 (Backtracking): If Attempt 1 fails—such as when encountering a dangling key ("a": 1, "b), a trailing comma, or a half-typed literal like fals—the library walks backward through its recorded cut points. It slices the buffer at each historical point, appends the stored closing tokens for that specific index, and attempts a native parse. The first successful parse wins.
  4. Fallback: If all attempts fail to yield a valid structure, the function quietly returns undefined, ensuring application code never catches unexpected syntax errors.

Performance and Comparative Benchmarks

Because streaming data frames can grow large over prolonged LLM sessions, efficiency is paramount. Benchmarks show that on a 1.6 MB buffer, SoFar executes in roughly $1.3times$ the time required for a bare, native JSON.parse().

When compared to existing partial-parsing alternatives, SoFar distinguishes itself through its uncompromising minimalism and defensive error handling:

Input Buffer SoFar partial-json best-effort-json-parser jsonrepair
Bundle Size (gzipped) 425 B 1.6 kB 1.9 kB 3.7 kB
'' (Empty String) undefined Throws error "" (Empty string) Throws error
(Dangling Braces) undefined Throws error Returns "}" Throws error
{"ok": tru ok: true ok: true ok: "tru"
{"a":"x","ingr a: "x" a: "x" a: "x" a: "x", ingr: null

Note: While libraries like jsonrepair excel at fixing broken syntax (such as single quotes or unquoted keys in static legacy files), SoFar is purpose-built for the high-velocity, append-only nature of real-time AI token streams.


Code Architecture & API Design

SoFar exposes a remarkably concise API consisting of just two primary functions: parsePartialJSON for single-shot evaluations, and createJSONStream for stateful stream handling.

Basic Partial Parsing

import  parsePartialJSON  from "sofar-json";

// Mid-string token arrival
parsePartialJSON('{"title": "Pad Th');
// ➔   "Pad Th" 

// Mid-key token arrival
parsePartialJSON('{"title": "Pad Thai", "ingr');
// ➔   "Pad Thai" 

// Complete property, trailing comma
parsePartialJSON('{"title": "Pad Thai", "servings": 4,');
// ➔   "Pad Thai", servings: 4 

// Incomplete literal (no speculation)
parsePartialJSON('{"ok": tru');
// ➔ 

// Empty input handling
parsePartialJSON('');
// ➔ undefined

Stateful Streaming Implementation

For real-world LLM integrations, developers utilize the createJSONStream wrapper to manage chunk accumulation across asynchronous iterators:

import  createJSONStream  from "sofar-json";

const stream = createJSONStream();

for await (const chunk of llmResponse) 
  const value = stream.feed(chunk);

  // Only render if a new valid structural state has emerged
  if (value !== undefined) 
    renderUI(value);
  


// Perform a strict native parse once the complete stream finishes
const finalDocument = JSON.parse(stream.raw);

Future Outlook: The Evolution of Structured AI Generation

As artificial intelligence models evolve, the industry is increasingly moving away from unstructured natural language toward guaranteed structured outputs. Technologies like constrained decoding (supported by engines like llama.cpp, Guidance, and Outlines) allow developers to force LLMs to adhere strictly to JSON schemas at the token generation layer by masking invalid logits.

However, network-level streaming limitations, cloud API latencies, and browser-side UI rendering constraints mean that client-side partial parsing will remain a vital architectural pattern for the foreseeable future. Developers building responsive consumer applications cannot afford to wait for massive JSON payloads to fully download before updating progress bars, rendering recipe ingredients, or displaying multi-part data dashboards.

Tools like SoFar demonstrate the power of minimalist, highly focused open-source engineering. By solving one specific problem exceptionally well—translating chaotic streaming tokens into valid, non-hallucinated partial objects with a 425-byte footprint—it removes a persistent friction point in modern AI application development.

Developers looking to experiment with streaming structured data can install the library via npm:

npm install sofar-json

Additionally, the maintainer has published an interactive web-based playground allowing engineers to inspect how raw JSON tokens are scanned, cut, and parsed character-by-character in real time at acegikmo135.github.io/sofar, with full source code and fuzz-testing suites available on GitHub.

Did you find this story helpful?

Share it with your friends and colleagues on social media.

Share

Leave a Comment

Your email address will not be published. Required fields are marked *