import { ChatRequest, ChatResponse, ErrorResponse, LmConfigListParams, LmConfigAddReq, LmConfigEditReq, LmConfigStatusReq, LmConfigDeleteParams, LmConfigGetParams } from '/@/api/assist/type' import { get, post, del, put } from '/@/utils/request' import getOrigin from '/@/utils/origin' import { getToken } from "/@/utils/auth"; export default { model: { // 大语言模型配置列表 getList: (params?: LmConfigListParams) => get('/system/lmconfig/list', params), // 获取大语言模型配置信息 detail: (params: LmConfigGetParams) => get('/system/lmconfig/get', params), // 添加大语言模型配置 add: (data: LmConfigAddReq) => post('/system/lmconfig/add', data), // 修改大语言模型配置 edit: (data: LmConfigEditReq) => put('/system/lmconfig/edit', data), // 删除大语言模型配置 del: (params: LmConfigDeleteParams) => del('/system/lmconfig/delete', params), // 设置大语言模型配置状态 setStatus: (data: LmConfigStatusReq) => put('/system/lmconfig/status', data), }, // SSE聊天方法 chat: ( data: { chatRequest: ChatRequest, // eslint-disable-next-line no-unused-vars onReceive: (resp: ChatResponse) => void, // eslint-disable-next-line no-unused-vars onComplete?: (error: Error|undefined)=> void }, ) => { const {chatRequest, onReceive,onComplete} = data //FIXME 需要抹掉 chatRequest.modelMcpId = 1 as unknown as number[] chatRequest["UserId"] = 10 // 构建SSE URL const baseURL = getOrigin(); const url = `${baseURL}/ai/chat`; // 使用fetch API实现SSE POST请求(EventSource不支持POST和自定义headers) const controller = new AbortController(); // 使用fetch API实现SSE POST请求 const startSSE = async () => { try { const headers: Record = { 'Content-Type': 'application/json', 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache', }; // 添加认证token const token = getToken(); if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(url, { method: 'POST', headers, body: JSON.stringify(chatRequest), signal: controller.signal, }).catch((e:Error)=> e); if (response instanceof Error) { throw new Error('无法连接到服务器'); } if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const reader = response.body?.getReader(); const decoder = new TextDecoder(); if (!reader) { throw new Error('Response body is not readable'); } // 读取SSE流 let currentEvent = ''; let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) { break; } buffer += decoder.decode(value, { stream: true }); // 按双换行符分割事件块 const eventBlocks = buffer.split('\n\n'); // 保留最后一个可能不完整的事件块 buffer = eventBlocks.pop() || ''; for (const eventBlock of eventBlocks) { if (!eventBlock.trim()) continue; const lines = eventBlock.split('\n'); let eventType = ''; let dataLines: string[] = []; // 解析事件块中的每一行 for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine.startsWith('event:')) { eventType = trimmedLine.substring(6).trim(); } else if (trimmedLine.startsWith('data:')) { // 提取data后的内容,保留原始格式(包括可能的空格) const dataContent = line.substring(line.indexOf('data:') + 5); dataLines.push(dataContent); } } // 如果有事件类型,更新当前事件 if (eventType) { currentEvent = eventType; } // 如果有数据行,处理数据 if (dataLines.length > 0) { // 将多行data合并,用换行符连接 const data = dataLines.join('\n'); // 根据事件类型处理数据 switch (currentEvent) { case 'message': { const messageResponse: ChatResponse = { type: 'message', message: data }; onReceive(messageResponse); break; } case 'toolcall': { const tools = JSON.parse(data); const toolcallResponse: ChatResponse = { type: 'toolcall', request: { name: tools["name"], data: tools["arguments"] } }; onReceive(toolcallResponse); break; } case 'toolres': { const tools = JSON.parse(data); const toolresResponse: ChatResponse = { type: 'toolres', response: { name: tools["name"], data: tools["response"] }, }; onReceive(toolresResponse); break; } case 'error': { const errorResponse: ErrorResponse = { type: 'error', error: data } onReceive(errorResponse) break } case 'meta': { break } default: { // 如果没有明确的事件类型,默认作为消息处理 const defaultResponse: ChatResponse = { type: 'message', message: data }; onReceive(defaultResponse); break; } } } } // 处理buffer中剩余的可能完整的单行事件 const remainingLines = buffer.split('\n'); const completeLines = remainingLines.slice(0, -1); buffer = remainingLines[remainingLines.length - 1] || ''; for (const line of completeLines) { const trimmedLine = line.trim(); if (trimmedLine === '') { continue; } if (trimmedLine.startsWith('event:')) { currentEvent = trimmedLine.substring(6).trim(); continue; } if (trimmedLine.startsWith('data:')) { const data = line.substring(line.indexOf('data:') + 5); // 根据事件类型处理数据 switch (currentEvent) { case 'message': { const messageResponse: ChatResponse = { type: 'message', message: data }; onReceive(messageResponse); break; } case 'toolcall': { try { const tools = JSON.parse(data); const toolcallResponse: ChatResponse = { type: 'toolcall', request: { name: tools["name"], data: tools["arguments"] } }; onReceive(toolcallResponse); } catch (error) { console.error('解析toolcall数据失败:', error, '原始数据:', data); } break; } case 'toolres': { try { const tools = JSON.parse(data); const toolresResponse: ChatResponse = { type: 'toolres', response: { name: tools["name"], data: tools["response"] }, }; onReceive(toolresResponse); } catch (error) { console.error('解析toolres数据失败:', error, '原始数据:', data); } break; } default: { // 如果没有明确的事件类型,默认作为消息处理 const defaultResponse: ChatResponse = { type: 'message', message: data }; onReceive(defaultResponse); break; } } } } } onComplete?.(undefined) } catch (error: any) { if (error.name !== 'AbortError') { console.error('SSE连接错误:', error); } onComplete?.(error) } }; // 启动SSE连接 startSSE(); // 返回关闭连接的函数 return () => { controller.abort(); }; }, }