65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
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();
|
|
}
|
|
}
|