import { fetchAuthenticated } from "../../../auth/api-client"; import type { AssistantEvent, AssistantRequest, } from "../../../shared/assets/assistant"; export class AssistantRequestError extends Error { constructor( message: string, public status: number, ) { super(message); this.name = "AssistantRequestError"; } } export async function askAssetAssistant( input: AssistantRequest, signal: AbortSignal, onEvent: (event: AssistantEvent) => void, ) { const response = await fetchAuthenticated("/api/asset-assistant/ask", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input), signal, }); if (!response.ok) { const body = await response.json().catch(() => null); throw new AssistantRequestError( body?.error || "查询服务暂时不可用。", response.status, ); } if (!response.body) throw new Error("当前浏览器无法读取查询进度。"); const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = "", completed = false; try { while (true) { const { value, done } = await reader.read(); buffer += decoder.decode(value, { stream: !done }); const frames = buffer.split(/\r?\n\r?\n/); buffer = frames.pop() || ""; for (const frame of frames) { const data = frame .split(/\r?\n/) .filter((line) => line.startsWith("data:")) .map((line) => line.slice(5).trim()) .join("\n"); if (!data) continue; const event = JSON.parse(data) as AssistantEvent; if (event.type === "result" || event.type === "error") completed = true; onEvent(event); } if (done) break; } if (!completed && !signal.aborted) throw new Error("连接中断,请重新提问。"); } finally { reader.releaseLock(); } }