Guides
Streaming
Set stream to true to receive the answer as it is written, as newline-delimited JSON events.
#Turn on streaming
Send the same request with "stream": true. The response has the content type application/x-ndjson: one JSON object per line, sent as Praus works.
curl -N https://praus.ai/api/chat/compound-pipeline-sync \
-H "Content-Type: application/json" \
-H "X-Api-Key: $PRAUS_API_KEY" \
-d '{
"message": "Write a two-sentence welcome for new customers.",
"queryProfile": "ask",
"searchEnabled": false,
"stream": true,
"sessionOnly": true,
"userMemory": { "memories": [], "customInstructions": "" }
}'import json
import os
import requests
response = requests.post(
"https://praus.ai/api/chat/compound-pipeline-sync",
headers={"Content-Type": "application/json", "X-Api-Key": os.environ["PRAUS_API_KEY"]},
json={
"message": "Write a two-sentence welcome for new customers.",
"queryProfile": "ask",
"searchEnabled": False,
"stream": True,
"sessionOnly": True,
"userMemory": {"memories": [], "customInstructions": ""},
},
stream=True,
timeout=120,
)
if not response.ok:
error = response.json()
raise RuntimeError(f"{response.status_code} {error.get('code')}: {error.get('message')}")
draft, final = "", None
for line in response.iter_lines(decode_unicode=True): # yields one complete line at a time
if not line:
continue
event = json.loads(line)
kind = event.get("type")
if kind == "token":
draft += event.get("content", "")
print(event.get("content", ""), end="", flush=True)
elif kind == "reset":
draft = "" # discard the provisional text
elif kind == "error":
raise RuntimeError(f"{event.get('code')}: {event.get('message')}")
elif kind == "done":
final = event["result"]["content"]
# other event types, such as "status", can be shown or ignored
if final is None:
raise RuntimeError("Stream ended before it finished. Treat the answer as incomplete.")
print("\n\nFinal answer:", final)// Node.js 18 or later. Run on your server.
const response = await fetch("https://praus.ai/api/chat/compound-pipeline-sync", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Api-Key": process.env.PRAUS_API_KEY },
body: JSON.stringify({
message: "Write a two-sentence welcome for new customers.",
queryProfile: "ask",
searchEnabled: false,
stream: true,
sessionOnly: true,
userMemory: { memories: [], customInstructions: "" },
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`${response.status} ${error.code}: ${error.message}`);
}
let buffer = "", draft = "", final = null;
function handle(event) {
if (event.type === "token") draft += event.content ?? "";
else if (event.type === "reset") draft = "";
else if (event.type === "error") throw new Error(`${event.code}: ${event.message}`);
else if (event.type === "done") final = event.result.content;
// other event types, such as "status", can be shown or ignored
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let newline;
while ((newline = buffer.indexOf("\n")) >= 0) { // only parse complete lines
const line = buffer.slice(0, newline).trim();
buffer = buffer.slice(newline + 1);
if (line) handle(JSON.parse(line));
}
}
buffer += decoder.decode();
if (buffer.trim()) handle(JSON.parse(buffer.trim()));
if (final === null) throw new Error("Stream ended before it finished. Treat the answer as incomplete.");
console.log(final);#What a stream looks like
{"type":"status","label":"Working"}
{"type":"token","content":"Hello"}
{"type":"token","content":" there."}
{"type":"done","result":{"content":"Hello there."}}#Event types
| Type | Fields | What to do |
|---|---|---|
status | label | Progress, such as Working. Show it or ignore it. |
token | content | A piece of the answer. Append it to the text you show. |
reset | None | Clear the provisional answer text you have shown so far. |
done | result.content | The final answer. Use it as the answer of record. |
error | code, message | The request failed, even though the HTTP status was 200. |
{"type":"reset"}
{"type":"error","code":"SERVICE_UNAVAILABLE","message":"Please retry shortly."}Other event types may appear. Ignore any type you don’t recognize.
#Rules for a reliable client
- Buffer until you have a full line. Network chunks can split a JSON object anywhere. Parse only complete lines.
- Append each
token.contentto the text you are showing. - On
reset, clear the provisional text and keep reading. - Treat
done.result.contentas final. It can differ from the tokens you joined, so replace your draft with it. - Treat
erroras a failure. The HTTP status was already 200 when the stream began, so check every event. - A stream that ends without
doneis incomplete. Don’t present it as a finished answer.
#Errors before the stream starts
Problems found before any events are sent, such as a missing key or a request over your limits, return a normal non-streaming error with an HTTP error status and a JSON body. Check the status before you start reading lines. See error codes.