Inference API

Streaming

Receive the response token by token as it is generated, instead of waiting for the full reply.

Streaming and non-streaming requests take the same wall-clock time to finish, the difference is when you start seeing output. For anything user-facing, streaming turns seconds of blank waiting into immediate feedback, and for reasoning models it lets you show the thinking phase while the final answer is still forming. Set stream: true:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://inference.pearlresearch.ai/v1",
    api_key=os.environ.get("PEARL_API_KEY"),
)

stream = client.chat.completions.create(
    model="zai-org/GLM-5.2",
    messages=[{"role": "user", "content": "Write a haiku about tide pools"}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

What's on the wire

A streamed response is server-sent events: data: lines, each carrying a JSON chunk, terminated by a literal data: [DONE]. The SDKs parse this for you; it only matters when you consume the HTTP stream yourself:

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Low"}}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" tide"}}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]
  • Each chunk carries a delta, the increment since the previous chunk: content text, reasoning on reasoning models, or tool_calls fragments when the model is composing a tool call.
  • The final content chunk carries finish_reason (stop, length, or tool_calls); concatenating every delta reproduces exactly the message a non-streamed call would have returned.

Streaming a reasoning model

With a reasoning model (GLM-5.2, DeepSeek V4 Pro, or V4 Flash), read both delta fields, thinking arrives in reasoning before the answer arrives in content:

for chunk in stream:
    if not chunk.choices:
        continue
    delta = chunk.choices[0].delta

    # Reasoning models interleave two delta fields: thinking first, then the answer.
    reasoning = getattr(delta, "reasoning", None)
    if reasoning:
        print(reasoning, end="", flush=True)          # e.g. render dimmed
    if delta.content:
        print(delta.content, end="", flush=True)      # the actual reply

Detecting incomplete streams

Once streaming begins, the HTTP status is already sent, a mid-generation failure ends the stream rather than delivering an error object. Treat a stream that closes without a finish_reason (or, on the raw wire, without [DONE]) as an incomplete response: keep the partial text if partial output is useful, otherwise retry the request.

Set max_tokens deliberately and watch for finish_reason: "length", a truncated stream looks exactly like a finished one to a user unless you check.

Streaming, Pearl Inference Docs