/** * API client for the LLM Council backend. */ const API_BASE = 'http://localhost:8001'; export const api = { /** * List all conversations. */ async listConversations() { const response = await fetch(`${API_BASE}/api/conversations`); if (!response.ok) { throw new Error('Failed to list conversations'); } return response.json(); }, /** * Create a new conversation. */ async createConversation() { const response = await fetch(`${API_BASE}/api/conversations`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({}), }); if (!response.ok) { throw new Error('Failed to create conversation'); } return response.json(); }, /** * Get a specific conversation. */ async getConversation(conversationId) { const response = await fetch( `${API_BASE}/api/conversations/${conversationId}` ); if (!response.ok) { throw new Error('Failed to get conversation'); } return response.json(); }, /** * Send a message in a conversation. */ async sendMessage(conversationId, content) { const response = await fetch( `${API_BASE}/api/conversations/${conversationId}/message`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ content }), } ); if (!response.ok) { throw new Error('Failed to send message'); } return response.json(); }, /** * Send a message and receive streaming updates. * @param {string} conversationId - The conversation ID * @param {string} content - The message content * @param {function} onEvent - Callback function for each event: (eventType, data) => void * @returns {Promise} */ async sendMessageStream(conversationId, content, onEvent, signal) { const response = await fetch( `${API_BASE}/api/conversations/${conversationId}/message/stream`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ content }), signal, } ); if (!response.ok) { throw new Error('Failed to send message'); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // Split on double newline (SSE event boundary) const parts = buffer.split('\n\n'); // Last part may be incomplete — keep it in buffer buffer = parts.pop(); for (const part of parts) { const lines = part.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6); try { const event = JSON.parse(data); onEvent(event.type, event); } catch (e) { console.error('Failed to parse SSE event:', e); } } } } } }, };