| const API_BASE = localStorage.getItem('agr_api_base') || ''; |
|
|
| async function request(path, opts = {}) { |
| const url = `${API_BASE}${path}`; |
| const config = { headers: {}, ...opts }; |
| if (config.body && typeof config.body === 'object' && !(config.body instanceof FormData)) { |
| config.headers['Content-Type'] = 'application/json'; |
| config.body = JSON.stringify(config.body); |
| } |
| const res = await fetch(url, config); |
| if (!res.ok) { |
| const errBody = await res.json().catch(() => null); |
| throw new Error(errBody?.detail || errBody?.error || `HTTP ${res.status}`); |
| } |
| return res; |
| } |
|
|
| export const api = { |
| health: () => request('/health').then(r => r.json()), |
| readiness: () => request('/ready').then(r => r.json()), |
|
|
| |
| createSession: (userId = 'web_ui') => |
| request('/api/v1/session', { method: 'POST', body: { user_id: userId } }).then(r => r.json()), |
| getSession: (id) => request(`/api/v1/session/${id}`).then(r => r.json()), |
| getMessages: (id) => request(`/api/v1/session/${id}/messages`).then(r => r.json()), |
| listSessions: (userId = 'default') => request(`/api/v1/sessions?user_id=${userId}`).then(r => r.json()), |
| updateSessionTitle: (sessionId, title) => |
| request(`/api/v1/session/${sessionId}?title=${encodeURIComponent(title)}`, { method: 'PUT' }).then(r => r.json()), |
| deleteSession: (sessionId) => |
| request(`/api/v1/session/${sessionId}`, { method: 'DELETE' }).then(r => r.json()), |
| clearSessionMessages: (sessionId) => |
| request(`/api/v1/session/${sessionId}/messages`, { method: 'DELETE' }).then(r => r.json()), |
| deleteAllSessions: () => |
| request('/api/v1/sessions', { method: 'DELETE' }).then(r => r.json()), |
|
|
| |
| search: (query, topK = 10, mode = 'hybrid', modality) => |
| request('/api/v1/rag/search', { |
| method: 'POST', |
| body: { query, top_k: topK, mode, ...(modality && modality !== 'all' ? { modality_filter: [modality] } : {}) }, |
| }).then(r => r.json()), |
| stats: () => request('/api/v1/rag/stats').then(r => r.json()), |
| listDocuments: () => request('/api/v1/rag/documents').then(r => r.json()), |
| getDocument: (docId) => request(`/api/v1/rag/document/${docId}`).then(r => r.json()), |
| deleteDocument: (docId) => request(`/api/v1/rag/document/${docId}`, { method: 'DELETE' }).then(r => r.json()), |
| clearKB: () => request('/api/v1/rag/clear', { method: 'POST' }).then(r => r.json()), |
| uploadFile: (file, source, ingestMode, mmMethod, chunkSize = 512, chunkOverlap = 50, enableKG = true) => { |
| const fd = new FormData(); |
| fd.append('file', file); |
| fd.append('source', source); |
| fd.append('ingest_mode', ingestMode); |
| fd.append('mm_method', mmMethod); |
| fd.append('chunk_size', chunkSize); |
| fd.append('chunk_overlap', chunkOverlap); |
| fd.append('enable_kg', enableKG); |
| return request('/api/v1/rag/upload', { method: 'POST', body: fd }).then(r => r.json()); |
| }, |
| serveFile: (path) => `${API_BASE}/api/v1/rag/file/${path.split('/').map(encodeURIComponent).join('/')}`, |
|
|
| |
| voiceChatStream: async (audioBlob, sessionId, mode, tts, onEvent, signal) => { |
| const fd = new FormData(); |
| fd.append('audio', audioBlob, 'recording.webm'); |
| fd.append('session_id', sessionId || ''); |
| fd.append('mode', mode || ''); |
| fd.append('tts', tts !== false); |
| const res = await fetch(`${API_BASE}/api/v1/chat/voice`, { method: 'POST', body: fd, signal }); |
| if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.detail || `HTTP ${res.status}`); } |
| const reader = res.body.getReader(); |
| const decoder = new TextDecoder(); |
| let buffer = '', currentEvent = ''; |
| while (true) { |
| const { done, value } = await reader.read(); |
| if (done) break; |
| buffer += decoder.decode(value, { stream: true }); |
| const lines = buffer.split('\n'); |
| buffer = lines.pop() || ''; |
| for (const line of lines) { |
| if (line.startsWith('event: ')) { |
| currentEvent = line.slice(7).trim(); |
| } else if (line.startsWith('data: ') && currentEvent) { |
| try { |
| const data = JSON.parse(line.slice(6)); |
| onEvent({ type: currentEvent, data }); |
| } catch {} |
| currentEvent = ''; |
| } |
| } |
| } |
| }, |
|
|
| |
| getSettings: () => request('/api/v1/settings').then(r => r.json()), |
| saveSettings: (sections) => request('/api/v1/settings', { method: 'PUT', body: { sections } }).then(r => r.json()), |
| testConnection: (payload) => request('/api/v1/settings/test', { method: 'POST', body: payload }).then(r => r.json()), |
|
|
| |
| getMcpConfig: () => request('/api/v1/mcp/servers').then(r => r.json()), |
| saveMcpConfig: (servers) => request('/api/v1/mcp/servers', { method: 'PUT', body: { servers } }).then(r => r.json()), |
| }; |
|
|
| export async function streamChat(message, sessionId, images, onEvent, signal) { |
| const res = await fetch(`${API_BASE}/api/v1/chat/stream`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ message, session_id: sessionId, stream: true, images }), |
| signal, |
| }); |
|
|
| |
| if (!res.ok) { |
| const errBody = await res.json().catch(() => null); |
| throw new Error(errBody?.detail || errBody?.error || `HTTP ${res.status}`); |
| } |
|
|
| const reader = res.body.getReader(); |
| const decoder = new TextDecoder(); |
| let buffer = ''; |
| let receivedDone = false; |
|
|
| try { |
| while (!receivedDone) { |
| const { done, value } = await reader.read(); |
| if (done) break; |
| buffer += decoder.decode(value, { stream: true }); |
| const lines = buffer.split('\n'); |
| buffer = lines.pop() || ''; |
| for (const line of lines) { |
| if (!line.startsWith('data: ')) continue; |
| const s = line.slice(6).trim(); |
| if (!s || s === '[DONE]') { |
| receivedDone = true; |
| break; |
| } |
| try { |
| const parsed = JSON.parse(s); |
| const type = parsed.event || parsed.event_type; |
| let data = parsed.data || parsed; |
| if (typeof data === 'string') { |
| try { data = JSON.parse(data); } catch { } |
| } |
| onEvent({ type, data }); |
| |
| if (type === 'done') { |
| receivedDone = true; |
| break; |
| } |
| } catch { } |
| } |
| } |
| } finally { |
| |
| reader.releaseLock(); |
| } |
| } |
|
|