LLM & Chat Streaming
Stream LLM responses in real time with ordered output segments, session history, and multi-turn conversations.
Overview
LLM (Large Language Model) requests on Wiro work differently from standard model runs:
- Responses are available as structured content in the
outputsarray (contenttype: "raw") and as merged text indebugoutput - Streaming
task_outputmessages contain ordered text/tool-callsegments, not plain strings - Multi-turn conversations are supported via
session_idanduser_idparameters pexitis the primary success indicator- The Direct LLM Gateway offers OpenAI Chat/Responses and Anthropic Messages on
llm.wiro.ai. Generic/syncwaits stay on Run a Model.
Available LLM models include:
- openai/gpt-5-2: GPT-5-2
- openai/gpt-oss-20b: GPT OSS 20B
- qwen/qwen3-5-27b: Qwen 3.5 27B
Session & Chat History
Wiro maintains conversation history per session. Send the same session_id together with user_id on every message of a conversation (some chat models keep history only when both are present), and the model builds on the earlier messages:
| Parameter | Type | Required | Description |
|---|---|---|---|
session_id |
string | No* | Non-empty conversation identifier (up to 256 UTF-8 bytes). Reuse the same ID for follow-up messages. |
user_id |
string | No | UUID identifying the user. |
prompt |
string | Yes | The user's message or question. |
// First message — start a new session
{
"prompt": "What is quantum computing?",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
// Follow-up — reuse the same session_id
{
"prompt": "Can you explain qubits in more detail?",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
session_id is optional on a first turn but required when continuing with previousTaskToken. Reuse the same session_id value on continuation. The value may contain up to 256 UTF-8 bytes; a UUID is allowed but not required.
Ordered Output Segments
LLM output is an ordered timeline with up to four segment types:
- Thinking: reasoning text exposed by the selected model
- Answer: the final response to the user
- Function call: JSON arguments for an application-defined function
- Custom tool call: grammar-constrained or free-form tool input
When streaming via WebSocket, task_output messages for LLM models contain a structured object (not a plain string):
// LLM task_output message format
{
"type": "task_output",
"id": "534574",
"message": {
"type": "progressGenerate",
"task": "Generate",
"speed": "12.4",
"speedType": "words/s",
"segments": [
{ "type": "thinking", "text": "Let me analyze this step by step..." },
{ "type": "answer", "text": "Quantum computing uses qubits that can exist in superposition..." }
],
"finishreason": "stop",
"usage": {
"input_tokens": 18,
"input_tokens_details": { "cached_tokens": 6 },
"output_tokens": 11,
"output_tokens_details": { "reasoning_tokens": 4 },
"total_tokens": 29
},
"isThinking": false,
"elapsedTime": "3s"
}
}
A model may alternate between thinking and answering multiple times during one response. The segments array preserves the exact order, including consecutive items of the same type:
// Ordered output segments
{
"segments": [
{ "type": "thinking", "text": "Let me break this into parts..." },
{ "type": "thinking", "text": "Now let me verify my reasoning..." },
{ "type": "answer", "text": "Quantum computing uses qubits..." },
{ "type": "thinking", "text": "I should clarify one detail..." },
{ "type": "answer", "text": "To summarize: qubits can be 0, 1, or..." }
]
}
Each task_output event contains the full accumulated segment snapshot, not only the latest delta. Replace your stored segment list with the newest array; do not append the complete array again. The active final item's text grows in place as chunks arrive, so render every snapshot before the response completes. A new item is appended only when the output phase changes. Render items in array order and use isThinking to show an active reasoning indicator.
| Field | Type | Description |
|---|---|---|
message.type |
string |
Progress payload type; currently "progressGenerate". |
message.segments |
array |
Full ordered text/tool-call snapshot. Replace the prior array rather than appending it. |
message.finishreason |
string |
Final reason: stop, tool_calls, length, content_filter, or error. Usually absent until the turn finishes. |
message.usage |
object |
Final normalized token/server-tool counters. Detail counters are already included in input/output totals. |
message.isThinking |
boolean |
Whether the model is currently in a thinking phase. |
message.speed |
string |
Generation speed (e.g. "12.4"). |
message.speedType |
string |
Speed unit (e.g. "words/s"). |
message.elapsedTime |
string |
Elapsed time since generation started (e.g. "3s", "1m 5s"). |
Note: Standard models may send message as a progress object or plain string. For LLM rendering, first verify that message.segments is an array.
Tool entries carry stable id, call_id, name, and status. function_call uses a JSON arguments string; custom_tool_call uses input. Execute only a completed call from a final turn whose finishreason is tool_calls. Earlier snapshots may contain in_progress or incomplete calls and must not trigger execution.
Streaming Flow
The complete flow for streaming an LLM response:
- Run the model with
prompt,session_id, anduser_id - Connect to WebSocket and send
task_info - Receive
task_outputmessages containing cumulative orderedsegments - Replace your stored snapshot and render
answeritems; optionally showthinkingitems and collect tool calls - Complete: on
task_postprocess_end, checkexitCodeon WebSocket orpexitin Task Detail, then inspectfinishreason
Polling Alternative
If you don't need real-time streaming, you can poll POST /Task/Detail instead. The response includes both debugoutput (merged plain text) and ordered segments in the structured outputs entry:
{
"result": true,
"tasklist": [{
"status": "task_postprocess_end",
"pexit": "0",
"debugoutput": "Quantum computing uses qubits that can exist in superposition...",
"outputs": [{
"contenttype": "raw",
"content": {
"prompt": "What is quantum computing?",
"raw": "Quantum computing uses qubits that can exist in superposition...",
"segments": [
{
"type": "answer",
"text": "Quantum computing uses qubits that can exist in superposition..."
}
],
"finishreason": "stop",
"usage": {
"input_tokens": 18,
"output_tokens": 11,
"total_tokens": 29
}
}
}]
}]
}
Note: Read outputs[].content.segments for structured output. debugoutput contains merged plain text. For real-time delivery, use WebSocket instead.
Completions Alternative
For a single HTTP connection instead of manual Run plus WebSocket setup:
- Use
POST /v1/Run/{owner}/{project}/sync?stream=trueonhttps://api.wiro.ai/v1for generic task-event SSE. See Run a Model. - Use the Direct LLM Gateway on
https://llm.wiro.aifor OpenAI Chat chunks ending in[DONE], Responses named events, or Anthropic events ending inmessage_stop.