import config from "@/config.js" import useUserStore from '@/stores/useUserStore'; /** * @param url String,请求的地址,默认:none * @param data Object,请求的参数,默认:{} * @returns promise **/ export default function StreamRequest(url, data = {}, onDataReceived, onError, onComplete) { const userStore = useUserStore(); const Authorization = userStore.token ? encodeURIComponent(userStore.token) : ''; const headers = { "Authorization": Authorization, "Accept": "text/event-stream", "Content-Type": "application/json;charset=UTF-8" }; return new Promise(async (resolve, reject) => { try { const response = await fetch(config.StreamBaseURl + url, { method: "POST", headers, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`HTTP 错误: ${response.status}`); } const reader = response.body.getReader(); const decoder = new TextDecoder("utf-8"); let buffer = ""; let retryCount = 0; const maxRetries = 3; while (true) { const { done, value } = await reader.read(); if (done) { console.log("📡 Stream reading completed"); break; } buffer += decoder.decode(value, { stream: true }); let lines = buffer.split("\n"); buffer = lines.pop(); // 可能是不完整的 JSON 片段,留待下次解析 console.log(`📦 Processing ${lines.length} lines, buffer length: ${buffer.length}`); for (let line of lines) { if (line.startsWith("data: ")) { const jsonData = line.slice(6).trim(); if (jsonData === "[DONE]") { onComplete && onComplete(); resolve(); return; } try { // 检查JSON数据是否完整 if (jsonData && jsonData.trim() && jsonData !== "[DONE]") { const parsedData = JSON.parse(jsonData); // 处理标准的choices格式 if (parsedData?.choices?.[0]?.delta?.content) { const content = parsedData.choices[0].delta.content; if (content) { onDataReceived && onDataReceived(content); } } // 处理reasoning_content else if (parsedData?.choices?.[0]?.delta?.reasoning_content) { const content = parsedData.choices[0].delta.reasoning_content; if (content) { onDataReceived && onDataReceived(content); } } // 处理tool响应 else if (parsedData?.tool?.response) { const content = parsedData.tool.response; if (content) { onDataReceived && onDataReceived(content); } } // 处理其他格式的数据(如jobs_array_number, durationSeconds等) else { console.log("📦 收到非内容数据:", Object.keys(parsedData)); } } } catch (e) { console.error("JSON 解析失败:", e.message, "原始数据长度:", jsonData.length, "数据预览:", jsonData.substring(0, 100) + "..."); // 不抛出错误,继续处理下一个数据块 } } } } // 处理剩余的缓冲区数据 if (buffer.trim()) { console.log("📦 Processing remaining buffer:", buffer.substring(0, 100) + "..."); const lines = buffer.split("\n"); for (let line of lines) { if (line.startsWith("data: ")) { const jsonData = line.slice(6).trim(); if (jsonData && jsonData !== "[DONE]") { try { const parsedData = JSON.parse(jsonData); // 处理标准的choices格式 if (parsedData?.choices?.[0]?.delta?.content) { const content = parsedData.choices[0].delta.content; if (content) { onDataReceived && onDataReceived(content); } } // 处理reasoning_content else if (parsedData?.choices?.[0]?.delta?.reasoning_content) { const content = parsedData.choices[0].delta.reasoning_content; if (content) { onDataReceived && onDataReceived(content); } } // 处理tool响应 else if (parsedData?.tool?.response) { const content = parsedData.tool.response; if (content) { onDataReceived && onDataReceived(content); } } } catch (e) { console.warn("处理剩余数据时JSON解析失败:", e.message); } } } } } onComplete && onComplete(); resolve(); } catch (error) { console.error("Stream 请求失败:", error); onError && onError(error); reject(error); } }); } /** * @param url String,请求的地址,默认:none * @param data Object,请求的参数,默认:{} * @param method String,请求的方式,默认:GET * @param loading Boolean,是否需要loading ,默认:false * @param header Object,headers,默认:{} * @returns promise **/ export function chatRequest(url, data = {}, method = 'GET', loading = false, headers = {}) { if (loading) { uni.showLoading({ title: '请稍后', mask: true }) } let Authorization = '' if (useUserStore().token) { Authorization = `${useUserStore().token}` } const header = headers || {}; header["Authorization"] = encodeURIComponent(Authorization); return new Promise((resolve, reject) => { uni.request({ url: config.StreamBaseURl + url, method: method, data: data, header, success: resData => { // 响应拦截 if (resData.statusCode === 200) { const { code, msg } = resData.data if (code === 200) { resolve(resData.data) return } uni.showToast({ title: msg, icon: 'none' }) } if (resData.data?.code === 401 || resData.data?.code === 402) { useUserStore().logOut() } const err = new Error('请求出现异常,请联系工作人员') err.error = resData reject(err) }, fail: (err) => { reject(err) }, complete: () => { if (loading) { uni.hideLoading(); } } }); }) }