OpenAI Node SDK streaming chat completions, tested with a mocked SSE fetch: working versions (tested 2026-10-15)
Stream chat completion deltas with the OpenAI SDK (for await over chunks), accumulate text and usage, and handle aborts, tested offline with a fake fetch that returns an SSE body.
passed2 passed · 0 failed2026-10-15 13:32 UTC- Status
- passed
- Last passed
- 2026-10-15 13:32 UTC
- Tests
- 2 / 2
- Language
- TypeScript
- Runtime
- node v22.23.3
npm install --no-audit --no-fund --ignore-scripts=false npm test
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38742087302
The code
Exactly the files that passed. Your agent can pull them with get_recipe("openai-streaming-mock") (install), or see them on GitHub.
{
"name": "openai-streaming-mock",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"openai": "7.34.0"
},
"devDependencies": {
"tsx": "4.23.15",
"@types/node": "24.19.2"
}
}
import OpenAI from "openai";
export async function streamAnswer(client: OpenAI, prompt: string, onToken: (t: string) => void, signal?: AbortSignal) {
const stream = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
stream: true,
stream_options: { include_usage: true },
}, { signal });
let text = "", usage: OpenAI.CompletionUsage | undefined;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) { text += delta; onToken(delta); }
if (chunk.usage) usage = chunk.usage; // final chunk has empty choices and usage
}
return { text, usage };
}
import { test } from "node:test";
import assert from "node:assert/strict";
import OpenAI from "openai";
import { streamAnswer } from "../src/stream.ts";
const chunk = (o: object) => `data: ${JSON.stringify({ id: "c", object: "chat.completion.chunk", created: 0, model: "gpt-4o-mini", ...o })}\n\n`;
function sseClient(events: string[], seen: any[] = []) {
const fetch = async (_u: any, init: any) => {
seen.push(JSON.parse(init.body));
const body = new ReadableStream({ start(c) { for (const e of events) c.enqueue(new TextEncoder().encode(e)); c.close(); } });
return new Response(body, { status: 200, headers: { "content-type": "text/event-stream" } });
};
return new OpenAI({ apiKey: "test", baseURL: "http://mock.local/v1", fetch: fetch as any, maxRetries: 0 });
}
test("accumulates deltas and usage", async () => {
const seen: any[] = [];
const tokens: string[] = [];
const client = sseClient([
chunk({ choices: [{ index: 0, delta: { role: "assistant", content: "" }, finish_reason: null }] }),
chunk({ choices: [{ index: 0, delta: { content: "Hel" }, finish_reason: null }] }),
chunk({ choices: [{ index: 0, delta: { content: "lo" }, finish_reason: "stop" }] }),
chunk({ choices: [], usage: { prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 } }),
"data: [DONE]\n\n",
], seen);
const r = await streamAnswer(client, "hi", (t) => tokens.push(t));
assert.equal(r.text, "Hello");
assert.deepEqual(tokens, ["Hel", "lo"]);
assert.equal(r.usage?.total_tokens, 5);
assert.equal(seen[0].stream, true);
assert.deepEqual(seen[0].stream_options, { include_usage: true });
});
test("abort rejects", async () => {
const ac = new AbortController();
ac.abort();
await assert.rejects(streamAnswer(sseClient([]), "hi", () => {}, ac.signal));
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
OpenAI Node SDK tool calling + Zod, tested with a mocked fetch
Run an OpenAI chat tool-calling loop with Zod-validated tool arguments, tested offline by injecting a fake fetch into the official SDK.
OpenAI chat.completions.parse with Pydantic output + respx mocks
Get typed output from OpenAI chat.completions.parse using a Pydantic response_format, handle refusals and schema mismatches, and test offline with respx returning canned JSON.
OpenAI Python SDK structured outputs, tested with respx
Call the OpenAI Python SDK and validate JSON output with Pydantic, testing success, malformed output and API errors offline by mocking HTTP with respx.