Skip to content

ykachala/streamschema

Repository files navigation

streamschema

Stream typed, validated partial objects out of an LLM token stream. Render structured output as it arrives.

npm status

Why this exists

When you ask an LLM for JSON, the user waits for the whole object before anything renders. Existing partial-JSON parsers (partial-json-parser, parse-json-stream) fix the parsing half — they hand you a half-built unknown. They don't tell you which fields are safe to use yet, and they don't give you types.

streamschema is the typed layer on top: you give it a Zod schema, it consumes the token stream, and it yields a sequence of progressively-complete, typed snapshots — each one telling you exactly which fields are settled, which are still streaming, and which are missing. So your UI can paint title the instant it closes, while body is still arriving.

What it does

  • Schema-driven — define the shape with Zod; every yielded snapshot is typed as a deep-partial of your schema. No casting any.
  • Field-completion tracking — each snapshot carries a status map: complete | streaming | pending per field, so the UI knows what's safe to commit.
  • Tolerant parsing — handles unterminated strings, trailing commas, and markdown code fences mid-stream; never throws on an incomplete chunk.
  • Backpressure-friendly — an async iterable that works with Node streams, ReadableStream, Bun, Deno, and Cloudflare Workers.
  • Final validation — when the stream ends, runs full Zod validation so you get a real, parsed, guaranteed-valid object (or a typed error).

Install

npm install streamschema zod

Quick start

import { streamSchema } from "streamschema";
import { z } from "zod";

const Recipe = z.object({
  title: z.string(),
  steps: z.array(z.string()),
});

const stream = streamSchema(Recipe, llmTokenStream); // llmTokenStream: AsyncIterable<string>

for await (const snapshot of stream) {
  // snapshot.value: DeepPartial<Recipe>, snapshot.status: per-field completion
  if (snapshot.status.title === "complete") render(snapshot.value.title);
  renderSteps(snapshot.value.steps ?? []); // grows as each step closes
}

const recipe = await stream.final(); // fully validated Recipe — or throws typed ValidationError

Roadmap

Roadmap: tolerant tokenizer → schema projection + status → final validation → React useStreamSchema hook.

License

MIT

About

Stream typed, validated partial objects out of an LLM token stream — render structured output as it arrives, with per-field completion status.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors