对话、重新生成、评价完成

This commit is contained in:
2025-11-05 16:55:58 +08:00
parent 8850a06fea
commit d9d62e22de
34 changed files with 1658 additions and 965 deletions

View File

@@ -9,7 +9,6 @@ import type {
AiConversation,
AiMessage,
ChatRequest,
ChatResponse,
ResultDomain,
StreamCallback
} from '@/types';
@@ -19,43 +18,102 @@ import type {
*/
export const chatApi = {
/**
* 流式对话SSE
* 流式对话SSE- 使用fetch支持Authorization
* @param request 对话请求
* @param callback 流式回调
* @returns Promise<ResultDomain<AiMessage>>
*/
async streamChat(request: ChatRequest, callback?: StreamCallback): Promise<ResultDomain<AiMessage>> {
const token = localStorage.getItem('token');
const tokenData = token ? JSON.parse(token) : null;
return new Promise((resolve, reject) => {
// 使用相对路径走Vite代理避免跨域
const eventSource = new EventSource(
`${api.defaults.baseURL}/ai/chat/stream?` +
`/api/ai/chat/stream?` +
new URLSearchParams({
agentId: request.agentId,
conversationId: request.conversationId || '',
query: request.query,
knowledgeIds: request.knowledgeIds?.join(',') || ''
knowledgeIds: request.knowledgeIds?.join(',') || '',
token: tokenData?.value || ''
})
);
let fullMessage = '';
// 通知外部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) => {
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);
console.log(`[Dify事件] ${eventType}:`, eventData);
// 调用自定义的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) => {
const error = new Error(event.data || '对话失败');
callback?.onError?.(error);
@@ -64,7 +122,7 @@ export const chatApi = {
});
eventSource.onerror = (error) => {
callback?.onError?.(error as Error);
callback?.onError?.(error as unknown as Error);
eventSource.close();
reject(error);
};
@@ -82,7 +140,7 @@ export const chatApi = {
},
/**
* 停止对话生成
* 停止对话生成通过消息ID
* @param messageId 消息ID
* @returns Promise<ResultDomain<boolean>>
*/
@@ -91,6 +149,20 @@ export const chatApi = {
return response.data;
},
/**
* 停止对话生成通过Dify TaskID
* @param taskId Dify任务ID
* @param agentId 智能体ID
* @returns Promise<ResultDomain<boolean>>
*/
async stopChatByTaskId(taskId: string, agentId: string): Promise<ResultDomain<boolean>> {
const response = await api.post<boolean>('/ai/chat/stop-by-taskid', {
taskId,
agentId
});
return response.data;
},
/**
* 创建新会话
* @param agentId 智能体ID
@@ -152,8 +224,8 @@ export const chatApi = {
* @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`);
async listMessages(conversationId: string): Promise<ResultDomain<AiMessage>> {
const response = await api.get<AiMessage>(`/ai/chat/conversation/${conversationId}/messages`);
return response.data;
},
@@ -168,46 +240,112 @@ export const chatApi = {
},
/**
* 重新生成回答
* 重新生成回答SSE流式
* @param messageId 原消息ID
* @param callback 流式回调(可选)
* @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`
);
const token = localStorage.getItem('token');
const tokenData = token ? JSON.parse(token) : null;
return new Promise((resolve, reject) => {
// 使用相对路径走Vite代理SSE流式推送
const eventSource = new EventSource(
`/api/ai/chat/regenerate/${messageId}?` +
new URLSearchParams({
token: tokenData?.value || ''
})
);
eventSource.addEventListener('message', (event) => {
callback.onMessage?.(event.data);
});
// 通知外部EventSource已创建
callback?.onStart?.(eventSource);
eventSource.addEventListener('end', (event) => {
const metadata = JSON.parse(event.data);
callback.onMessageEnd?.(metadata);
eventSource.close();
resolve({
success: true,
data: metadata as AiMessage,
message: '重新生成成功'
});
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let fullMessage = ''; // 累积完整消息内容
eventSource.addEventListener('error', (event: any) => {
const error = new Error(event.data || '重新生成失败');
callback.onError?.(error);
eventSource.close();
reject(error);
// 监听初始化事件包含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) => {
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: '重新生成成功'
});
});
} else {
// 使用阻塞方式重新生成
const response = await api.post<AiMessage>(`/ai/chat/regenerate/${messageId}`);
return response.data;
}
// 监听所有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);
console.log(`[Dify事件] ${eventType}:`, eventData);
// 调用自定义的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) => {
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);
};
});
},
/**