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.