Streaming

The same endpoint, one token at a time.

Add "stream": true to a chat completions request and the response changes from one JSON object to a sequence of them, arriving as the model writes — the same server-sent-event format an OpenAI client already knows how to read.

POST/v1/chat/completions

The frame format

Every event is one line starting with data: , holding one JSON object, followed by a blank line. The stream ends with a line that is the literal text [DONE] — not JSON, and not part of any chunk's shape:

data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{"content":"One"},"finish_reason":null}]}

data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{"content":", two"},"finish_reason":null}]}

data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

When reasoning_effort isn't "none" — see the chat reference before you rely on this — the same chunks can carry a reasoning_content delta instead of (or before) content, so a client that only reads content sees nothing until the model is done thinking, if it finishes at all.

Reconstructing the message

The full message is every chunk's delta.content concatenated in order. The first chunk usually carries delta.role; the last carries a non-null finish_reason and an empty delta — that's your signal the answer is complete, one event before [DONE] closes the connection.

Streamed chunks never carry a `usage` field. If your application needs the exact token count for a reply, make the same request without stream and read usage from the single response, or reconcile it afterward against the billing API.

Aborting

There's no cancel endpoint — a stream stops when you stop reading it. The server notices the connection is gone on its next write and stops generating; nothing about it is billed differently for having been cut short.

JavaScript
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // give up after 5s

const stream = await client.chat.completions.create(
  { model: "larsa-general", messages, stream: true },
  { signal: controller.signal },
);
Python
stream = client.chat.completions.create(
    model="larsa-general", messages=messages, stream=True,
)

for chunk in stream:
    ...
    if enough_already:
        stream.close()  # closes the underlying connection
        break

Talking plain HTTP, it's the same idea in whatever your language calls it: cancel the request's context in Go, drop the response in Rust, close the InputStream in Java, dispose the response message in C#, return the wrong byte count from CURLOPT_WRITEFUNCTION in PHP, or just break out of the read_body block in Ruby.

Full example

curl https://api.console.larsa.larsima.com/v1/chat/completions \
  -N \
  -H "Authorization: Bearer $LARSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "larsa-general",
    "stream": true,
    "messages": [
      {"role": "user", "content": "Count from one to five."}
    ],
    "reasoning_effort": "none"
  }'

See also

Navigate Open esc Close