// ============================================================================ // Frontend Integration - How to Consume SSE Streams // ============================================================================ // ============================================================================ // VANILLA JAVASCRIPT EXAMPLE // ============================================================================ async function sendStreamingMessage(query, conversationId = null) { const token = localStorage.getItem('jwt_token'); // Prepare request const requestData = { query: query, conversation_id: conversationId }; try { // Make POST request to get stream const response = await fetch('http://localhost:8000/api/v1/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(requestData) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // Read stream const reader = response.body.getReader(); const decoder = new TextDecoder(); let fullResponse = ''; let currentConversationId = conversationId; while (true) { const { done, value } = await reader.read(); if (done) break; // Decode chunk const chunk = decoder.decode(value); // Parse SSE events const events = parseSSE(chunk); for (const event of events) { switch (event.type) { case 'conversation_id': currentConversationId = event.data.conversation_id; console.log('Conversation ID:', currentConversationId); break; case 'status': console.log('Status:', event.data.message); // Update UI with status updateStatusIndicator(event.data.message); break; case 'content': fullResponse += event.data.text; // Append to UI in real-time appendToMessageBubble(event.data.text); break; case 'metadata': console.log('Metadata:', event.data); // Save metadata if needed break; case 'done': console.log('Stream completed'); hideStatusIndicator(); break; case 'error': console.error('Stream error:', event.data.error); showError(event.data.message); break; } } } return { response: fullResponse, conversationId: currentConversationId }; } catch (error) { console.error('Streaming error:', error); throw error; } } // Parse SSE format function parseSSE(text) { const events = []; const lines = text.split('\n\n'); for (const line of lines) { if (!line.trim()) continue; const eventMatch = line.match(/event: (.+)/); const dataMatch = line.match(/data: (.+)/); if (eventMatch && dataMatch) { try { events.push({ type: eventMatch[1], data: JSON.parse(dataMatch[1]) }); } catch (e) { console.warn('Failed to parse SSE event:', e); } } } return events; } // ============================================================================ // REACT EXAMPLE (with hooks) // ============================================================================ import { useState, useRef } from 'react'; function ChatComponent() { const [messages, setMessages] = useState([]); const [isStreaming, setIsStreaming] = useState(false); const [statusMessage, setStatusMessage] = useState(''); const abortControllerRef = useRef(null); const sendStreamingMessage = async (query, conversationId) => { setIsStreaming(true); setStatusMessage(''); // Add user message immediately const userMessage = { role: 'user', content: query }; setMessages(prev => [...prev, userMessage]); // Create assistant message placeholder const assistantMessageId = Date.now(); setMessages(prev => [...prev, { id: assistantMessageId, role: 'assistant', content: '' }]); try { const token = localStorage.getItem('jwt_token'); // Create abort controller for cancellation abortControllerRef.current = new AbortController(); const response = await fetch('http://localhost:8000/api/v1/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ query, conversation_id: conversationId }), signal: abortControllerRef.current.signal }); const reader = response.body.getReader(); const decoder = new TextDecoder(); let newConversationId = conversationId; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const events = parseSSE(chunk); for (const event of events) { switch (event.type) { case 'conversation_id': newConversationId = event.data.conversation_id; break; case 'status': setStatusMessage(event.data.message); break; case 'content': // Update assistant message setMessages(prev => prev.map(msg => msg.id === assistantMessageId ? { ...msg, content: msg.content + event.data.text } : msg )); break; case 'done': setStatusMessage(''); break; case 'error': console.error('Error:', event.data); setStatusMessage(''); break; } } } return newConversationId; } catch (error) { if (error.name === 'AbortError') { console.log('Stream cancelled by user'); } else { console.error('Streaming error:', error); } } finally { setIsStreaming(false); setStatusMessage(''); abortControllerRef.current = null; } }; const stopStreaming = () => { if (abortControllerRef.current) { abortControllerRef.current.abort(); } }; return (
{messages.map((msg, idx) => (
{msg.content}
))} {statusMessage && (
{statusMessage}
)}
{isStreaming && ( )}
); } // ============================================================================ // REGENERATE & EDIT EXAMPLES // ============================================================================ // Regenerate last response async function regenerateResponse(conversationId) { const token = localStorage.getItem('jwt_token'); const response = await fetch( `http://localhost:8000/api/v1/chat/conversation/${conversationId}/regenerate`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } } ); // Process stream same as sendStreamingMessage // ... } // Edit last message async function editLastMessage(conversationId, newContent) { const token = localStorage.getItem('jwt_token'); const response = await fetch( `http://localhost:8000/api/v1/chat/conversation/${conversationId}/edit`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ new_content: newContent }) } ); // Process stream // ... }