n4nAI

LangChain streaming with n4n.ai and Server-Sent Events

Step-by-step guide to langchain streaming n4n.ai sse with FastAPI and EventSource, covering config, SSE wiring, error handling, and verification.

n4n Team3 min read685 words

Audio narration

Coming soon — every post will get a voice note here.

Implementing langchain streaming n4n.ai sse in a production web service means bridging LangChain’s token iterator to the text/event-stream wire format without buffering. This guide gives you a runnable FastAPI service and a browser client that together demonstrate the full path from model to screen.

Step 1: Install dependencies

Create a clean virtual environment and install the minimal set of packages. You need the LangChain OpenAI integration, an ASGI server, and FastAPI.

pip install langchain-openai fastapi uvicorn

If you plan to test from Node or a browser, no extra packages are required client-side; Server-Sent Events (SSE) are native to browsers via EventSource.

Step 2: Configure the LangChain model

Point ChatOpenAI at an OpenAI-compatible base URL. n4n.ai exposes an OpenAI-compatible endpoint that fronts 240+ models and applies automatic fallback when a provider is degraded, so the client config stays identical to OpenAI’s SDK.

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model=os.getenv("MODEL_NAME", "gpt-4o-mini"),
    base_url=os.getenv("OPENAI_BASE_URL", "https://api.n4n.ai/v1"),
    api_key=os.getenv("N4N_API_KEY"),
    streaming=True,
    temperature=0.7,
)

Store keys in environment variables. Never hardcode credentials in source. The streaming=True flag tells the client to use the chunked response protocol rather than waiting for the full completion.

Step 3: Scaffold the FastAPI app

SSE requires a persistent HTTP connection with Content-Type: text/event-stream. FastAPI’s StreamingResponse handles this cleanly. Each message is a line prefixed with data: and terminated by a blank line.

from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
import json

app = FastAPI()

Step 4: Stream tokens from LangChain to SSE

The core of langchain streaming n4n.ai sse is the async iterator that llm.astream returns. Wrap it in a generator that yields SSE-formatted strings. JSON-encode the payload to avoid newline or unicode breakage.

@app.get("/chat")
async def chat(request: Request, q: str):
    async def event_stream():
        try:
            async for chunk in llm.astream(q):
                if await request.is_disconnected():
                    break
                content = chunk.content or ""
                yield f"data: {json.dumps(content)}\n\n"
        except Exception as e:
            yield f"event: error\ndata: {json.dumps(str(e))}\n\n"
    return StreamingResponse(
        event_stream(),
        media_type="text/event-stream",
        headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
    )

The X-Accel-Buffering: no header disables proxy buffering (relevant behind NGINX). The request.is_disconnected() check prevents wasted inference if the user closes the tab.

Step 5: Consume the SSE feed in the browser

EventSource only supports GET requests, which is fine for a simple query param. For POST bodies, you would open the stream from a fetch with ReadableStream instead.

const es = new EventSource(`/chat?q=${encodeURIComponent("Explain SSE briefly")}`);
const out = document.getElementById("output");

es.onmessage = (ev) => {
  const token = JSON.parse(ev.data);
  out.textContent += token;
};

es.onerror = (err) => {
  console.error("stream closed", err);
  es.close();
};

If you need to pass a conversation history, encode it as a JSON string in the query param or switch to a fetch-based stream reader.

Step 6: Verify the stream end-to-end

Start the server:

uvicorn main:app --port 8000 --reload

Open the browser page that runs the EventSource code. Tokens should appear incrementally.

For a lower-level check, use curl with -N to disable buffering:

curl -N "http://localhost:8000/chat?q=What%20is%20SSE"

You should see repeated lines like:

data: "Server"
data: "-"
data: "Sent"
data: " Events"

When verifying langchain streaming n4n.ai sse, confirm that the connection stays open until the final token and then closes gracefully. If you see the full response in one blob, a proxy is buffering—check the X-Accel-Buffering header and your deployment config.

Step 7: Handle errors, aborts, and provider fallback

Production caveats for langchain streaming n4n.ai sse include client aborts, model timeouts, and malformed content. The try/except in the generator already emits an error event. On the client, treat es.onerror as terminal and surface a retry button.

If the upstream provider rate-limits, the gateway’s automatic fallback switches to a healthy provider without changing your code. Your stream may experience a brief pause but should resume. Log the chunk.response_metadata if you need to inspect which model actually served the token:

async for chunk in llm.astream(q):
    if chunk.response_metadata:
        print(chunk.response_metadata.get("model"))

For per-token cost tracking, capture chunk.usage on the final chunk if the endpoint returns it, or rely on the gateway’s metering sidechannel.

Alternative: using a callback handler

If you need to broadcast one model stream to multiple SSE clients, llm.astream alone is insufficient. Use an AsyncCallbackHandler to push tokens into an asyncio.Queue per subscriber.

from langchain_core.callbacks import AsyncCallbackHandler
import asyncio

class QueueHandler(AsyncCallbackHandler):
    def __init__(self, q: asyncio.Queue):
        self.q = q
    async def on_llm_new_token(self, token: str, **kwargs):
        await self.q.put(token)

async def generate(q: asyncio.Queue, prompt: str):
    handler = QueueHandler(q)
    await llm.ainvoke(prompt, config={"callbacks": [handler]})

The SSE endpoint then drains the queue until a sentinel value arrives. This pattern decouples inference from transport and lets you add auth, rate limiting, or fan-out.

Deployment notes

Run at least two Uvicorn workers behind a reverse proxy that supports chunked transfer. Disable gzip for the stream path—compression defeats the purpose of low-latency tokens. Set proxy_read_timeout high enough to cover slow generations, but rely on client disconnect checks to free resources.

SSE is unidirectional; if your app needs to send mid-stream corrections (e.g., stop sequences from the UI), close the EventSource and open a new request. For bidirectional needs, use WebSocket, but keep the LangChain side identical—astream works the same under any transport.

Verify success

Success means: curl -N prints incremental data: lines; the browser appends text without full-page reload; closing the tab stops inference within a second; and an invalid API key returns an error event rather than a hung connection. Once those four conditions hold, you have a working langchain streaming n4n.ai sse pipeline ready for production traffic.

Tagslangchainstreamingn4n-aisse

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All langchain streaming responses & callbacks posts →