vk98's picture
Initial backend deployment - Hono proxy + ColPali embedding API
5dfbe50
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 };