/** * @description AI对话相关API * @author AI Assistant * @since 2025-11-04 */ import { api } from '@/apis/index'; import { API_BASE_URL } from '@/config'; import type { AiConversation, AiMessage, ChatRequest, ResultDomain, StreamCallback } from '@/types'; /** * AI对话API服务 */ export const chatApi = { /** * 流式对话(SSE)- 两步法:POST准备 + GET建立SSE * @param request 对话请求 * @param callback 流式回调 * @returns Promise> */ async streamChat(request: ChatRequest, callback?: StreamCallback): Promise> { return new Promise((resolve, reject) => { // 使用IIFE包装async逻辑,避免Promise executor是async的警告 (async () => { try { const token = localStorage.getItem('token'); const tokenData = token ? JSON.parse(token).value : ''; // 第1步:POST准备会话,获取sessionId const prepareResponse = await api.post('/ai/chat/stream/prepare', { agentId: request.agentId, conversationId: request.conversationId || '', query: request.query, files: request.files || [] }, { showLoading: false }); if (!prepareResponse.data.success || !prepareResponse.data.data) { throw new Error(prepareResponse.data.message || '准备会话失败'); } const sessionId = prepareResponse.data.data; console.log('[会话创建成功] sessionId:', sessionId); // 第2步:GET建立SSE连接 const eventSource = new EventSource( `${API_BASE_URL}/ai/chat/stream?sessionId=${sessionId}&token=${tokenData}` ); // 通知外部EventSource已创建 callback?.onStart?.(eventSource); // eslint-disable-next-line @typescript-eslint/no-unused-vars let fullMessage = ''; // 累积完整消息内容 // 监听初始化事件(包含messageId和conversationId) eventSource.addEventListener('init', (event) => { try { const initData = JSON.parse(event.data); console.log('[初始化数据]', initData); // 通知外部保存messageId(用于停止生成) if (callback?.onInit) { callback.onInit(initData); } } catch (e) { console.warn('解析init事件失败:', event.data); } }); // 监听标准消息事件 eventSource.addEventListener('message', (event) => { try { // 解析JSON字符串,处理Unicode转义 const data = JSON.parse(event.data); fullMessage += data; callback?.onMessage?.(data); } catch (e) { // 如果不是JSON,直接使用原始数据 const data = event.data; fullMessage += data; callback?.onMessage?.(data); } }); // 监听结束事件 eventSource.addEventListener('end', (event) => { const metadata = JSON.parse(event.data); callback?.onMessageEnd?.(metadata); eventSource.close(); resolve({ code: 200, success: true, login: true, auth: true, data: metadata as AiMessage, message: '对话成功' }); }); // 监听所有Dify原始事件(workflow_started, node_started等) const difyEventTypes = [ 'dify_workflow_started', 'dify_node_started', 'dify_node_finished', 'dify_workflow_finished', 'dify_message', 'dify_agent_message', 'dify_message_end', 'dify_message_file', 'dify_agent_thought', 'dify_ping' ]; difyEventTypes.forEach(eventType => { eventSource.addEventListener(eventType, (event: any) => { try { const eventData = JSON.parse(event.data); // 调用自定义的Dify事件回调 if (callback?.onDifyEvent) { const cleanEventType = eventType.replace('dify_', ''); callback.onDifyEvent(cleanEventType, eventData); } } catch (e) { console.warn(`解析Dify事件失败 ${eventType}:`, event.data); } }); }); // 监听错误事件 eventSource.addEventListener('error', (event: any) => { // 如果 data 为空,说明是正常的流结束信号(有些工作流不发送end事件) if (!event.data || event.data.trim() === '') { console.log('[SSE流结束] 收到空error事件,当作正常结束处理'); // 触发结束回调(如果有的话) callback?.onMessageEnd?.({ conversationId: request.conversationId, messageId: '', answer: fullMessage } as any); eventSource.close(); // 正常结束 resolve({ code: 200, success: true, login: true, auth: true, data: { conversationId: request.conversationId, answer: fullMessage } as any, message: '对话已结束' }); return; } const error = new Error(event.data); callback?.onError?.(error); eventSource.close(); reject(error); }); eventSource.onerror = (error) => { callback?.onError?.(error as unknown as Error); eventSource.close(); reject(error); }; } catch (error) { console.error('流式对话失败:', error); callback?.onError?.(error as Error); reject(error); } })(); // 立即执行IIFE }); }, /** * 阻塞式对话(非流式) * @param request 对话请求 * @returns Promise> */ async blockingChat(request: ChatRequest): Promise> { const response = await api.post('/ai/chat/blocking', request); return response.data; }, /** * 停止对话生成(通过消息ID) * @param messageId 消息ID * @returns Promise> */ async stopChat(messageId: string): Promise> { const response = await api.post(`/ai/chat/stop/${messageId}`); return response.data; }, /** * 停止对话生成(通过Dify TaskID) * @param taskId Dify任务ID * @param agentId 智能体ID * @returns Promise> */ async stopChatByTaskId(taskId: string, agentId: string): Promise> { const response = await api.post('/ai/chat/stop-by-taskid', { taskId, agentId }); return response.data; }, /** * 创建新会话 * @param agentId 智能体ID * @param title 会话标题(可选) * @returns Promise> */ async createConversation(agentId: string, title?: string): Promise> { const response = await api.post('/ai/chat/conversation', { agentId, title }, { showLoading: false }); return response.data; }, /** * 获取会话信息 * @param conversationId 会话ID * @returns Promise> */ async getConversation(conversationId: string): Promise> { const response = await api.get(`/ai/chat/conversation/${conversationId}`); return response.data; }, /** * 更新会话 * @param conversation 会话信息 * @returns Promise> */ async updateConversation(conversation: AiConversation): Promise> { const response = await api.put('/ai/chat/conversation', conversation, { showLoading: false }); return response.data; }, /** * 删除会话 * @param conversationId 会话ID * @returns Promise> */ async deleteConversation(conversationId: string): Promise> { const response = await api.delete(`/ai/chat/conversation/${conversationId}`,{}, { showLoading: false }); return response.data; }, /** * 获取用户的会话列表 * @param agentId 智能体ID(可选) * @returns Promise> */ async listUserConversations(agentId?: string): Promise> { const response = await api.get('/ai/chat/conversations', { params: { agentId } }); return response.data; }, /** * 获取会话的消息列表 * @param conversationId 会话ID * @returns Promise> */ async listMessages(conversationId: string): Promise> { const response = await api.get(`/ai/chat/conversation/${conversationId}/messages`, {},{ showLoading: false }); return response.data; }, /** * 获取单条消息 * @param messageId 消息ID * @returns Promise> */ async getMessage(messageId: string): Promise> { const response = await api.get(`/ai/chat/message/${messageId}`); return response.data; }, /** * 重新生成回答(SSE流式) * @param messageId 原消息ID * @param callback 流式回调 * @returns Promise> */ async regenerateAnswer(messageId: string, callback?: StreamCallback): Promise> { const token = localStorage.getItem('token'); const tokenData = token ? JSON.parse(token) : null; return new Promise((resolve, reject) => { // 使用配置的baseURL,SSE流式推送 const eventSource = new EventSource( `${API_BASE_URL}/ai/chat/regenerate/${messageId}?` + new URLSearchParams({ token: tokenData?.value || '' }) ); // 通知外部EventSource已创建 callback?.onStart?.(eventSource); // eslint-disable-next-line @typescript-eslint/no-unused-vars let fullMessage = ''; // 累积完整消息内容 // 监听初始化事件(包含messageId和conversationId) eventSource.addEventListener('init', (event) => { try { const initData = JSON.parse(event.data); console.log('[初始化数据-重新生成]', initData); // 通知外部保存messageId(用于停止生成) if (callback?.onInit) { callback.onInit(initData); } } catch (e) { console.warn('解析init事件失败:', event.data); } }); // 监听标准消息事件 eventSource.addEventListener('message', (event) => { try { // 解析JSON字符串,处理Unicode转义 const data = JSON.parse(event.data); fullMessage += data; callback?.onMessage?.(data); } catch (e) { // 如果不是JSON,直接使用原始数据 const data = event.data; fullMessage += data; callback?.onMessage?.(data); } }); // 监听结束事件 eventSource.addEventListener('end', (event) => { const metadata = JSON.parse(event.data); callback?.onMessageEnd?.(metadata); eventSource.close(); resolve({ code: 200, success: true, login: true, auth: true, data: metadata as AiMessage, message: '重新生成成功' }); }); // 监听所有Dify原始事件(workflow_started, node_started等) const difyEventTypes = [ 'dify_workflow_started', 'dify_node_started', 'dify_node_finished', 'dify_workflow_finished', 'dify_message', 'dify_agent_message', 'dify_message_end', 'dify_message_file', 'dify_agent_thought', 'dify_ping' ]; difyEventTypes.forEach(eventType => { eventSource.addEventListener(eventType, (event: any) => { try { const eventData = JSON.parse(event.data); // 调用自定义的Dify事件回调 if (callback?.onDifyEvent) { const cleanEventType = eventType.replace('dify_', ''); callback.onDifyEvent(cleanEventType, eventData); } } catch (e) { console.warn(`解析Dify事件失败 ${eventType}:`, event.data); } }); }); // 监听错误事件 eventSource.addEventListener('error', (event: any) => { // 如果 data 为空,说明是正常的流结束信号(有些工作流不发送end事件) if (!event.data || event.data.trim() === '') { console.log('[SSE流结束-重新生成] 收到空error事件,当作正常结束处理'); // 触发结束回调(如果有的话) callback?.onMessageEnd?.({ messageId: messageId, answer: fullMessage } as any); eventSource.close(); // 正常结束 resolve({ code: 200, success: true, login: true, auth: true, data: { messageId: messageId, answer: fullMessage } as any, message: '重新生成已结束' }); return; } const error = new Error(event.data); callback?.onError?.(error); eventSource.close(); reject(error); }); eventSource.onerror = (error) => { callback?.onError?.(error as unknown as Error); eventSource.close(); reject(error); }; }); }, /** * 异步生成会话摘要 * @param conversationId 会话ID * @returns Promise> */ async generateSummary(conversationId: string): Promise> { const response = await api.post(`/ai/chat/conversation/${conversationId}/summary`); return response.data; }, /** * 评价消息 * @param messageId 消息ID * @param rating 评分(1=好评,-1=差评,0=取消评价) * @param feedback 反馈内容(可选) * @returns Promise> */ async rateMessage(messageId: string, rating: number, feedback?: string): Promise> { const response = await api.post(`/ai/chat/message/${messageId}/rate`, { rating, feedback }, { showLoading: false }); return response.data; } };