Skip to content

OpenAI

Use GistUI with OpenAI's Chat Completions API, with plain fetch or the official openai SDK.

Updated View as Markdown

Send prompt().text as the system message, ask for a stream, and pass the text deltas to the browser.

npm i @gistui/catalog @gistui/server

With fetch

api/answer.tsts
import { prompt } from "@gistui/catalog";
import { textDeltas, toReadable } from "@gistui/server";

export async function POST(req: Request) {
  const { messages } = await req.json();

  const res = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "gpt-5",
      stream: true,
      messages: [{ role: "system", content: prompt().text }, ...messages],
    }),
  });

  // A failed request has no stream to read: answer with its status instead.
  if (!res.ok) return new Response(await res.text(), { status: res.status });
  return new Response(toReadable(textDeltas(res, "openai")));
}

With the openai SDK

The SDK gives you chunks; yield their text and wrap the generator with toReadable.

api/answer.tsts
import OpenAI from "openai";
import { prompt } from "@gistui/catalog";
import { toReadable } from "@gistui/server";

const client = new OpenAI();

export async function POST(req: Request) {
  const { messages } = await req.json();

  const stream = await client.chat.completions.create({
    model: "gpt-5",
    stream: true,
    messages: [{ role: "system", content: prompt().text }, ...messages],
  });

  async function* text() {
    for await (const chunk of stream) {
      const delta = chunk.choices[0]?.delta?.content;
      if (delta) yield delta;
    }
  }
  return new Response(toReadable(text()));
}

Render it

import "@gistui/styles/styles.css";
import { GistUI } from "@gistui/react";
import { ui } from "@gistui/react/ui";

const res = await fetch("/api/answer", { method: "POST", body: JSON.stringify({ messages }) });
<GistUI library={ui} stream={res.body} onAction={handle} />;

The same works in Vue, Svelte, Solid and vanilla JavaScript.

Validate and repair on the server

To log or store the repaired program, pass the text through repairStream. It forwards the text as it arrives and appends repairs at the end.

import { library } from "@gistui/catalog";
import { repairStream, textDeltas, toReadable } from "@gistui/server";

return new Response(toReadable(repairStream(textDeltas(res, "openai"), library)));

In a chat, without your own route

For a prototype, @gistui/chat has an adapter that calls OpenAI directly. A key in the browser is readable by every visitor, so use this for local demos only; in production, point url at your own endpoint.

import { createChat, openai } from "@gistui/chat";

const store = createChat({
  adapter: openai({ model: "gpt-5", url: "/api/chat" }), // your endpoint, OpenAI's request and response shape
  system: chatPrompt, // prompt({ mode: "inline" }).text, sent by your server in production
});
Navigation

Type to search…

↑↓ navigate↵ selectEsc close