Streaming
Stream tokens, reasoning, and usage as they are generated, on every route.
Set stream: true and XField sends the response as server-sent events while the model generates it. Every route streams, every model streams, and the event format is the one your SDK already expects. For anything interactive, and for long reasoning runs, streaming is the right default.
Event format
Each protocol keeps its own native stream shape.
| Route | What you receive | How it ends |
|---|---|---|
/chat/completions | data: lines with chat.completion.chunk objects | data: [DONE] |
/responses | Typed events such as response.output_text.delta | response.completed |
/messages | Anthropic events: message_start, content_block_delta, message_delta | message_stop |
A Chat Completions stream looks like this on the wire:
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"reasoning":"The user wants"}}]}
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"SameSite"}}]}
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":412,"completion_tokens":233,"prompt_tokens_details":{"cached_tokens":384}}}
data: [DONE]Usage in the last chunk
On Chat Completions, usage arrives in a final chunk just before data: [DONE]. That chunk has an empty choices array, so guard for it in your loop. You do not need to set stream_options.include_usage; XField asks for usage on every stream.
On Messages, usage arrives in message_start and message_delta. On Responses, it is on the response.completed event.
Reading reasoning deltas
When a model thinks, the thinking streams before the answer.
- Chat Completions: reasoning text arrives in
delta.reasoning, answer text indelta.content. - Responses: reasoning comes as reasoning item events, followed by the output text events.
- Messages: a
thinkingcontent block streamsthinking_deltaevents, then atextblock streamstext_deltaevents.
If you only want the answer, ignore the reasoning deltas, or turn reasoning off or hide it. See Reasoning.
Client examples
import os
from openai import OpenAI
client = OpenAI(base_url="https://xfield.cloud.brutal.ai/api/v1", api_key=os.environ["XFIELD_API_KEY"])
stream = client.chat.completions.create(
model="deepseek-v4.1-flash-1017-uncensored",
messages=[{"role": "user", "content": "Summarize the OWASP Top 10 in one line each."}],
stream=True,
)
for chunk in stream:
if chunk.usage:
print(f"\n\n{chunk.usage.completion_tokens} output tokens")
if not chunk.choices:
continue
delta = chunk.choices[0].delta
thinking = getattr(delta, "reasoning", None)
if thinking:
print(thinking, end="", flush=True) # or send it to a separate pane
if delta.content:
print(delta.content, end="", flush=True)The Anthropic SDKs stream with their usual helpers, for example client.messages.stream(...) in Python, pointed at https://xfield.cloud.brutal.ai/api.
Cancelling
To stop a generation, close the connection. In the SDKs, break out of the loop and close the stream, or abort the request with an AbortController in TypeScript. XField stops the model when you disconnect. Usage is recorded for what was generated up to that point.