import type { AiRequestBody } from "@/lib/ai/types"; import { decodeHexByteEscapes } from "@/lib/ai/decode-text"; function emitText(text: string, onUpdate: (text: string) => void) { onUpdate(decodeHexByteEscapes(text)); } function parseApiError(text: string, status: number): string { const trimmed = text.trim(); if ( trimmed.startsWith("404") ) { return `AI 接口未到达后端 (${status})。请确认 Nginx 反代到 3130 且包含 /api/ 路径。`; } return trimmed.slice(0, 800) || `AI 请求失败 (${status})`; } export async function streamAiCompletion( body: AiRequestBody, onUpdate: (text: string) => void, ): Promise { const res = await fetch("/api/ai", { method: "POST", cache: "no-store", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { throw new Error(parseApiError(await res.text(), res.status)); } if (!res.body) { throw new Error("AI 响应为空"); } const reader = res.body.getReader(); const decoder = new TextDecoder(); let text = ""; while (true) { const { done, value } = await reader.read(); if (done) { break; } text += decoder.decode(value, { stream: true }); emitText(text, onUpdate); } text += decoder.decode(); emitText(text, onUpdate); if (!text.trim()) { throw new Error("AI 返回内容为空,请检查模型配置或稍后重试"); } return decodeHexByteEscapes(text); }