Streaming examples

Purpose and Scope

Streaming lets an application start reacting before a model response has fully completed. In this SDK, the most important reader task is choosing the right level of abstraction: use the Responses stream helper when building new model interactions, use event iteration when you need direct access to typed stream events, and understand the lower-level SSE parser behavior when debugging transport issues. The README positions the Responses API as the primary model interaction surface, while Chat Completions remains supported as the previous standard, so examples should usually begin with Responses and treat older chat workflows as compatibility paths.

Sources: README.md, tests/streaming.test.ts, tests/lib/ResponseStream.test.ts

The official streaming guidance describes HTTP streaming over server-sent events, where a request asks the API to emit semantic events as generation progresses. The SDK mirrors that model in JavaScript: a stream can be consumed incrementally, and event names distinguish lifecycle updates from text deltas and completion events. The test suite gives a practical contract for this behavior. One group verifies raw server-sent event decoding, while another verifies the higher-level Responses stream helper that accumulates snapshots and returns a final response object with convenience fields.

Sources: tests/streaming.test.ts, tests/lib/ResponseStream.test.ts

Relevant Source Files

  • README.md — Introduces the SDK, shows the OpenAI client construction pattern, identifies Responses as the primary API, and shows the supported Chat Completions surface for older message-based workflows.
  • tests/streaming.test.ts — Exercises the internal SSE message iterator with event fields, missing event names, empty data, multiple events, and multiple data lines.
  • tests/lib/ResponseStream.test.ts — Verifies the Responses stream helper, text delta snapshots, final response accumulation, reasoning output accumulation, and the SDK-only output text convenience field.

Core Streaming Primitives

The first primitive is the OpenAI client. The README shows constructing a client with the API key coming from the environment and then calling model resources from that client. The second primitive is the Responses API, which accepts a model, instructions, and input, and returns output through response items. The third primitive is the stream object returned by the Responses stream helper. Tests show that this stream supports event subscription for a text delta event and a final response method that resolves after the stream has been accumulated into a regular response-shaped object.

Sources: README.md, tests/lib/ResponseStream.test.ts

The lower-level primitive is the server-sent event message iterator. The streaming decoder tests import the iterator from the SDK core streaming module and feed it a Response built from a readable stream. Each yielded message has an event name that may be present or null and a data string that may be empty or contain JSON. Most application code should not need to call this internal iterator directly, but knowing its behavior is useful when diagnosing malformed streams, proxy buffering, missing blank-line separators, or unexpected event names in integration tests.

Sources: tests/streaming.test.ts

Responses Stream Flow

A practical Responses streaming flow has three phases. First, create a stream request with the desired model and input. Second, subscribe to events that matter to the UI or worker process, such as text delta events for incremental rendering. Third, await the final response when the stream is done so downstream code can use the same response shape it would receive from a non-streaming request. The Responses stream test demonstrates this pattern by collecting text snapshots from the delta event and then asserting that the final response contains the completed assistant message.

Sources: tests/lib/ResponseStream.test.ts

import OpenAI from 'openai';
 
const client = new OpenAI();
 
const stream = client.responses
  .stream({
    model: 'gpt-4o-2024-08-06',
    input: 'Say hello world',
  })
  .on('response.output_text.delta', (event) => {
    process.stdout.write(event.snapshot);
  });
 
const final = await stream.finalResponse();
console.log(final.output_text);

The most important detail in the Responses helper is that deltas and final state are both useful, but they serve different purposes. Delta events are ideal for progress indicators, terminal output, chat bubbles, and partial processing. The final response is the durable result that should be logged, stored, or passed to the next workflow step. Tests assert that the final object has response object shape, includes a message output item, and exposes output text even though that convenience field is not present in the raw stream payload.

Sources: tests/lib/ResponseStream.test.ts

Reasoning and Output Accumulation

Streaming is not limited to simple text. The Responses stream test suite includes a reasoning scenario where the final response contains a reasoning output item followed by an assistant message item. This matters for applications that use reasoning models or tools, because a stream may contain several kinds of output before the user-visible message is complete. The final response method is responsible for accumulating those pieces in order, preserving reasoning text and the eventual assistant text. Consumers should avoid assuming that the first output item is always a message.

Sources: tests/lib/ResponseStream.test.ts

const stream = client.responses.stream({
  model: 'o3',
  input: 'Compute 6 * 7',
  reasoning: { effort: 'medium' },
});
 
const final = await stream.finalResponse();
for (const item of final.output) {
  if (item.type === 'message') {
    console.log(item.content);
  }
}

Chat Completions and Compatibility Patterns

The README shows Chat Completions as the previous standard for generating text and states that it remains supported. For new work, prefer Responses because it is the primary API surface and aligns with the official migration guidance. When maintaining a chat workflow, keep the same client construction and message-array shape shown in the README, then use streaming only where the endpoint supports incremental delivery. Conceptually, the event-processing concerns are the same: handle partial output for responsiveness, then keep a completed result for persistence, retries, or follow-up requests.

Sources: README.md

const completion = await client.chat.completions.create({
  model: 'gpt-5.5',
  messages: [
    { role: 'developer', content: 'Talk like a pirate.' },
    { role: 'user', content: 'Are semicolons optional in JavaScript?' },
  ],
});
 
console.log(completion.choices[0].message.content);

SSE Parser Behavior and Edge Cases

The raw streaming tests define several edge cases that are worth mirroring in application-level expectations. A server-sent event can include an event name and JSON data, data without an event name, or an event without data. Multiple events may arrive from one response body, and each event is completed by a blank line. Data can also be split across multiple lines, including empty data lines, before being joined into the message payload. These cases explain why consumers should iterate stream events instead of manually splitting buffers by newline in application code.

Sources: tests/streaming.test.ts

for await (const event of stream) {
  if (event.type === 'response.output_text.delta') {
    process.stdout.write(event.delta);
  }
}

The parser tests are especially useful for troubleshooting infrastructure. If a reverse proxy buffers the response, the application may receive fewer visible updates even though the final response succeeds. If a custom fetch implementation or runtime shim mishandles readable streams, the iterator may not see the blank-line boundaries needed to finish events. If a diagnostic log shows data with no event name, that is still a valid shape in the parser contract. Treat the SDK stream APIs as the stable boundary and reserve raw SSE assumptions for tests and debugging.

Sources: tests/streaming.test.ts

Next Steps

Start new streaming features with the Responses stream helper, subscribe only to the events your interface needs, and await the final response before saving state. If the workflow is message-based and already uses Chat Completions, compare it with the README’s Responses example before adding more compatibility code. For deeper implementation checks, read the stream helper tests for accumulation behavior and the SSE decoding tests for transport-level edge cases. Related pages to read next are Responses API concepts, Streaming and events, Chat Completions, Errors retries and timeouts, and Platforms and runtimes.

Sources: README.md, tests/streaming.test.ts, tests/lib/ResponseStream.test.ts