knowledgebase_influencer/src/store/chat/chat.thunks.js

492 lines
19 KiB
JavaScript
Raw Normal View History

2025-04-16 09:51:27 +08:00
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 || '创建会话失败');
}
}
);