Back to browse
GitHub Repository

An append-only streaming JSON parser. Parse incomplete JSON as it streams in - data is never removed or mutated, only extended.

10 starsTypeScript

Jsiphon – Streaming JSON parser with delta tracking and ambiguity trees

by sugeul·Feb 16, 2026·1 point·0 comments

AI Analysis

●●●BangerWizardryBig BrainZero to One

Append-only parsing with delta-only DOM updates solves the 'wait for complete JSON' problem cleanly.

Strengths
  • Append-only semantics + delta tracking eliminates wasteful re-renders on each LLM token—proven UX win
  • Ambiguity tree tracks which fields are 'finalized' vs. still in flux, enabling safe early partial returns
  • Genuinely novel framing of streaming JSON as a state machine; handles edge cases (preamble junk, trailing text)
Weaknesses
  • Narrow use case: primarily solves LLM JSON streaming, less useful for generic streaming APIs
  • No published benchmarks or real-world production examples in README; adoption proof needed
Target Audience

Frontend developers building real-time LLM chat UIs, streaming API consumers

Post Description

Hi HN, I built Jsiphon to solve a common frustration with LLM streaming: you ask for structured JSON output, but can't use any of it until the entire stream finishes.

If you've used JSON mode (OpenAI, Anthropic, etc.), you've hit this — you want {"answer": "...", "sources": [...]}, but JSON.parse() fails on every incomplete chunk.

LLM responses are inherently append-only (tokens arrive left to right, never go back), so Jsiphon leans into that with three ideas:

1) Append-only parsing — Feed in {"msg": "Hel and get {msg: "Hel"} immediately. Values are only extended, never removed or mutated.

2) Delta tracking — Each snapshot contains only what's new. For a chat bubble, just append delta.content to the DOM — when the LLM produces next chunk "lo, World!", we immediately get {msg: "lo, World!"}. No need to repeat partial JSON parsing or full tree rerendering.

3) Ambiguity tree — A tree that mirrors the shape of your data and tracks which subtrees are finalized at every depth. For example, if you're streaming {"header": {"title": "...", "date": "..."}, "body": "..."}, you can check isAmbiguous(ambiguous.header.title) to use the title the moment it's done, even while header.date and body are still streaming. This isn't a flat "is the whole thing done?" flag — it's per-node stability tracking that propagates up, so isAmbiguous(ambiguous.header) turns false only when all of header's children are finalized.

Existing partial JSON parsers like partial-json and gjp-4-gpt do a great job at the core parsing problem — turning broken JSON into usable objects. Jsiphon builds on that foundation and takes it one step further: instead of just parsing, it gives you a streaming data pipeline where append-only snapshots, per-field deltas, and multi-depth ambiguity tracking all come out of a single async iteration. If you've been using partial-json and wished you knew which fields were done vs still streaming without polling the whole object, that's exactly the gap this fills.

Zero dependencies, never throws on invalid input, handles junk text before/after the JSON root (which LLMs sometimes produce).

GitHub: https://github.com/webtoon-today/jsiphon npm install jsiphon

Would love feedback on the API design — especially the ambiguity tree. Tracking per-node stability across arbitrary nesting depth was the trickiest part. Curious if anyone sees a cleaner approach.

Disclosure: I'm a native Korean speaker. I used Claude to help structure and translate this post into English. The ideas and code are mine.

Similar Projects