import { getTokenKeySessionStorage } from "@/utils/auth"; /** * 创建聊天流式连接 * @param {Object} params 请求参数 * @param {string} params.prompt 用户输入 * @param {string} params.userId 用户ID * @param {string} params.userName 用户名 * @param {string} [params.conversationId] 会话ID * @returns {Object} 包含stream和cancel方法的对象 */ export function createChatStream(params) { const requestId = `req-${Date.now()}-${Math.random() .toString(36) .slice(2, 8)}`; const url = `${process.env.VUE_APP_BASE_API}/aitutor/aichat/stream`; const token = getTokenKeySessionStorage(); if (!token) { throw new Error("请先登录"); } const controller = new AbortController(); const fetchPromise = fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, "X-Request-ID": requestId, }, body: JSON.stringify({ query: params.prompt, user_id: params.userId, user_name: params.userName, user_token: params.user_token || "123", user_role: "student", conversation_id: params.conversationId || null, }), signal: controller.signal, }).then((response) => { if (!response.ok) { throw new Error(`HTTP ${response.status}`); } if (!response.body) { throw new Error("Response body is null"); } return { reader: response.body.getReader(), decoder: new TextDecoder("utf-8"), }; }); return { stream: fetchPromise, cancel: (reason) => { if (!controller.signal.aborted) { controller.abort(reason); } }, }; } /** * 处理流式响应 * @param {ReadableStreamDefaultReader} reader 读取器 * @param {TextDecoder} decoder 文本解码器 * @param {Function} onMessage 消息回调 * @param {Function} onError 错误回调 * @param {Function} onComplete 完成回调 */ export async function processStream( reader, decoder, { onMessage, onError, onComplete } ) { 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.trim()) continue; try { const data = JSON.parse(line); if (typeof onMessage === "function") { onMessage(data); } } catch (e) { console.warn("解析消息失败:", line, e); } } } if (typeof onComplete === "function") { onComplete(); } } catch (error) { if (error.name !== "AbortError" && typeof onError === "function") { onError(error); } } finally { reader.releaseLock(); } }