"""Server-sent events: the wire format, the headers, and the error frame. Two routers stream: the turn engine in `routers/adventures/turns.py` and the chat scratchpad in `routers/chat.py`. Both send JSON objects as SSE `data:` frames, so the format lives here rather than in either one. """ import json # `no-cache` stops an intermediary from caching the stream. `X-Accel-Buffering` # makes an nginx-style reverse proxy flush each event immediately rather than # buffer it, which matters if anyone puts one in front of the app. SSE_HEADERS = {"Cache-Control": "no-cache", "X-Accel-Buffering": "no"} def sse(obj: dict) -> str: """Returns one SSE frame carrying `obj` as JSON.""" return f"data: {json.dumps(obj)}\n\n" def turn_error(detail: str, **extra) -> str: """Returns an SSE error for a turn that could not be produced. A failed turn is still an HTTP 200 response, because the error is reported inside the stream the client is already reading. """ return sse({"type": "error", "detail": detail, **extra})