Stream AI responses with SSE

Consume the POST /api/chat/stream response, assemble deltas and handle terminal events.

Updated 29 July 2026Local API · v1 contract

How the stream behaves

POST /api/chat/stream returns a Server-Sent Events response with Cache-Control: no-cache and X-Accel-Buffering: no. The server yields one data line for each provider text piece, then writes the completed response to local history before sending the terminal event.

Event shapes

EventJSON payloadClient action
delta{"delta":"next text piece"}Append delta to the visible answer
done{"done":true,"text":"full answer","sessionId":"...","autocopy":{...}}Treat the answer as complete and persist the returned session ID
error{"error":"provider message"}Stop rendering, show a recoverable error and decide whether to retry

Inspect the wire format

Shell
curl --no-buffer -sS \n  -X POST "$BASE_URL/api/chat/stream" \n  -H "X-OpenSS-Session: $SESSION_TOKEN" \n  -H "Content-Type: application/json" \n  -d "{\"message\":\"Explain the selected text\",\"source\":\"manual\"}"\n\n# data: {\"delta\":\"First\"}\n#\n# data: {\"delta\":\" answer\"}\n#\n# data: {\"done\":true,\"text\":\"First answer\",\"sessionId\":\"...\",\"autocopy\":{...}}

Parse the stream in Python

Python
import json
import requests

with requests.post(
    base_url + "/api/chat/stream",
    headers={"X-OpenSS-Session": session_token},
    json={"message": "Explain the selected text", "source": "manual"},
    stream=True,
    timeout=120,
) as response:
    response.raise_for_status()
    for line in response.iter_lines(decode_unicode=True):
        if not line or not line.startswith("data: "):
            continue
        event = json.loads(line.removeprefix("data: "))
        if "delta" in event:
            print(event["delta"], end="", flush=True)
        elif event.get("done"):
            session_id = event["sessionId"]
            print()
        elif "error" in event:
            raise RuntimeError(event["error"])

Streaming a screenshot analysis

Include screenshotId and set source to capture. The server reads the temporary capture, combines the image with local OCR and the instruction, then streams provider output. OCR is supporting context; the model is instructed to use the screenshot as the primary visual source.

JSON
{
  "source": "capture",
  "screenshotId": "capture-id-from-/api/screenshots/capture",
  "message": "Identify the key figures and list the next actions.",
  "sessionId": null,
  "modelTier": "mini"
}

Retries and cancellation

  • Do not retry after a done event; the answer has already been written to local history.
  • A stream-level error is emitted as a data event rather than an HTTP error after streaming begins. Surface its text and allow a deliberate retry.
  • POST /api/chat/cancel acknowledges a request ID, but a client should still stop reading and close its response stream.
  • Keep the screenshot cleanup in a finally block so provider errors do not leave temporary captures behind.
Contract source: OpenSS/src/openss/gui/server.py and schemas.py.