import api from "@/lib/api-client.ts"; import type { AiChat, AiChatMessage, AiChatStreamEvent, ChatAttachment, } from "../types/ai-chat.types"; import { IPagination } from "@/lib/types.ts"; export async function createChat(): Promise { const req = await api.post("/ai/chats/create"); return req.data; } export async function listChats(params?: { limit?: number; cursor?: string; }): Promise> { const req = await api.post("/ai/chats", params); return req.data; } export async function getChatInfo( chatId: string, ): Promise<{ chat: AiChat; messages: AiChatMessage[] }> { const req = await api.post("/ai/chats/info", { chatId }); return req.data; } export async function deleteChat(chatId: string): Promise { await api.post("/ai/chats/delete", { chatId }); } export async function updateChatTitle( chatId: string, title: string, ): Promise { await api.post("/ai/chats/update", { chatId, title }); } export async function searchChats(query: string): Promise { const req = await api.post("/ai/chats/search", { query }); return req.data; } export async function uploadChatFile( file: File, chatId?: string, ): Promise { const formData = new FormData(); formData.append("file", file); if (chatId) { formData.append("chatId", chatId); } return await api.post("/ai/chats/upload", formData, { headers: { "Content-Type": "multipart/form-data" }, }); } export function sendChatMessage( params: { chatId?: string; content: string; mentionedPageIds?: string[]; contextPageId?: string; attachmentIds?: string[]; }, onEvent: (event: AiChatStreamEvent) => void, onError?: (error: string) => void, onComplete?: () => void, ): AbortController { const abortController = new AbortController(); (async () => { try { const response = await fetch("/api/ai/chats/send", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params), signal: abortController.signal, credentials: "include", }); if (!response.ok) { const errorBody = await response.text(); let errorMessage = `HTTP error ${response.status}`; try { const parsed = JSON.parse(errorBody); errorMessage = parsed.message || errorMessage; } catch { // use default } onError?.(errorMessage); return; } const reader = response.body?.getReader(); const decoder = new TextDecoder(); if (!reader) { onError?.("Response body is not readable"); return; } let buffer = ""; try { 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("data: ")) { const data = line.slice(6); if (data === "[DONE]") { onComplete?.(); return; } try { const parsed = JSON.parse(data) as AiChatStreamEvent; onEvent(parsed); } catch { // Skip invalid JSON } } } } } finally { reader.releaseLock(); } onComplete?.(); } catch (error: any) { if (error.name !== "AbortError") { onError?.(error.message); } } })(); return abortController; }