Integrating LLM APIs · Building Blocks

Streaming Responses to Users

Token streaming from the provider through your backend to browsers via SSE.

Generation takes seconds; streaming shows text as it's produced (the "typing" effect) and avoids HTTP timeouts on long outputs. Your backend consumes the provider stream and relays it to clients — typically via Server-Sent Events (one-way, auto-reconnect, works over HTTP/2). Handle client disconnects (cancel the upstream request to stop paying), send heartbeats through proxies, disable proxy buffering, and persist the final message (with usage) when the stream completes.

Streaming vs not

Criterion Non-streaming Streaming (SSE relay)
Time to first token shown Full generation time Sub-second
Long outputs Risk of HTTP timeouts Safe
Implementation Simple Relay, cancellation, partial state
Post-processing (validation) Easy on full text After completion (or incremental)
Best for Background jobs, short outputs Chat UIs, long generations
from fastapi.responses import StreamingResponse
import anthropic, json

aclient = anthropic.AsyncAnthropic()

@router.post("/v1/chat/stream")
async def chat_stream(body: ChatIn, request: Request, user=Depends(current_user)):
    async def events():
        async with aclient.messages.stream(model=MODEL, max_tokens=64000,
                                           system=SYSTEM, messages=body.messages) as stream:
            async for text in stream.text_stream:
                if await request.is_disconnected():
                    break                                  # leaving the context closes the upstream stream
                yield f"data: {json.dumps({'delta': text})}\n\n"
            final = await stream.get_final_message()
            await save_turn(user.id, final)                # persist text + usage
            yield f"event: done\ndata: {json.dumps({'stop_reason': final.stop_reason})}\n\n"
    return StreamingResponse(events(), media_type="text/event-stream",
                             headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})

Common pitfalls

  • Proxy buffering (Nginx) that delivers the whole response at the end.
  • Not cancelling upstream on disconnect.
  • Rendering partial Markdown/HTML unsafely as it streams.

Key points

  • Relay via SSE
  • Cancel upstream on disconnect
  • Persist the final message + usage

Further reading: Streaming messages

Practice questions

Why is SSE a common choice for relaying LLM tokens to browsers?

Answer

Token streams are one-way server→client, and SSE works over plain HTTP with automatic reconnection and simple client APIs.

What should happen when the user closes the tab mid-generation?

Answer

The backend should detect the disconnect and abort the upstream provider request so it stops generating (and billing) tokens.

Open in the app to mark lessons complete, take notes and track your progress.