File size: 3,363 Bytes
5dfbe50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { Hono } from 'hono';
import { streamSSE } from 'hono/streaming';
import { config } from '../config';

const visualRagChatApp = new Hono();

// Visual RAG Chat SSE endpoint - matches Next.js /api/visual-rag-chat
visualRagChatApp.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 { visualRagChatApp };