2025-03-28 15:19:42 +08:00
|
|
|
|
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) {
|
2025-10-21 22:58:47 +08:00
|
|
|
|
// #ifdef MP-WEIXIN
|
|
|
|
|
|
return StreamRequestMiniProgram(url, data, onDataReceived, onError, onComplete);
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
|
|
// #ifdef H5
|
|
|
|
|
|
return StreamRequestH5(url, data, onDataReceived, onError, onComplete);
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
|
|
// #ifndef H5 || MP-WEIXIN
|
|
|
|
|
|
return StreamRequestH5(url, data, onDataReceived, onError, onComplete);
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 微信小程序流式请求实现
|
|
|
|
|
|
function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onComplete) {
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
const Authorization = userStore.token ? encodeURIComponent(userStore.token) : '';
|
|
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
let buffer = '';
|
2026-01-22 12:57:01 +08:00
|
|
|
|
let hasReceivedContent = false;
|
2025-10-21 22:58:47 +08:00
|
|
|
|
|
|
|
|
|
|
const requestTask = uni.request({
|
|
|
|
|
|
url: config.StreamBaseURl + url,
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
data: data,
|
|
|
|
|
|
header: {
|
|
|
|
|
|
"Authorization": Authorization,
|
|
|
|
|
|
"Accept": "text/event-stream",
|
|
|
|
|
|
"Content-Type": "application/json;charset=UTF-8"
|
|
|
|
|
|
},
|
|
|
|
|
|
enableChunked: true, // 启用分块传输
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
console.log('📡 Stream request completed');
|
|
|
|
|
|
onComplete && onComplete();
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
console.error('Stream 请求失败:', err);
|
|
|
|
|
|
onError && onError(err);
|
|
|
|
|
|
reject(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听分块数据
|
|
|
|
|
|
requestTask.onChunkReceived((res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
|
|
|
const chunk = decoder.decode(new Uint8Array(res.data));
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('📦 收到分块数据:', chunk);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
buffer += chunk;
|
|
|
|
|
|
|
|
|
|
|
|
let lines = buffer.split("\n");
|
|
|
|
|
|
buffer = lines.pop() || ''; // 保留不完整的行
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('📝 解析到行:', lines.length, '行,缓冲区剩余:', buffer.length, '字符');
|
2025-10-21 22:58:47 +08:00
|
|
|
|
|
|
|
|
|
|
for (let line of lines) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('🔍 处理行:', line);
|
|
|
|
|
|
// 处理重复的 data: 前缀
|
|
|
|
|
|
let processedLine = line;
|
|
|
|
|
|
// 移除所有开头的 data: 前缀(无论是否有空格),直到只剩下一个或没有
|
|
|
|
|
|
while (processedLine.startsWith("data:")) {
|
|
|
|
|
|
// 检查是否还有另一个 data: 前缀
|
|
|
|
|
|
const nextPart = processedLine.slice(5).trimStart();
|
|
|
|
|
|
if (nextPart.startsWith("data:")) {
|
|
|
|
|
|
processedLine = nextPart;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (processedLine.startsWith("data: ")) {
|
|
|
|
|
|
const jsonData = processedLine.slice(6).trim();
|
|
|
|
|
|
console.log('📄 提取的JSON数据:', jsonData);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
if (jsonData === "[DONE]") {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('✅ 收到结束标记 [DONE]');
|
2025-10-21 22:58:47 +08:00
|
|
|
|
onComplete && onComplete();
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (jsonData && jsonData.trim()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsedData = JSON.parse(jsonData);
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('🔧 解析后的JSON:', parsedData);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
|
2026-01-22 12:57:01 +08:00
|
|
|
|
// 检查是否有错误信息
|
|
|
|
|
|
const finishReason = parsedData?.choices?.[0]?.finish_reason;
|
|
|
|
|
|
if (finishReason === "error") {
|
|
|
|
|
|
let errorContent = parsedData?.choices?.[0]?.delta?.content || "流式请求失败";
|
|
|
|
|
|
console.error('❌ 收到错误信息:', errorContent);
|
|
|
|
|
|
|
|
|
|
|
|
// 优化token超限错误提示
|
|
|
|
|
|
if (errorContent.includes("maximum input ids length")) {
|
|
|
|
|
|
errorContent = "对话历史过长,请尝试清除部分历史记录或简化问题";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 只有当未收到正常内容时才显示错误信息
|
|
|
|
|
|
if (!hasReceivedContent) {
|
|
|
|
|
|
// 显示错误信息给用户
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: errorContent,
|
|
|
|
|
|
icon: 'none',
|
|
|
|
|
|
duration: 3000
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-21 22:58:47 +08:00
|
|
|
|
// 处理标准的choices格式
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else if (parsedData?.choices?.[0]?.delta?.content) {
|
2025-10-21 22:58:47 +08:00
|
|
|
|
const content = parsedData.choices[0].delta.content;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(content):', content);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理reasoning_content
|
|
|
|
|
|
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
|
|
|
|
|
|
const content = parsedData.choices[0].delta.reasoning_content;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(reasoning_content):', content);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理tool响应
|
|
|
|
|
|
else if (parsedData?.tool?.response) {
|
|
|
|
|
|
const content = parsedData.tool.response;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(tool.response):', content);
|
|
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理其他可能的内容格式
|
|
|
|
|
|
else if (parsedData?.content) {
|
|
|
|
|
|
// 直接返回content字段的情况
|
|
|
|
|
|
const content = parsedData.content;
|
|
|
|
|
|
if (content) {
|
|
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(direct content):', content);
|
|
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理完整的text字段(非流式)
|
|
|
|
|
|
else if (parsedData?.choices?.[0]?.text) {
|
|
|
|
|
|
const content = parsedData.choices[0].text;
|
|
|
|
|
|
if (content) {
|
|
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(full text):', content);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else {
|
|
|
|
|
|
console.warn('⚠️ 未匹配到任何内容格式:', parsedData);
|
|
|
|
|
|
}
|
2025-10-21 22:58:47 +08:00
|
|
|
|
} catch (e) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.error("JSON 解析失败:", e.message, "原始数据:", jsonData);
|
2025-10-21 22:58:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else if (processedLine.trim()) {
|
|
|
|
|
|
// 处理非data:开头的行
|
|
|
|
|
|
console.warn('⚠️ 收到非data:开头的行:', processedLine);
|
|
|
|
|
|
}
|
2025-10-21 22:58:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('处理分块数据失败:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// H5 流式请求实现(原有的 fetch 实现)
|
|
|
|
|
|
function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
|
2025-03-28 15:19:42 +08:00
|
|
|
|
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 = "";
|
2025-10-13 16:01:49 +08:00
|
|
|
|
let retryCount = 0;
|
|
|
|
|
|
const maxRetries = 3;
|
2026-01-22 12:57:01 +08:00
|
|
|
|
let hasReceivedContent = false;
|
2025-10-13 16:01:49 +08:00
|
|
|
|
|
2025-03-28 15:19:42 +08:00
|
|
|
|
while (true) {
|
|
|
|
|
|
const {
|
|
|
|
|
|
done,
|
|
|
|
|
|
value
|
|
|
|
|
|
} = await reader.read();
|
|
|
|
|
|
|
2025-10-13 16:01:49 +08:00
|
|
|
|
if (done) {
|
|
|
|
|
|
console.log("📡 Stream reading completed");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-03-28 15:19:42 +08:00
|
|
|
|
|
|
|
|
|
|
buffer += decoder.decode(value, {
|
|
|
|
|
|
stream: true
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let lines = buffer.split("\n");
|
|
|
|
|
|
buffer = lines.pop(); // 可能是不完整的 JSON 片段,留待下次解析
|
2025-10-13 16:01:49 +08:00
|
|
|
|
|
|
|
|
|
|
console.log(`📦 Processing ${lines.length} lines, buffer length: ${buffer.length}`);
|
|
|
|
|
|
|
2025-03-28 15:19:42 +08:00
|
|
|
|
for (let line of lines) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('🔍 处理行:', line);
|
|
|
|
|
|
// 处理重复的 data: 前缀
|
|
|
|
|
|
let processedLine = line;
|
|
|
|
|
|
// 移除所有开头的 data: 前缀(无论是否有空格),直到只剩下一个或没有
|
|
|
|
|
|
while (processedLine.startsWith("data:")) {
|
|
|
|
|
|
// 检查是否还有另一个 data: 前缀
|
|
|
|
|
|
const nextPart = processedLine.slice(5).trimStart();
|
|
|
|
|
|
if (nextPart.startsWith("data:")) {
|
|
|
|
|
|
processedLine = nextPart;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (processedLine.startsWith("data: ")) {
|
|
|
|
|
|
const jsonData = processedLine.slice(6).trim();
|
|
|
|
|
|
console.log('📄 提取的JSON数据:', jsonData);
|
2025-03-28 15:19:42 +08:00
|
|
|
|
if (jsonData === "[DONE]") {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('✅ 收到结束标记 [DONE]');
|
2025-03-28 15:19:42 +08:00
|
|
|
|
onComplete && onComplete();
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2025-10-13 16:01:49 +08:00
|
|
|
|
// 检查JSON数据是否完整
|
|
|
|
|
|
if (jsonData && jsonData.trim() && jsonData !== "[DONE]") {
|
|
|
|
|
|
const parsedData = JSON.parse(jsonData);
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('🔧 解析后的JSON:', parsedData);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
|
2026-01-22 12:57:01 +08:00
|
|
|
|
// 检查是否有错误信息
|
|
|
|
|
|
const finishReason = parsedData?.choices?.[0]?.finish_reason;
|
|
|
|
|
|
if (finishReason === "error") {
|
|
|
|
|
|
let errorContent = parsedData?.choices?.[0]?.delta?.content || "流式请求失败";
|
|
|
|
|
|
console.error('❌ 收到错误信息:', errorContent);
|
|
|
|
|
|
|
|
|
|
|
|
// 优化token超限错误提示
|
|
|
|
|
|
if (errorContent.includes("maximum input ids length")) {
|
|
|
|
|
|
errorContent = "对话历史过长,请尝试清除部分历史记录或简化问题";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 只有当未收到正常内容时才显示错误信息
|
|
|
|
|
|
if (!hasReceivedContent) {
|
|
|
|
|
|
// 显示错误信息给用户
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: errorContent,
|
|
|
|
|
|
icon: 'none',
|
|
|
|
|
|
duration: 3000
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-13 16:01:49 +08:00
|
|
|
|
// 处理标准的choices格式
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else if (parsedData?.choices?.[0]?.delta?.content) {
|
2025-10-13 16:01:49 +08:00
|
|
|
|
const content = parsedData.choices[0].delta.content;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(content):', content);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理reasoning_content
|
|
|
|
|
|
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
|
|
|
|
|
|
const content = parsedData.choices[0].delta.reasoning_content;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(reasoning_content):', content);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理tool响应
|
|
|
|
|
|
else if (parsedData?.tool?.response) {
|
|
|
|
|
|
const content = parsedData.tool.response;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(tool.response):', content);
|
|
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理其他可能的内容格式
|
|
|
|
|
|
else if (parsedData?.content) {
|
|
|
|
|
|
// 直接返回content字段的情况
|
|
|
|
|
|
const content = parsedData.content;
|
|
|
|
|
|
if (content) {
|
|
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(direct content):', content);
|
|
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理完整的text字段(非流式)
|
|
|
|
|
|
else if (parsedData?.choices?.[0]?.text) {
|
|
|
|
|
|
const content = parsedData.choices[0].text;
|
|
|
|
|
|
if (content) {
|
|
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(full text):', content);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理其他格式的数据(如jobs_array_number, durationSeconds等)
|
|
|
|
|
|
else {
|
|
|
|
|
|
console.log("📦 收到非内容数据:", Object.keys(parsedData));
|
|
|
|
|
|
}
|
2025-03-28 15:19:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
2025-10-13 16:01:49 +08:00
|
|
|
|
console.error("JSON 解析失败:", e.message, "原始数据长度:", jsonData.length, "数据预览:", jsonData.substring(0, 100) + "...");
|
|
|
|
|
|
// 不抛出错误,继续处理下一个数据块
|
2025-03-28 15:19:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else if (processedLine.trim()) {
|
|
|
|
|
|
// 处理非data:开头的行
|
|
|
|
|
|
console.warn('⚠️ 收到非data:开头的行:', processedLine);
|
|
|
|
|
|
}
|
2025-03-28 15:19:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-13 16:01:49 +08:00
|
|
|
|
// 处理剩余的缓冲区数据
|
|
|
|
|
|
if (buffer.trim()) {
|
|
|
|
|
|
console.log("📦 Processing remaining buffer:", buffer.substring(0, 100) + "...");
|
|
|
|
|
|
const lines = buffer.split("\n");
|
|
|
|
|
|
for (let line of lines) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('🔍 处理剩余缓冲区行:', line);
|
|
|
|
|
|
// 处理重复的 data: 前缀
|
|
|
|
|
|
let processedLine = line;
|
|
|
|
|
|
// 移除所有开头的 data: 前缀(无论是否有空格),直到只剩下一个或没有
|
|
|
|
|
|
while (processedLine.startsWith("data:")) {
|
|
|
|
|
|
// 检查是否还有另一个 data: 前缀
|
|
|
|
|
|
const nextPart = processedLine.slice(5).trimStart();
|
|
|
|
|
|
if (nextPart.startsWith("data:")) {
|
|
|
|
|
|
processedLine = nextPart;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (processedLine.startsWith("data: ")) {
|
|
|
|
|
|
const jsonData = processedLine.slice(6).trim();
|
|
|
|
|
|
console.log('📄 提取的剩余JSON数据:', jsonData);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
if (jsonData && jsonData !== "[DONE]") {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsedData = JSON.parse(jsonData);
|
2026-01-22 12:57:01 +08:00
|
|
|
|
console.log('🔧 解析后的剩余JSON:', parsedData);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
|
2026-01-22 12:57:01 +08:00
|
|
|
|
// 检查是否有错误信息
|
|
|
|
|
|
const finishReason = parsedData?.choices?.[0]?.finish_reason;
|
|
|
|
|
|
if (finishReason === "error") {
|
|
|
|
|
|
let errorContent = parsedData?.choices?.[0]?.delta?.content || "流式请求失败";
|
|
|
|
|
|
console.error('❌ 收到错误信息:', errorContent);
|
|
|
|
|
|
|
|
|
|
|
|
// 优化token超限错误提示
|
|
|
|
|
|
if (errorContent.includes("maximum input ids length")) {
|
|
|
|
|
|
errorContent = "对话历史过长,请尝试清除部分历史记录或简化问题";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 只有当未收到正常内容时才显示错误信息
|
|
|
|
|
|
if (!hasReceivedContent) {
|
|
|
|
|
|
// 显示错误信息给用户
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: errorContent,
|
|
|
|
|
|
icon: 'none',
|
|
|
|
|
|
duration: 3000
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-13 16:01:49 +08:00
|
|
|
|
// 处理标准的choices格式
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else if (parsedData?.choices?.[0]?.delta?.content) {
|
2025-10-13 16:01:49 +08:00
|
|
|
|
const content = parsedData.choices[0].delta.content;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(content):', content);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理reasoning_content
|
|
|
|
|
|
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
|
|
|
|
|
|
const content = parsedData.choices[0].delta.reasoning_content;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(reasoning_content):', content);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理tool响应
|
|
|
|
|
|
else if (parsedData?.tool?.response) {
|
|
|
|
|
|
const content = parsedData.tool.response;
|
|
|
|
|
|
if (content) {
|
2026-01-22 12:57:01 +08:00
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(tool.response):', content);
|
|
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理其他可能的内容格式
|
|
|
|
|
|
else if (parsedData?.content) {
|
|
|
|
|
|
// 直接返回content字段的情况
|
|
|
|
|
|
const content = parsedData.content;
|
|
|
|
|
|
if (content) {
|
|
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(direct content):', content);
|
|
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 处理完整的text字段(非流式)
|
|
|
|
|
|
else if (parsedData?.choices?.[0]?.text) {
|
|
|
|
|
|
const content = parsedData.choices[0].text;
|
|
|
|
|
|
if (content) {
|
|
|
|
|
|
hasReceivedContent = true;
|
|
|
|
|
|
console.log('📤 调用onDataReceived(full text):', content);
|
2025-10-13 16:01:49 +08:00
|
|
|
|
onDataReceived && onDataReceived(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn("处理剩余数据时JSON解析失败:", e.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 12:57:01 +08:00
|
|
|
|
else if (processedLine.trim()) {
|
|
|
|
|
|
// 处理非data:开头的行
|
|
|
|
|
|
console.warn('⚠️ 收到非data:开头的剩余行:', processedLine);
|
|
|
|
|
|
}
|
2025-10-13 16:01:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-28 15:19:42 +08:00
|
|
|
|
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({
|
2026-01-22 12:57:01 +08:00
|
|
|
|
title: msg || '请求失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
// 拒绝Promise并提供详细错误信息
|
|
|
|
|
|
const err = new Error(msg || '请求失败,服务器返回错误码: ' + code)
|
|
|
|
|
|
err.error = resData
|
|
|
|
|
|
reject(err)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 处理非200状态码
|
|
|
|
|
|
const errorMsg = `请求失败,HTTP状态码: ${resData.statusCode}`
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: errorMsg,
|
2025-03-28 15:19:42 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
2026-01-22 12:57:01 +08:00
|
|
|
|
const err = new Error(errorMsg)
|
|
|
|
|
|
err.error = resData
|
|
|
|
|
|
reject(err)
|
2025-03-28 15:19:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (resData.data?.code === 401 || resData.data?.code === 402) {
|
|
|
|
|
|
useUserStore().logOut()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
reject(err)
|
|
|
|
|
|
},
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|