Files
zhxg_pc/src/utils/ai_stream.js

112 lines
2.8 KiB
JavaScript
Raw Normal View History

import { getTokenKeySessionStorage } from "@/utils/auth";
2025-08-14 15:43:24 +08:00
/**
* 创建聊天流式连接
* @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)}`;
2025-08-15 16:51:24 +08:00
const url = `${process.env.VUE_APP_BASE_API}/aitutor/aichat/stream`;
const token = getTokenKeySessionStorage();
2025-08-14 15:43:24 +08:00
if (!token) {
throw new Error("请先登录");
}
2025-08-14 15:43:24 +08:00
const controller = new AbortController();
2025-08-14 15:43:24 +08:00
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");
2025-08-14 15:43:24 +08:00
}
return {
reader: response.body.getReader(),
decoder: new TextDecoder("utf-8"),
};
});
return {
stream: fetchPromise,
cancel: (reason) => {
if (!controller.signal.aborted) {
controller.abort(reason);
}
},
};
2025-08-14 15:43:24 +08:00
}
/**
* 处理流式响应
* @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 = "";
2025-08-14 15:43:24 +08:00
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
2025-08-14 15:43:24 +08:00
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
2025-08-14 15:43:24 +08:00
for (const line of lines) {
if (!line.trim()) continue;
2025-08-14 15:43:24 +08:00
try {
const data = JSON.parse(line);
if (typeof onMessage === "function") {
onMessage(data);
}
} catch (e) {
console.warn("解析消息失败:", line, e);
2025-08-14 15:43:24 +08:00
}
}
}
2025-08-14 15:43:24 +08:00
if (typeof onComplete === "function") {
onComplete();
}
} catch (error) {
if (error.name !== "AbortError" && typeof onError === "function") {
onError(error);
2025-08-14 15:43:24 +08:00
}
} finally {
reader.releaseLock();
}
}