239 lines
6.9 KiB
TypeScript
239 lines
6.9 KiB
TypeScript
|
|
/**
|
|||
|
|
* @description AI对话相关API
|
|||
|
|
* @author AI Assistant
|
|||
|
|
* @since 2025-11-04
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { api } from '@/apis/index';
|
|||
|
|
import type {
|
|||
|
|
AiConversation,
|
|||
|
|
AiMessage,
|
|||
|
|
ChatRequest,
|
|||
|
|
ChatResponse,
|
|||
|
|
ResultDomain,
|
|||
|
|
StreamCallback
|
|||
|
|
} from '@/types';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* AI对话API服务
|
|||
|
|
*/
|
|||
|
|
export const chatApi = {
|
|||
|
|
/**
|
|||
|
|
* 流式对话(SSE)
|
|||
|
|
* @param request 对话请求
|
|||
|
|
* @param callback 流式回调
|
|||
|
|
* @returns Promise<ResultDomain<AiMessage>>
|
|||
|
|
*/
|
|||
|
|
async streamChat(request: ChatRequest, callback?: StreamCallback): Promise<ResultDomain<AiMessage>> {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const eventSource = new EventSource(
|
|||
|
|
`${api.defaults.baseURL}/ai/chat/stream?` +
|
|||
|
|
new URLSearchParams({
|
|||
|
|
agentId: request.agentId,
|
|||
|
|
conversationId: request.conversationId || '',
|
|||
|
|
query: request.query,
|
|||
|
|
knowledgeIds: request.knowledgeIds?.join(',') || ''
|
|||
|
|
})
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
let fullMessage = '';
|
|||
|
|
|
|||
|
|
eventSource.addEventListener('message', (event) => {
|
|||
|
|
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({
|
|||
|
|
success: true,
|
|||
|
|
data: metadata as AiMessage,
|
|||
|
|
message: '对话成功'
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
eventSource.addEventListener('error', (event: any) => {
|
|||
|
|
const error = new Error(event.data || '对话失败');
|
|||
|
|
callback?.onError?.(error);
|
|||
|
|
eventSource.close();
|
|||
|
|
reject(error);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
eventSource.onerror = (error) => {
|
|||
|
|
callback?.onError?.(error as Error);
|
|||
|
|
eventSource.close();
|
|||
|
|
reject(error);
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 阻塞式对话(非流式)
|
|||
|
|
* @param request 对话请求
|
|||
|
|
* @returns Promise<ResultDomain<AiMessage>>
|
|||
|
|
*/
|
|||
|
|
async blockingChat(request: ChatRequest): Promise<ResultDomain<AiMessage>> {
|
|||
|
|
const response = await api.post<AiMessage>('/ai/chat/blocking', request);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 停止对话生成
|
|||
|
|
* @param messageId 消息ID
|
|||
|
|
* @returns Promise<ResultDomain<boolean>>
|
|||
|
|
*/
|
|||
|
|
async stopChat(messageId: string): Promise<ResultDomain<boolean>> {
|
|||
|
|
const response = await api.post<boolean>(`/ai/chat/stop/${messageId}`);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建新会话
|
|||
|
|
* @param agentId 智能体ID
|
|||
|
|
* @param title 会话标题(可选)
|
|||
|
|
* @returns Promise<ResultDomain<AiConversation>>
|
|||
|
|
*/
|
|||
|
|
async createConversation(agentId: string, title?: string): Promise<ResultDomain<AiConversation>> {
|
|||
|
|
const response = await api.post<AiConversation>('/ai/chat/conversation', {
|
|||
|
|
agentId,
|
|||
|
|
title
|
|||
|
|
});
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取会话信息
|
|||
|
|
* @param conversationId 会话ID
|
|||
|
|
* @returns Promise<ResultDomain<AiConversation>>
|
|||
|
|
*/
|
|||
|
|
async getConversation(conversationId: string): Promise<ResultDomain<AiConversation>> {
|
|||
|
|
const response = await api.get<AiConversation>(`/ai/chat/conversation/${conversationId}`);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新会话
|
|||
|
|
* @param conversation 会话信息
|
|||
|
|
* @returns Promise<ResultDomain<AiConversation>>
|
|||
|
|
*/
|
|||
|
|
async updateConversation(conversation: AiConversation): Promise<ResultDomain<AiConversation>> {
|
|||
|
|
const response = await api.put<AiConversation>('/ai/chat/conversation', conversation);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 删除会话
|
|||
|
|
* @param conversationId 会话ID
|
|||
|
|
* @returns Promise<ResultDomain<boolean>>
|
|||
|
|
*/
|
|||
|
|
async deleteConversation(conversationId: string): Promise<ResultDomain<boolean>> {
|
|||
|
|
const response = await api.delete<boolean>(`/ai/chat/conversation/${conversationId}`);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取用户的会话列表
|
|||
|
|
* @param agentId 智能体ID(可选)
|
|||
|
|
* @returns Promise<ResultDomain<AiConversation[]>>
|
|||
|
|
*/
|
|||
|
|
async listUserConversations(agentId?: string): Promise<ResultDomain<AiConversation[]>> {
|
|||
|
|
const response = await api.get<AiConversation[]>('/ai/chat/conversations', {
|
|||
|
|
params: { agentId }
|
|||
|
|
});
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取会话的消息列表
|
|||
|
|
* @param conversationId 会话ID
|
|||
|
|
* @returns Promise<ResultDomain<AiMessage[]>>
|
|||
|
|
*/
|
|||
|
|
async listMessages(conversationId: string): Promise<ResultDomain<AiMessage[]>> {
|
|||
|
|
const response = await api.get<AiMessage[]>(`/ai/chat/conversation/${conversationId}/messages`);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取单条消息
|
|||
|
|
* @param messageId 消息ID
|
|||
|
|
* @returns Promise<ResultDomain<AiMessage>>
|
|||
|
|
*/
|
|||
|
|
async getMessage(messageId: string): Promise<ResultDomain<AiMessage>> {
|
|||
|
|
const response = await api.get<AiMessage>(`/ai/chat/message/${messageId}`);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重新生成回答
|
|||
|
|
* @param messageId 原消息ID
|
|||
|
|
* @param callback 流式回调(可选)
|
|||
|
|
* @returns Promise<ResultDomain<AiMessage>>
|
|||
|
|
*/
|
|||
|
|
async regenerateAnswer(messageId: string, callback?: StreamCallback): Promise<ResultDomain<AiMessage>> {
|
|||
|
|
if (callback) {
|
|||
|
|
// 使用流式方式重新生成
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const eventSource = new EventSource(
|
|||
|
|
`${api.defaults.baseURL}/ai/chat/regenerate/${messageId}?stream=true`
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
eventSource.addEventListener('message', (event) => {
|
|||
|
|
callback.onMessage?.(event.data);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
eventSource.addEventListener('end', (event) => {
|
|||
|
|
const metadata = JSON.parse(event.data);
|
|||
|
|
callback.onMessageEnd?.(metadata);
|
|||
|
|
eventSource.close();
|
|||
|
|
resolve({
|
|||
|
|
success: true,
|
|||
|
|
data: metadata as AiMessage,
|
|||
|
|
message: '重新生成成功'
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
eventSource.addEventListener('error', (event: any) => {
|
|||
|
|
const error = new Error(event.data || '重新生成失败');
|
|||
|
|
callback.onError?.(error);
|
|||
|
|
eventSource.close();
|
|||
|
|
reject(error);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 使用阻塞方式重新生成
|
|||
|
|
const response = await api.post<AiMessage>(`/ai/chat/regenerate/${messageId}`);
|
|||
|
|
return response.data;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 异步生成会话摘要
|
|||
|
|
* @param conversationId 会话ID
|
|||
|
|
* @returns Promise<ResultDomain<boolean>>
|
|||
|
|
*/
|
|||
|
|
async generateSummary(conversationId: string): Promise<ResultDomain<boolean>> {
|
|||
|
|
const response = await api.post<boolean>(`/ai/chat/conversation/${conversationId}/summary`);
|
|||
|
|
return response.data;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 评价消息
|
|||
|
|
* @param messageId 消息ID
|
|||
|
|
* @param rating 评分(1=好评,-1=差评,0=取消评价)
|
|||
|
|
* @param feedback 反馈内容(可选)
|
|||
|
|
* @returns Promise<ResultDomain<boolean>>
|
|||
|
|
*/
|
|||
|
|
async rateMessage(messageId: string, rating: number, feedback?: string): Promise<ResultDomain<boolean>> {
|
|||
|
|
const response = await api.post<boolean>(`/ai/chat/message/${messageId}/rate`, {
|
|||
|
|
rating,
|
|||
|
|
feedback
|
|||
|
|
});
|
|||
|
|
return response.data;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|