mirror of
https://github.com/Funkoala14/knowledgebase_influencer.git
synced 2025-06-08 19:28:15 +08:00
492 lines
19 KiB
JavaScript
492 lines
19 KiB
JavaScript
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||
import { get, post, put, del, streamRequest } from '../../services/api';
|
||
import { showNotification } from '../notification.slice';
|
||
import { addMessage, updateMessage, setCurrentChat } from './chat.slice';
|
||
|
||
/**
|
||
* 获取聊天列表
|
||
* @param {Object} params - 查询参数
|
||
* @param {number} params.page - 页码
|
||
* @param {number} params.page_size - 每页数量
|
||
*/
|
||
export const fetchChats = createAsyncThunk('chat/fetchChats', async (params = {}, { rejectWithValue }) => {
|
||
try {
|
||
const response = await get('/chat-history/', { params });
|
||
|
||
// 处理返回格式
|
||
if (response && response.code === 200) {
|
||
return {
|
||
results: response.data.results,
|
||
total: response.data.total,
|
||
page: response.data.page || 1,
|
||
page_size: response.data.page_size || 10,
|
||
};
|
||
}
|
||
|
||
return { results: [], total: 0, page: 1, page_size: 10 };
|
||
} catch (error) {
|
||
console.error('Error fetching chats:', error);
|
||
return rejectWithValue(error.response?.data?.message || 'Failed to fetch chats');
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 创建新聊天
|
||
* @param {Object} chatData - 聊天数据
|
||
* @param {string} chatData.knowledge_base_id - 知识库ID
|
||
* @param {string} chatData.title - 聊天标题
|
||
*/
|
||
export const createChat = createAsyncThunk('chat/createChat', async (chatData, { rejectWithValue }) => {
|
||
try {
|
||
const response = await post('/chat-history/', chatData);
|
||
|
||
// 处理返回格式
|
||
if (response && response.code === 200) {
|
||
return response.data.chat;
|
||
}
|
||
|
||
return response.data?.chat || {};
|
||
} catch (error) {
|
||
return rejectWithValue(error.response?.data?.message || 'Failed to create chat');
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 更新聊天
|
||
* @param {Object} params - 更新参数
|
||
* @param {string} params.id - 聊天ID
|
||
* @param {Object} params.data - 更新数据
|
||
*/
|
||
export const updateChat = createAsyncThunk('chat/updateChat', async ({ id, data }, { rejectWithValue }) => {
|
||
try {
|
||
const response = await put(`/chat-history/${id}/`, data);
|
||
|
||
// 处理返回格式
|
||
if (response && response.code === 200) {
|
||
return response.data.chat;
|
||
}
|
||
|
||
return response.data?.chat || {};
|
||
} catch (error) {
|
||
return rejectWithValue(error.response?.data?.message || 'Failed to update chat');
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 删除聊天
|
||
* @param {string} conversationId - 聊天ID
|
||
*/
|
||
export const deleteChat = createAsyncThunk('chat/deleteChat', async (conversationId, { rejectWithValue }) => {
|
||
try {
|
||
const response = await del(`/chat-history/delete_conversation?conversation_id=${conversationId}`);
|
||
|
||
// 处理返回格式
|
||
if (response && response.code === 200) {
|
||
return conversationId;
|
||
}
|
||
|
||
return conversationId;
|
||
} catch (error) {
|
||
console.error('Error deleting chat:', error);
|
||
return rejectWithValue(error.response?.data?.message || '删除聊天失败');
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 获取可用于聊天的知识库列表
|
||
*/
|
||
export const fetchAvailableDatasets = createAsyncThunk(
|
||
'chat/fetchAvailableDatasets',
|
||
async (_, { rejectWithValue }) => {
|
||
try {
|
||
const response = await get('/chat-history/available_datasets/');
|
||
|
||
if (response && response.code === 200) {
|
||
return response.data;
|
||
}
|
||
|
||
return rejectWithValue('获取可用知识库列表失败');
|
||
} catch (error) {
|
||
console.error('Error fetching available datasets:', error);
|
||
return rejectWithValue(error.response?.data?.message || '获取可用知识库列表失败');
|
||
}
|
||
}
|
||
);
|
||
|
||
/**
|
||
* 创建聊天记录
|
||
* @param {Object} params - 聊天参数
|
||
* @param {string[]} params.dataset_id_list - 知识库ID列表
|
||
* @param {string} params.question - 用户问题
|
||
* @param {string} params.conversation_id - 会话ID,可选
|
||
*/
|
||
export const createChatRecord = createAsyncThunk(
|
||
'chat/createChatRecord',
|
||
async ({ question, conversation_id, dataset_id_list }, { dispatch, getState, rejectWithValue }) => {
|
||
try {
|
||
// 构建请求数据
|
||
const requestBody = {
|
||
question,
|
||
dataset_id_list,
|
||
conversation_id,
|
||
};
|
||
console.log('准备发送聊天请求:', requestBody);
|
||
|
||
// 先添加用户消息到聊天窗口
|
||
const userMessageId = Date.now().toString();
|
||
dispatch(
|
||
addMessage({
|
||
id: userMessageId,
|
||
role: 'user',
|
||
content: question,
|
||
created_at: new Date().toISOString(),
|
||
})
|
||
);
|
||
|
||
// 添加临时的助手消息(流式传输期间显示)
|
||
const assistantMessageId = (Date.now() + 1).toString();
|
||
dispatch(
|
||
addMessage({
|
||
id: assistantMessageId,
|
||
role: 'assistant',
|
||
content: '',
|
||
created_at: new Date().toISOString(),
|
||
is_streaming: true,
|
||
})
|
||
);
|
||
|
||
let finalMessage = '';
|
||
let conversationId = conversation_id;
|
||
|
||
// 使用流式请求函数处理
|
||
const result = await streamRequest(
|
||
'/chat-history/',
|
||
requestBody,
|
||
// 处理每个数据块
|
||
(chunkText) => {
|
||
try {
|
||
const data = JSON.parse(chunkText);
|
||
console.log('收到聊天数据块:', data);
|
||
|
||
if (data.code === 200) {
|
||
// 保存会话ID (无论消息类型,只要找到会话ID就保存)
|
||
if (data.data && data.data.conversation_id && !conversationId) {
|
||
conversationId = data.data.conversation_id;
|
||
console.log('获取到会话ID:', conversationId);
|
||
}
|
||
|
||
// 处理各种可能的消息类型
|
||
const messageType = data.message;
|
||
|
||
// 处理部分内容更新
|
||
if ((messageType === 'partial' || messageType === '部分') && data.data) {
|
||
// 累加内容
|
||
if (data.data.content !== undefined) {
|
||
finalMessage += data.data.content;
|
||
console.log('累加内容:', finalMessage);
|
||
|
||
// 更新消息内容
|
||
dispatch(
|
||
updateMessage({
|
||
id: assistantMessageId,
|
||
content: finalMessage,
|
||
})
|
||
);
|
||
}
|
||
|
||
// 处理结束标志
|
||
if (data.data.is_end) {
|
||
console.log('检测到消息结束标志');
|
||
dispatch(
|
||
updateMessage({
|
||
id: assistantMessageId,
|
||
is_streaming: false,
|
||
})
|
||
);
|
||
}
|
||
}
|
||
// 处理开始流式传输的消息
|
||
else if (messageType === '开始流式传输' || messageType === 'start_streaming') {
|
||
console.log('开始流式传输,会话ID:', data.data?.conversation_id);
|
||
}
|
||
// 处理完成消息
|
||
else if (
|
||
messageType === 'completed' ||
|
||
messageType === '完成' ||
|
||
messageType === 'end_streaming' ||
|
||
messageType === '结束流式传输'
|
||
) {
|
||
console.log('收到完成消息');
|
||
dispatch(
|
||
updateMessage({
|
||
id: assistantMessageId,
|
||
is_streaming: false,
|
||
})
|
||
);
|
||
}
|
||
// 其他类型的消息
|
||
else {
|
||
console.log('收到其他类型消息:', messageType);
|
||
// 如果有content字段,也尝试更新
|
||
if (data.data && data.data.content !== undefined) {
|
||
finalMessage += data.data.content;
|
||
dispatch(
|
||
updateMessage({
|
||
id: assistantMessageId,
|
||
content: finalMessage,
|
||
})
|
||
);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn('收到非成功状态码:', data.code, data.message);
|
||
}
|
||
} catch (error) {
|
||
console.error('解析或处理JSON失败:', error, '原始数据:', chunkText);
|
||
}
|
||
},
|
||
// 处理错误
|
||
(error) => {
|
||
console.error('流式请求错误:', error);
|
||
dispatch(
|
||
updateMessage({
|
||
id: assistantMessageId,
|
||
content: `错误: ${error.message || '请求失败'}`,
|
||
is_streaming: false,
|
||
})
|
||
);
|
||
}
|
||
);
|
||
|
||
// 确保流式传输结束后标记消息已完成
|
||
dispatch(
|
||
updateMessage({
|
||
id: assistantMessageId,
|
||
is_streaming: false,
|
||
})
|
||
);
|
||
|
||
// 返回会话信息
|
||
const chatInfo = {
|
||
conversation_id: conversationId || result.conversation_id,
|
||
success: true,
|
||
};
|
||
|
||
// 如果聊天创建成功,添加到历史列表,并设置为当前激活聊天
|
||
if (chatInfo.conversation_id) {
|
||
// 获取知识库信息
|
||
const state = getState();
|
||
const availableDatasets = state.chat.availableDatasets.items || [];
|
||
const existingChats = state.chat.history.items || [];
|
||
|
||
// 检查是否已存在此会话ID的记录
|
||
const existingChat = existingChats.find((chat) => chat.conversation_id === chatInfo.conversation_id);
|
||
|
||
// 只有在不存在相同会话ID的记录时才创建新的记录
|
||
if (!existingChat) {
|
||
console.log('创建新的聊天sidebar记录:', chatInfo.conversation_id);
|
||
|
||
// 创建一个新的聊天记录对象添加到历史列表
|
||
const newChatEntry = {
|
||
conversation_id: chatInfo.conversation_id,
|
||
datasets: dataset_id_list.map((id) => {
|
||
// 尝试查找知识库名称
|
||
const formattedId = id.includes('-')
|
||
? id
|
||
: id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5');
|
||
const dataset = availableDatasets.find((ds) => ds.id === formattedId);
|
||
return {
|
||
id: formattedId,
|
||
name: dataset?.name || '新知识库对话',
|
||
};
|
||
}),
|
||
create_time: new Date().toISOString(),
|
||
last_message: question,
|
||
message_count: 2, // 用户问题和助手回复
|
||
};
|
||
|
||
// 更新当前聊天
|
||
dispatch({
|
||
type: 'chat/fetchChats/fulfilled',
|
||
payload: {
|
||
results: [newChatEntry],
|
||
total: 1,
|
||
append: true, // 标记为追加,而不是替换
|
||
},
|
||
});
|
||
} else {
|
||
console.log('聊天sidebar记录已存在,不再创建:', chatInfo.conversation_id);
|
||
}
|
||
|
||
// 设置为当前聊天
|
||
dispatch(
|
||
setCurrentChat({
|
||
conversation_id: chatInfo.conversation_id,
|
||
datasets: existingChat
|
||
? existingChat.datasets
|
||
: dataset_id_list.map((id) => {
|
||
const formattedId = id.includes('-')
|
||
? id
|
||
: id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5');
|
||
const dataset = availableDatasets.find((ds) => ds.id === formattedId);
|
||
return {
|
||
id: formattedId,
|
||
name: dataset?.name || '新知识库对话',
|
||
};
|
||
}),
|
||
})
|
||
);
|
||
}
|
||
|
||
return chatInfo;
|
||
} catch (error) {
|
||
console.error('创建聊天记录失败:', error);
|
||
|
||
// 显示错误通知
|
||
dispatch(
|
||
showNotification({
|
||
message: `发送失败: ${error.message || '未知错误'}`,
|
||
type: 'danger',
|
||
})
|
||
);
|
||
|
||
return rejectWithValue(error.message || '创建聊天记录失败');
|
||
}
|
||
}
|
||
);
|
||
|
||
/**
|
||
* 获取会话详情
|
||
* @param {string} conversationId - 会话ID
|
||
*/
|
||
export const fetchConversationDetail = createAsyncThunk(
|
||
'chat/fetchConversationDetail',
|
||
async (conversationId, { rejectWithValue, dispatch, getState }) => {
|
||
try {
|
||
// 先检查是否是刚创建的会话
|
||
const state = getState();
|
||
const createSession = state.chat.createSession || {};
|
||
const currentChat = state.chat.currentChat.data;
|
||
|
||
// 如果是刚创建成功的会话,且会话ID匹配,则直接返回现有会话数据
|
||
if (
|
||
createSession.status === 'succeeded' &&
|
||
createSession.sessionId === conversationId &&
|
||
currentChat?.conversation_id === conversationId
|
||
) {
|
||
console.log('使用新创建的会话数据,跳过详情请求:', conversationId);
|
||
return currentChat;
|
||
}
|
||
|
||
const response = await get('/chat-history/conversation_detail', {
|
||
params: { conversation_id: conversationId },
|
||
});
|
||
|
||
if (response && response.code === 200) {
|
||
// 如果存在消息,更新Redux状态
|
||
if (response.data.messages) {
|
||
dispatch({
|
||
type: 'chat/fetchMessages/fulfilled',
|
||
payload: response.data.messages,
|
||
});
|
||
}
|
||
|
||
return response.data;
|
||
}
|
||
|
||
return rejectWithValue('获取会话详情失败');
|
||
} catch (error) {
|
||
// 明确检查是否是404错误
|
||
const is404Error = error.response && error.response.status === 404;
|
||
|
||
if (is404Error) {
|
||
console.log('会话未找到,可能是新创建的会话:', conversationId);
|
||
return null;
|
||
}
|
||
|
||
console.error('Error fetching conversation detail:', error);
|
||
return rejectWithValue(error.response?.data?.message || '获取会话详情失败');
|
||
}
|
||
}
|
||
);
|
||
|
||
/**
|
||
* 创建新会话(仅获取会话ID,不发送消息)
|
||
* @param {Object} params - 参数
|
||
* @param {string[]} params.dataset_id_list - 知识库ID列表
|
||
*/
|
||
export const createConversation = createAsyncThunk(
|
||
'chat/createConversation',
|
||
async ({ dataset_id_list }, { dispatch, getState, rejectWithValue }) => {
|
||
try {
|
||
console.log('创建新会话,知识库ID列表:', dataset_id_list);
|
||
const params = {
|
||
dataset_id_list: dataset_id_list,
|
||
};
|
||
const response = await post('/chat-history/create_conversation/', params);
|
||
|
||
if (response && response.code === 200) {
|
||
const conversationData = response.data;
|
||
console.log('会话创建成功:', conversationData);
|
||
|
||
// 获取知识库信息
|
||
const state = getState();
|
||
const availableDatasets = state.chat.availableDatasets.items || [];
|
||
|
||
// 创建一个新的聊天记录对象添加到历史列表
|
||
const newChatEntry = {
|
||
conversation_id: conversationData.conversation_id,
|
||
datasets: dataset_id_list.map((id) => {
|
||
// 尝试查找知识库名称
|
||
const formattedId = id.includes('-')
|
||
? id
|
||
: id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5');
|
||
const dataset = availableDatasets.find((ds) => ds.id === formattedId);
|
||
return {
|
||
id: formattedId,
|
||
name: dataset?.name || '新知识库对话',
|
||
};
|
||
}),
|
||
create_time: new Date().toISOString(),
|
||
last_message: '',
|
||
message_count: 0,
|
||
};
|
||
|
||
// 更新聊天历史列表
|
||
dispatch({
|
||
type: 'chat/fetchChats/fulfilled',
|
||
payload: {
|
||
results: [newChatEntry],
|
||
total: 1,
|
||
append: true, // 标记为追加,而不是替换
|
||
},
|
||
});
|
||
|
||
// 设置为当前聊天
|
||
dispatch(
|
||
setCurrentChat({
|
||
conversation_id: conversationData.conversation_id,
|
||
datasets: newChatEntry.datasets,
|
||
})
|
||
);
|
||
|
||
return conversationData;
|
||
}
|
||
|
||
return rejectWithValue('创建会话失败');
|
||
} catch (error) {
|
||
console.error('创建会话失败:', error);
|
||
|
||
// 显示错误通知
|
||
dispatch(
|
||
showNotification({
|
||
message: `创建会话失败: ${error.message || '未知错误'}`,
|
||
type: 'danger',
|
||
})
|
||
);
|
||
|
||
return rejectWithValue(error.message || '创建会话失败');
|
||
}
|
||
}
|
||
);
|