I built a few tools to let you parse the streaming JSON and start consuming it safely before the stream is completed.
will work on the server or in the browser
supports all valid JSON, deeply nested multi-dimensional, whatever.
every key within the data structure will stream independently - and the entire model will be stubbed out based on the schema - so it is safe to parse asap and during the stream - u can also provide defaults for any property to be set before the value streams in
ready to use tools for typescript here:
zod-stream on npm
stream-hooks on npm
and the streaming json parser that powers them is schema-stream on npm
Browser/Next.js example:
create the stream completion - can use my helpers here - this just converts the zod schema to the proper JSON schema and tool function parameters - then converts the SSE response to a readable stream.
import OpenAI from "openai"
import { z } from "zod"
import { withResponseModel, OAIStream } from "zod-stream"
const oai = new OpenAI({
apiKey: process.env["OPENAI_API_KEY"] ?? undefined,
organization: process.env["OPENAI_ORG_ID"] ?? undefined
})
export async function POST(request: Request) {
const { messages } = await request.json()
const params = withResponseModel({
response_model: {
schema: z.object({ content: z.string() }),
name: "Content response"
},
params: {
messages,
model: "gpt-4",
stream: true
},
mode: "TOOLS"
})
const extractionStream = await oai.chat.completions.create(params)
return new Response(
OAIStream({
res: extractionStream
})
)
}
simple react hook consumer
import { useJsonStream } from "stream-hooks"
const { loading, startStream, stopStream, data } = useJsonStream({
schema: z.object({
content: z.string()
}),
onReceive: data => {
console.log("incremental update to final response model", data)
console.log(data.content) // this json stream is now fully safe to parse and read from before it has completed
}
})
vanilla consumer
import ZodStream from "zod-stream"
const streamClient = new ZodStream()
const extractionStream = await streamClient.current.create({
completionPromise: async () => await fetch("/path/to/stream")
response_model: { schema } // should match schema of stream
})
for await (const data of extractionStream) {
console.log(data.content) // safe to parse partial json
}