Imagine it's 11 PM on Black Friday. Your e-commerce AI customer service chatbot is handling 5,000 concurrent users when suddenly the streaming response from your LLM provider stutters and drops. Users see loading spinners. Support tickets flood in. You need a bulletproof streaming implementation yesterday.
Or consider this: you've just launched an enterprise RAG system processing thousands of document queries daily. Your CEO demo is in 2 hours. The streaming works perfectly in testing but falls apart under production load with intermittent network blips and provider rate limits.
As an indie developer building your first AI-powered product, you've got limited budget but ambitious goals. Every millisecond of latency matters, and every unexpected disconnection costs you users.
Sound familiar? You're not alone. Server-Sent Events (SSE) streaming is the backbone of modern AI applications, but implementing it robustly requires understanding connection lifecycle management, reconnection strategies, and graceful error handling.
In this comprehensive guide, I'll walk you through building a production-ready SSE streaming solution using HolySheep AI โ a cost-effective LLM provider offering sub-50ms latency and 85%+ savings compared to mainstream alternatives.
Understanding SSE Streaming Architecture
Server-Sent Events is a server push technology enabling automatic updates from server to client over HTTP. Unlike WebSockets, SSE is unidirectional and automatically handles reconnection natively. For AI streaming responses where the server generates tokens and pushes them to the client, SSE provides the ideal mechanism.
The Complete Frontend Implementation
Let's build a robust streaming client that handles connection management, token accumulation, reconnection with exponential backoff, and graceful degradation.
Core Streaming Client Class
// streaming-client.ts
interface StreamConfig {
apiKey: string;
baseUrl?: string;
model: string;
onChunk?: (text: string, fullResponse: string) => void;
onError?: (error: Error) => void;
onComplete?: (fullResponse: string) => void;
onConnectionChange?: (status: 'connecting' | 'connected' | 'reconnecting' | 'disconnected') => void;
}
interface RetryState {
attempts: number;
maxAttempts: number;
baseDelay: number;
maxDelay: number;
lastError?: Error;
}
export class HolySheepStreamingClient {
private baseUrl: string;
private apiKey: string;
private abortController: AbortController | null = null;
private retryState: RetryState;
private reconnectTimeoutId: number | null = null;
private config: StreamConfig;
// Buffer to accumulate response for resumption
private responseBuffer: string = '';
private lastEventId: string | null = null;
constructor(config: StreamConfig) {
this.config = config;
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl || 'https://api.holysheep.ai/v1';
this.retryState = {
attempts: 0,
maxAttempts: 5,
baseDelay: 1000,
maxDelay: 30000
};
}
async sendMessageStream(messages: Array<{role: string; content: string}>, systemPrompt?: string): Promise<string> {
this.abortController = new AbortController();
this.config.onConnectionChange?.('connecting');
try {
const response = await fetch(${this.baseUrl}/chat/completions, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${this.apiKey},
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
body: JSON.stringify({
model: this.config.model,
messages: [
...(systemPrompt ? [{ role: 'system', content: systemPrompt }] : []),
...messages
],
stream: true,
stream_options: { include_usage: true }
}),
signal: this.abortController.signal,
});
if (!response.ok) {
throw new Error(HTTP ${response.status}: ${response.statusText});
}
if (!response.body) {
throw new Error('Response body is null - streaming not supported');
}
this.config.onConnectionChange?.('connected');
this.retryState.attempts = 0; // Reset retry counter on successful connection
const fullResponse = await this.processStream(response.body);
this.config.onComplete?.(fullResponse);
return fullResponse;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
this.config.onConnectionChange?.('disconnected');
return this.responseBuffer;
}
this.config.onError?.(error as Error);
return this.handleError(error as Error, messages, systemPrompt);
}
}
private async processStream(body: ReadableStream<Uint8Array>): Promise<string> {
const reader = body.getReader();
const decoder = new TextDecoder();
let buffer = '';
this.responseBuffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep incomplete line in buffer
for (const line of lines) {
const parsed = this.parseSSELine(line);
if (parsed) {
this.responseBuffer += parsed;
this.config.onChunk?.(parsed, this.responseBuffer);
}
}
}
// Process any remaining data in buffer
if (buffer.trim()) {
const parsed = this.parseSSELine(buffer);
if (parsed) {
this.responseBuffer += parsed;
this.config.onChunk?.(parsed, this.responseBuffer);
}
}
} finally {
reader.releaseLock();
}
return this.responseBuffer;
}
private parseSSELine(line: string): string | null {
// Handle both 'data: ' and 'data:' formats
if (!line.startsWith('data:')) return null;
const data = line.slice(5).trim();
if (data === '[DONE]' || data === '') return null;
try {
const json = JSON.parse(data);
// Handle different SSE event formats from various providers
if (json.choices?.[0]?.delta?.content) {
return json.choices[0].delta.content;
}
// HolySheep and OpenAI-compatible format
if (json.delta?.content) {
return json.delta.content;
}
// Error handling for error events
if (json.error) {
throw new Error(json.error.message || JSON.stringify(json.error));
}
return null;
} catch {
return null;
}
}
private async handleError(error: Error, messages: any[], systemPrompt?: string): Promise<string> {
this.retryState.lastError = error;
this.config.onConnectionChange?.('reconnecting');
// Check if we should retry
if (this.retryState.attempts >= this.retryState.maxAttempts) {
this.config.onConnectionChange?.('disconnected');
throw new Error(Max retry attempts (${this.retryState.maxAttempts}) reached. Last error: ${error.message});
}
// Check for non-retryable errors
const nonRetryableErrors = ['401', '403', '400'];
for (const code of nonRetryableErrors) {
if (error.message.includes(code)) {
throw error;
}
}
// Exponential backoff with jitter
const delay = Math.min(
this.retryState.baseDelay * Math.pow(2, this.retryState.attempts) + Math.random() * 1000,
this.retryState.maxDelay
);
console.log(Retry attempt ${this.retryState.attempts + 1}/${this.retryState.maxAttempts} after ${delay}ms delay);
await this.delay(delay);
this.retryState.attempts++;
// Retry the request
return this.sendMessageStream(messages, systemPrompt);
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => {
this.reconnectTimeoutId = window.setTimeout(resolve, ms);
});
}
abort(): void {
if (this.reconnectTimeoutId) {
clearTimeout(this.reconnectTimeoutId);
this.reconnectTimeoutId = null;
}
this.abortController?.abort();
this.config.onConnectionChange?.('disconnected');
}
getResponseBuffer(): string {
return this.responseBuffer;
}
}
React Hook for Streaming Responses
// useStreamingChat.ts
import { useState, useCallback, useRef, useEffect } from 'react';
import { HolySheepStreamingClient } from './streaming-client';
interface UseStreamingChatOptions {
apiKey: string;
model: string;
systemPrompt?: string;
baseUrl?: string;
}
interface Message {
id: string;
role: 'user' | 'assistant';
content: string;
isStreaming?: boolean;
}
export function useStreamingChat(options: UseStreamingChatOptions) {
const [messages, setMessages] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [connectionStatus, setConnectionStatus] = useState<
'connecting' | 'connected' | 'reconnecting' | 'disconnected'
>('disconnected');
const [error, setError] = useState<Error | null>(null);
const clientRef = useRef<HolySheepStreamingClient | null>(null);
const currentMessageIdRef = useRef<string | null>(null);
// Initialize client
useEffect(() => {
clientRef.current = new HolySheepStreamingClient({
apiKey: options.apiKey,
baseUrl: options.baseUrl || 'https://api.holysheep.ai/v1',
model: options.model,
onChunk: (chunk, fullResponse) => {
if (currentMessageIdRef.current) {
setMessages(prev => prev.map(msg =>
msg.id === currentMessageIdRef.current
? { ...msg, content: fullResponse, isStreaming: true }
: msg
));
}
},
onComplete: (fullResponse) => {
if (currentMessageIdRef.current) {
setMessages(prev => prev.map(msg =>
msg.id === currentMessageIdRef.current
? { ...msg, content: fullResponse, isStreaming: false }
: msg
));
}
setIsLoading(false);
setConnectionStatus('disconnected');
currentMessageIdRef.current = null;
},
onError: (err) => {
setError(err);
setIsLoading(false);
setConnectionStatus('disconnected');
// Mark current message as errored
if (currentMessageIdRef.current) {
setMessages(prev => prev.map(msg =>
msg.id === currentMessageIdRef.current
? { ...msg, isStreaming: false }
: msg
));
}
currentMessageIdRef.current = null;
},
onConnectionChange: setConnectionStatus,
});
return () => {
clientRef.current?.abort();
};
}, [options.apiKey, options.model, options.baseUrl]);
const sendMessage = useCallback(async (content: string) => {
if (!clientRef.current || isLoading) return;
const userMessage: Message = {
id: user-${Date.now()},
role: 'user',
content,
};
const assistantMessage: Message = {
id: assistant-${Date.now()},
role: 'assistant',
content: '',
isStreaming: true,
};
setMessages(prev => [...prev, userMessage, assistantMessage]);
setIsLoading(true);
setError(null);
currentMessageIdRef.current = assistantMessage.id;
const conversationHistory = messages.map(m => ({
role: m.role,
content: m.content,
}));
try {
await clientRef.current.sendMessageStream(
[...conversationHistory, { role: 'user', content }],
options.systemPrompt
);
} catch (err) {
console.error('Stream error:', err);
}
}, [messages, isLoading, options.systemPrompt]);
const abort = useCallback(() => {
clientRef.current?.abort();
setIsLoading(false);
currentMessageIdRef.current = null;
}, []);
const clearMessages = useCallback(() => {
setMessages([]);
setError(null);
}, []);
return {
messages,
isLoading,
connectionStatus,
error,
sendMessage,
abort,
clearMessages,
};
}
React Component Implementation
// StreamingChat.tsx
import React, { useState } from 'react';
import { useStreamingChat } from './useStreamingChat';
// In production, load from environment variables
const API_KEY = 'YOUR_HOLYSHEEP_API_KEY';
export function StreamingChat() {
const [input, setInput] = useState('');
const {
messages,
isLoading,
connectionStatus,
error,
sendMessage,
abort,
clearMessages,
} = useStreamingChat({
apiKey: API_KEY,
model: 'deepseek-v3.2',
systemPrompt: 'You are a helpful AI assistant.',
baseUrl: 'https://api.holysheep.ai/v1',
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (input.trim() && !isLoading) {
sendMessage(input);
setInput('');
}
};
const getStatusIndicator = () => {
const statusConfig = {
connecting: { color: 'bg-yellow-500', text: 'Connecting...' },
connected: { color: 'bg-green-500', text: 'Connected' },
reconnecting: { color: 'bg-orange-500', text: 'Reconnecting...' },
disconnected: { color: 'bg-gray-500', text: 'Ready' },
};
const config = statusConfig[connectionStatus];
return (
<span className="flex items-center gap-2 text-sm">
<span className={w-2 h-2 rounded-full ${config.color} ${connectionStatus === 'connected' ? 'animate-pulse' : ''}}></span>
{config.text}
</span>
);
};
return (
<div className="max-w-2xl mx-auto p-6">
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="p-4 border-b bg-gray-50 flex justify-between items-center">
<h2 className="font-semibold">AI Customer Service Demo</h2>
<div className="flex items-center gap-4">
{getStatusIndicator()}
<button
onClick={clearMessages}
className="text-sm text-gray-500 hover:text-gray-700"
>
Clear
</button>
</div>
</div>
<div className="h-96 overflow-y-auto p-4 space-y-4">
{messages.map(msg => (
<div
key={msg.id}
className={flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}}
>
<div
className={`max-w-[80%] rounded-lg px-4 py-2 ${
msg.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-800'
}`}
>
<div className="whitespace-pre-wrap">{msg.content}</div>
{msg.isStreaming && (
<span className="inline-block w-2 h-4 ml-1 bg-blue-500 animate-pulse"></span>
)}
</div>
</div>
))}
{error && (
<div className="text-red-500 text-sm p-2 bg-red-50 rounded">
Error: {error.message}
</div>
)}
</div>
<form onSubmit={handleSubmit} className="p-4 border-t">
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isLoading}
placeholder="Type your message..."
className="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100"
/>
{isLoading ? (
<button
type="button"
onClick={abort}
className="px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600"
>
Stop
Related Resources
Related Articles