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