import api from "@/lib/api-client.ts"; import { AiGenerateDto, AiContentResponse, AiStreamChunk, AiStreamError, } from "@/ee/ai/types/ai.types.ts"; export async function generateAiContent( data: AiGenerateDto, ): Promise { const req = await api.post("/ai/generate", data); return req.data; } export async function generateAiContentStream( data: AiGenerateDto, onChunk: (chunk: AiStreamChunk) => void, onError?: (error: AiStreamError) => void, onComplete?: () => void, ): Promise { const abortController = new AbortController(); try { const response = await fetch("/api/ai/generate/stream", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), signal: abortController.signal, credentials: "include", // This ensures cookies are sent, matching axios withCredentials }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const reader = response.body?.getReader(); const decoder = new TextDecoder(); if (!reader) { throw new Error("Response body is not readable"); } const processStream = async () => { 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); if (parsed.error) { onError?.(parsed); } else { onChunk(parsed); } } catch (e) { // Skip invalid JSON } } } } } catch (error) { if (error.name !== "AbortError") { onError?.({ error: error.message }); } } finally { reader.releaseLock(); } }; processStream(); } catch (error) { onError?.({ error: error.message }); } return abortController; }