import { Hono } from 'hono'; import { streamSSE } from 'hono/streaming'; import { config } from '../config'; const chatApp = new Hono(); // Visual RAG Chat SSE endpoint - matches Next.js /api/visual-rag-chat chatApp.get('/', async (c) => { const queryId = c.req.query('queryId'); const query = c.req.query('query'); const docIds = c.req.query('docIds'); if (!queryId || !query || !docIds) { return c.json({ error: 'Missing required parameters: queryId, query, docIds' }, 400); } return streamSSE(c, async (stream) => { try { // Create abort controller for cleanup const abortController = new AbortController(); // Forward request to backend /get-message endpoint const chatUrl = `${config.backendUrl}/get-message?query_id=${encodeURIComponent(queryId)}&query=${encodeURIComponent(query)}&doc_ids=${encodeURIComponent(docIds)}`; const response = await fetch(chatUrl, { headers: { 'Accept': 'text/event-stream', }, signal: abortController.signal, }); if (!response.ok) { await stream.writeSSE({ event: 'error', data: JSON.stringify({ error: `Backend returned ${response.status}` }), }); return; } if (!response.body) { await stream.writeSSE({ event: 'error', data: JSON.stringify({ error: 'No response body' }), }); return; } // Stream the response 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 }); const lines = buffer.split('\n'); // Keep the last incomplete line in the buffer buffer = lines.pop() || ''; for (const line of lines) { if (line.trim() === '') continue; if (line.startsWith('data: ')) { const data = line.slice(6); await stream.writeSSE({ data }); } else if (line.startsWith('event: ')) { // Handle event lines if backend sends them const event = line.slice(7).trim(); // Look for the next data line const nextLineIndex = lines.indexOf(line) + 1; if (nextLineIndex < lines.length) { const nextLine = lines[nextLineIndex]; if (nextLine.startsWith('data: ')) { const data = nextLine.slice(6); await stream.writeSSE({ event, data }); lines.splice(nextLineIndex, 1); // Remove processed line } } } } } // Handle any remaining data in buffer if (buffer.trim()) { if (buffer.startsWith('data: ')) { await stream.writeSSE({ data: buffer.slice(6) }); } } // Cleanup abortController.abort(); } catch (error) { console.error('Chat streaming error:', error); await stream.writeSSE({ event: 'error', data: JSON.stringify({ error: 'Streaming failed', message: error instanceof Error ? error.message : 'Unknown error' }), }); } }); }); export { chatApp };