| 
									
										
										
										
											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 = ''; | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         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)); | 
					
						
							|  |  |  |  |                 buffer += chunk; | 
					
						
							|  |  |  |  |                  | 
					
						
							|  |  |  |  |                 let lines = buffer.split("\n"); | 
					
						
							|  |  |  |  |                 buffer = lines.pop() || ''; // 保留不完整的行
 | 
					
						
							|  |  |  |  |                  | 
					
						
							|  |  |  |  |                 for (let line of lines) { | 
					
						
							|  |  |  |  |                     if (line.startsWith("data: ")) { | 
					
						
							|  |  |  |  |                         const jsonData = line.slice(6).trim(); | 
					
						
							|  |  |  |  |                         if (jsonData === "[DONE]") { | 
					
						
							|  |  |  |  |                             onComplete && onComplete(); | 
					
						
							|  |  |  |  |                             resolve(); | 
					
						
							|  |  |  |  |                             return; | 
					
						
							|  |  |  |  |                         } | 
					
						
							|  |  |  |  |                          | 
					
						
							|  |  |  |  |                         if (jsonData && jsonData.trim()) { | 
					
						
							|  |  |  |  |                             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.error("JSON 解析失败:", e.message); | 
					
						
							|  |  |  |  |                             } | 
					
						
							|  |  |  |  |                         } | 
					
						
							|  |  |  |  |                     } | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } 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; | 
					
						
							|  |  |  |  |              | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  |  |                     if (line.startsWith("data: ")) { | 
					
						
							|  |  |  |  |                         const jsonData = line.slice(6).trim(); | 
					
						
							|  |  |  |  |                         if (jsonData === "[DONE]") { | 
					
						
							|  |  |  |  |                             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); | 
					
						
							|  |  |  |  |                                  | 
					
						
							|  |  |  |  |                                 // 处理标准的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)); | 
					
						
							|  |  |  |  |                                 } | 
					
						
							| 
									
										
										
										
											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
										 |  |  |  |                         } | 
					
						
							|  |  |  |  |                     } | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  |  |                     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); | 
					
						
							|  |  |  |  |                             } | 
					
						
							|  |  |  |  |                         } | 
					
						
							|  |  |  |  |                     } | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |              | 
					
						
							| 
									
										
										
										
											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({ | 
					
						
							|  |  |  |  |                         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(); | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  | } |