feat: Fetch conversation list by @tanstack/react-query and displays error message that task_executor does not exist #2088 (#2112)

### What problem does this PR solve?

feat: Fetch conversation list by @tanstack/react-query
feat: Displays error message that task_executor does not exist

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-08-27 14:45:17 +08:00 committed by GitHub
parent 1d88b197fb
commit 61d2a74b25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 350 additions and 460 deletions

View File

@ -1,3 +1,4 @@
.thumbnailImg { .thumbnailImg {
display: inline-block;
max-width: 20px; max-width: 20px;
} }

View File

@ -15,3 +15,10 @@ export enum SharedFrom {
Agent = 'agent', Agent = 'agent',
Chat = 'chat', Chat = 'chat',
} }
export enum ChatSearchParams {
DialogId = 'dialogId',
ConversationId = 'conversationId',
}
export const EmptyConversationId = 'empty';

View File

@ -1,131 +1,255 @@
import { ChatSearchParams } from '@/constants/chat';
import { import {
IConversation, IConversation,
IDialog, IDialog,
IStats, IStats,
IToken, IToken,
Message,
} from '@/interfaces/database/chat'; } from '@/interfaces/database/chat';
import i18n from '@/locales/config';
import { IClientConversation, IMessage } from '@/pages/chat/interface';
import chatService from '@/services/chat-service'; import chatService from '@/services/chat-service';
import { isConversationIdExist } from '@/utils/chat';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { message } from 'antd';
import dayjs, { Dayjs } from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import { useCallback, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'umi'; import { useSearchParams } from 'umi';
import { v4 as uuid } from 'uuid';
export const useFetchDialogList = () => { //#region logic
const dispatch = useDispatch();
const fetchDialogList = useCallback(() => { export const useClickDialogCard = () => {
return dispatch<any>({ type: 'chatModel/listDialog' }); const [, setSearchParams] = useSearchParams();
}, [dispatch]);
return fetchDialogList; const newQueryParameters: URLSearchParams = useMemo(() => {
}; return new URLSearchParams();
}, []);
export const useSelectDialogList = () => { const handleClickDialog = useCallback(
const dialogList: IDialog[] = useSelector( (dialogId: string) => {
(state: any) => state.chatModel.dialogList, newQueryParameters.set(ChatSearchParams.DialogId, dialogId);
// newQueryParameters.set(
// ChatSearchParams.ConversationId,
// EmptyConversationId,
// );
setSearchParams(newQueryParameters);
},
[newQueryParameters, setSearchParams],
); );
return dialogList; return { handleClickDialog };
}; };
export const useFetchConversationList = () => { export const useGetChatSearchParams = () => {
const dispatch = useDispatch(); const [currentQueryParameters] = useSearchParams();
const fetchConversationList = useCallback( return {
async (dialogId: string) => { dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
if (dialogId) { conversationId:
dispatch({ currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
type: 'chatModel/listConversation', };
payload: { dialog_id: dialogId }, };
});
//#endregion
//#region dialog
export const useFetchNextDialogList = () => {
const { handleClickDialog } = useClickDialogCard();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IDialog[]>({
queryKey: ['fetchDialogList'],
initialData: [],
gcTime: 0,
refetchOnWindowFocus: false,
queryFn: async () => {
const { data } = await chatService.listDialog();
if (data.retcode === 0 && data.data.length > 0) {
handleClickDialog(data.data[0].id);
} }
},
[dispatch],
);
return fetchConversationList; return data?.data ?? [];
};
export const useSelectConversationList = () => {
const conversationList: IConversation[] = useSelector(
(state: any) => state.chatModel.conversationList,
);
return conversationList;
};
export const useFetchConversation = () => {
const dispatch = useDispatch();
const fetchConversation = useCallback(
(conversationId: string, needToBeSaved = true) => {
return dispatch<any>({
type: 'chatModel/getConversation',
payload: {
needToBeSaved,
conversation_id: conversationId,
}, },
}); });
},
[dispatch],
);
return fetchConversation; return { data, loading, refetch };
}; };
export const useFetchDialog = () => { export const useSetNextDialog = () => {
const dispatch = useDispatch(); const queryClient = useQueryClient();
const {
const fetchDialog = useCallback( data,
(dialogId: string, needToBeSaved = true) => { isPending: loading,
if (dialogId) { mutateAsync,
return dispatch<any>({ } = useMutation({
type: 'chatModel/getDialog', mutationKey: ['setDialog'],
payload: { dialog_id: dialogId, needToBeSaved }, mutationFn: async (params: IDialog) => {
}); const { data } = await chatService.setDialog(params);
if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchDialogList'] });
message.success(
i18n.t(`message.${params.id ? 'modified' : 'created'}`),
);
} }
}, return data?.retcode;
[dispatch],
);
return fetchDialog;
};
export const useRemoveDialog = () => {
const dispatch = useDispatch();
const removeDocument = useCallback(
(dialogIds: Array<string>) => {
return dispatch({
type: 'chatModel/removeDialog',
payload: {
dialog_ids: dialogIds,
}, },
}); });
},
[dispatch],
);
return removeDocument; return { data, loading, setDialog: mutateAsync };
}; };
export const useUpdateConversation = () => { export const useFetchNextDialog = () => {
const dispatch = useDispatch(); const { dialogId } = useGetChatSearchParams();
const updateConversation = useCallback( const { data, isFetching: loading } = useQuery<IDialog>({
(payload: any) => { queryKey: ['fetchDialog', dialogId],
return dispatch<any>({ gcTime: 0,
type: 'chatModel/setConversation', initialData: {} as IDialog,
payload, enabled: !!dialogId,
}); refetchOnWindowFocus: false,
queryFn: async () => {
const { data } = await chatService.getDialog({ dialogId });
return data?.data ?? ({} as IDialog);
}, },
[dispatch], });
);
return updateConversation; return { data, loading };
};
export const useFetchManualDialog = () => {
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['fetchManualDialog'],
gcTime: 0,
mutationFn: async (dialogId: string) => {
const { data } = await chatService.getDialog({ dialogId });
return data;
},
});
return { data, loading, fetchDialog: mutateAsync };
};
export const useRemoveNextDialog = () => {
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['removeDialog'],
mutationFn: async (dialogIds: string[]) => {
const { data } = await chatService.removeDialog({ dialogIds });
if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchDialogList'] });
message.success(i18n.t('message.deleted'));
}
return data.retcode;
},
});
return { data, loading, removeDialog: mutateAsync };
};
//#endregion
//#region conversation
export const useFetchNextConversationList = () => {
const { dialogId } = useGetChatSearchParams();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IConversation[]>({
queryKey: ['fetchConversationList', dialogId],
initialData: [],
gcTime: 0,
refetchOnWindowFocus: false,
enabled: !!dialogId,
queryFn: async () => {
const { data } = await chatService.listConversation({ dialogId });
return data?.data;
},
});
return { data, loading, refetch };
};
export const useFetchNextConversation = () => {
const { conversationId } = useGetChatSearchParams();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IClientConversation>({
queryKey: ['fetchConversation', conversationId],
initialData: {} as IClientConversation,
// enabled: isConversationIdExist(conversationId),
gcTime: 0,
refetchOnWindowFocus: false,
queryFn: async () => {
if (isConversationIdExist(conversationId)) {
const { data } = await chatService.getConversation({ conversationId });
// if (data.retcode === 0 && needToBeSaved) {
// yield put({
// type: 'kFModel/fetch_document_thumbnails',
// payload: {
// doc_ids: getDocumentIdsFromConversionReference(data.data),
// },
// });
// yield put({ type: 'setCurrentConversation', payload: data.data });
// }
const conversation = data?.data ?? {};
const messageList =
conversation?.message?.map((x: Message | IMessage) => ({
...x,
id: 'id' in x && x.id ? x.id : uuid(),
})) ?? [];
return { ...conversation, message: messageList };
}
return { message: [] };
},
});
return { data, loading, refetch };
};
export const useFetchManualConversation = () => {
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['fetchManualConversation'],
gcTime: 0,
mutationFn: async (conversationId: string) => {
const { data } = await chatService.getConversation({ conversationId });
return data;
},
});
return { data, loading, fetchConversation: mutateAsync };
}; };
export const useUpdateNextConversation = () => { export const useUpdateNextConversation = () => {
const queryClient = useQueryClient();
const { const {
data, data,
isPending: loading, isPending: loading,
@ -134,7 +258,9 @@ export const useUpdateNextConversation = () => {
mutationKey: ['updateConversation'], mutationKey: ['updateConversation'],
mutationFn: async (params: Record<string, any>) => { mutationFn: async (params: Record<string, any>) => {
const { data } = await chatService.setConversation(params); const { data } = await chatService.setConversation(params);
if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
}
return data; return data;
}, },
}); });
@ -142,72 +268,34 @@ export const useUpdateNextConversation = () => {
return { data, loading, updateConversation: mutateAsync }; return { data, loading, updateConversation: mutateAsync };
}; };
export const useSetDialog = () => { export const useRemoveNextConversation = () => {
const dispatch = useDispatch(); const queryClient = useQueryClient();
const { dialogId } = useGetChatSearchParams();
const setDialog = useCallback( const {
(payload: IDialog) => { data,
return dispatch<any>({ type: 'chatModel/setDialog', payload }); isPending: loading,
}, mutateAsync,
[dispatch], } = useMutation({
); mutationKey: ['removeConversation'],
mutationFn: async (conversationIds: string[]) => {
return setDialog; const { data } = await chatService.removeConversation({
}; conversationIds,
dialogId,
export const useRemoveConversation = () => { });
const dispatch = useDispatch(); if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
const removeConversation = useCallback( }
(conversationIds: Array<string>, dialogId: string) => { return data.retcode;
return dispatch<any>({
type: 'chatModel/removeConversation',
payload: {
dialog_id: dialogId,
conversation_ids: conversationIds,
}, },
}); });
},
[dispatch],
);
return removeConversation; return { data, loading, removeConversation: mutateAsync };
};
/*
@deprecated
*/
export const useCompleteConversation = () => {
const dispatch = useDispatch();
const completeConversation = useCallback(
(payload: any) => {
return dispatch<any>({
type: 'chatModel/completeConversation',
payload,
});
},
[dispatch],
);
return completeConversation;
}; };
//#endregion
// #region API provided for external calls // #region API provided for external calls
export const useCreateToken = (params: Record<string, any>) => {
const dispatch = useDispatch();
const createToken = useCallback(() => {
return dispatch<any>({
type: 'chatModel/createToken',
payload: params,
});
}, [dispatch, params]);
return createToken;
};
export const useCreateNextToken = () => { export const useCreateNextToken = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { const {
@ -303,36 +391,41 @@ export const useFetchNextStats = () => {
//#region shared chat //#region shared chat
export const useCreateSharedConversation = () => { export const useCreateNextSharedConversation = () => {
const dispatch = useDispatch(); const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['createSharedConversation'],
mutationFn: async (userId?: string) => {
const { data } = await chatService.createExternalConversation({ userId });
const createSharedConversation = useCallback( return data;
(userId?: string) => {
return dispatch<any>({
type: 'chatModel/createExternalConversation',
payload: { userId },
});
}, },
[dispatch], });
);
return createSharedConversation; return { data, loading, createSharedConversation: mutateAsync };
}; };
export const useFetchSharedConversation = () => { export const useFetchNextSharedConversation = () => {
const dispatch = useDispatch(); const {
data,
const fetchSharedConversation = useCallback( isPending: loading,
(conversationId: string) => { mutateAsync,
return dispatch<any>({ } = useMutation({
type: 'chatModel/getExternalConversation', mutationKey: ['fetchSharedConversation'],
payload: conversationId, mutationFn: async (conversationId: string) => {
}); const { data } = await chatService.getExternalConversation(
}, null,
[dispatch], conversationId,
); );
return fetchSharedConversation; return data;
},
});
return { data, loading, fetchConversation: mutateAsync };
}; };
//#endregion //#endregion

View File

@ -28,8 +28,9 @@ export interface ISystemStatus {
mysql: Minio; mysql: Minio;
redis: Redis; redis: Redis;
task_executor: { task_executor: {
error?: string;
status: string; status: string;
elapsed: TaskExecutorElapsed; elapsed?: TaskExecutorElapsed;
}; };
} }

View File

@ -8,7 +8,6 @@ import {
useFetchConversationOnMount, useFetchConversationOnMount,
useGetFileIcon, useGetFileIcon,
useGetSendButtonDisabled, useGetSendButtonDisabled,
useSelectConversationLoading,
useSendButtonDisabled, useSendButtonDisabled,
useSendMessage, useSendMessage,
} from '../hooks'; } from '../hooks';
@ -16,6 +15,7 @@ import { buildMessageItemReference } from '../utils';
import MessageInput from '@/components/message-input'; import MessageInput from '@/components/message-input';
import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { memo } from 'react';
import styles from './index.less'; import styles from './index.less';
const ChatContainer = () => { const ChatContainer = () => {
@ -26,6 +26,7 @@ const ChatContainer = () => {
removeLatestMessage, removeLatestMessage,
addNewestAnswer, addNewestAnswer,
conversationId, conversationId,
loading,
} = useFetchConversationOnMount(); } = useFetchConversationOnMount();
const { const {
handleInputChange, handleInputChange,
@ -43,7 +44,6 @@ const ChatContainer = () => {
const disabled = useGetSendButtonDisabled(); const disabled = useGetSendButtonDisabled();
const sendDisabled = useSendButtonDisabled(value); const sendDisabled = useSendButtonDisabled(value);
useGetFileIcon(); useGetFileIcon();
const loading = useSelectConversationLoading();
const { data: userInfo } = useFetchUserInfo(); const { data: userInfo } = useFetchUserInfo();
const { createConversationBeforeUploadDocument } = const { createConversationBeforeUploadDocument } =
useCreateConversationBeforeUploadDocument(); useCreateConversationBeforeUploadDocument();
@ -104,4 +104,4 @@ const ChatContainer = () => {
); );
}; };
export default ChatContainer; export default memo(ChatContainer);

View File

@ -1,16 +1,16 @@
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { fileIconMap } from '@/constants/common'; import { fileIconMap } from '@/constants/common';
import { import {
useFetchConversation, useFetchManualConversation,
useFetchConversationList, useFetchManualDialog,
useFetchDialog, useFetchNextConversation,
useFetchDialogList, useFetchNextConversationList,
useRemoveConversation, useFetchNextDialog,
useRemoveDialog, useGetChatSearchParams,
useSelectConversationList, useRemoveNextConversation,
useSelectDialogList, useRemoveNextDialog,
useSetDialog, useSetNextDialog,
useUpdateConversation, useUpdateNextConversation,
} from '@/hooks/chat-hooks'; } from '@/hooks/chat-hooks';
import { import {
useSetModalState, useSetModalState,
@ -18,7 +18,6 @@ import {
useTranslate, useTranslate,
} from '@/hooks/common-hooks'; } from '@/hooks/common-hooks';
import { useSendMessageWithSse } from '@/hooks/logic-hooks'; import { useSendMessageWithSse } from '@/hooks/logic-hooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
import { import {
IAnswer, IAnswer,
IConversation, IConversation,
@ -27,6 +26,8 @@ import {
} from '@/interfaces/database/chat'; } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { getFileExtension } from '@/utils'; import { getFileExtension } from '@/utils';
import { useMutationState } from '@tanstack/react-query';
import { get } from 'lodash';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import trim from 'lodash/trim'; import trim from 'lodash/trim';
import { import {
@ -37,7 +38,7 @@ import {
useRef, useRef,
useState, useState,
} from 'react'; } from 'react';
import { useDispatch, useSearchParams, useSelector } from 'umi'; import { useSearchParams } from 'umi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { ChatSearchParams } from './constants'; import { ChatSearchParams } from './constants';
import { import {
@ -45,70 +46,20 @@ import {
IMessage, IMessage,
VariableTableDataType, VariableTableDataType,
} from './interface'; } from './interface';
import { ChatModelState } from './model';
import { isConversationIdExist } from './utils';
export const useSelectCurrentDialog = () => { export const useSelectCurrentDialog = () => {
const currentDialog: IDialog = useSelector( const data = useMutationState({
(state: any) => state.chatModel.currentDialog, filters: { mutationKey: ['fetchDialog'] },
); select: (mutation) => {
return get(mutation, 'state.data.data', {});
return currentDialog;
};
export const useFetchDialogOnMount = (
dialogId: string,
visible: boolean,
): IDialog => {
const currentDialog: IDialog = useSelectCurrentDialog();
const fetchDialog = useFetchDialog();
useEffect(() => {
if (dialogId && visible) {
fetchDialog(dialogId);
}
}, [dialogId, fetchDialog, visible]);
return currentDialog;
};
export const useSetCurrentDialog = () => {
const dispatch = useDispatch();
const currentDialog: IDialog = useSelector(
(state: any) => state.chatModel.currentDialog,
);
const setCurrentDialog = useCallback(
(dialogId: string) => {
dispatch({
type: 'chatModel/setCurrentDialog',
payload: { id: dialogId },
});
}, },
[dispatch],
);
return { currentDialog, setCurrentDialog };
};
export const useResetCurrentDialog = () => {
const dispatch = useDispatch();
const resetCurrentDialog = useCallback(() => {
dispatch({
type: 'chatModel/setCurrentDialog',
payload: {},
}); });
}, [dispatch]);
return { resetCurrentDialog }; return (data.at(-1) ?? {}) as IDialog;
}; };
export const useSelectPromptConfigParameters = (): VariableTableDataType[] => { export const useSelectPromptConfigParameters = (): VariableTableDataType[] => {
const currentDialog: IDialog = useSelector( const { data: currentDialog } = useFetchNextDialog();
(state: any) => state.chatModel.currentDialog,
);
const finalParameters: VariableTableDataType[] = useMemo(() => { const finalParameters: VariableTableDataType[] = useMemo(() => {
const parameters = currentDialog?.prompt_config?.parameters ?? []; const parameters = currentDialog?.prompt_config?.parameters ?? [];
@ -129,83 +80,15 @@ export const useSelectPromptConfigParameters = (): VariableTableDataType[] => {
export const useDeleteDialog = () => { export const useDeleteDialog = () => {
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const removeDocument = useRemoveDialog(); const { removeDialog } = useRemoveNextDialog();
const onRemoveDialog = (dialogIds: Array<string>) => { const onRemoveDialog = (dialogIds: Array<string>) => {
showDeleteConfirm({ onOk: () => removeDocument(dialogIds) }); showDeleteConfirm({ onOk: () => removeDialog(dialogIds) });
}; };
return { onRemoveDialog }; return { onRemoveDialog };
}; };
export const useGetChatSearchParams = () => {
const [currentQueryParameters] = useSearchParams();
return {
dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
conversationId:
currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
};
};
export const useSetCurrentConversation = () => {
const dispatch = useDispatch();
const setCurrentConversation = useCallback(
(currentConversation: IClientConversation) => {
dispatch({
type: 'chatModel/setCurrentConversation',
payload: currentConversation,
});
},
[dispatch],
);
return setCurrentConversation;
};
export const useClickDialogCard = () => {
const [currentQueryParameters, setSearchParams] = useSearchParams();
const newQueryParameters: URLSearchParams = useMemo(() => {
return new URLSearchParams();
}, []);
const handleClickDialog = useCallback(
(dialogId: string) => {
newQueryParameters.set(ChatSearchParams.DialogId, dialogId);
// newQueryParameters.set(
// ChatSearchParams.ConversationId,
// EmptyConversationId,
// );
setSearchParams(newQueryParameters);
},
[newQueryParameters, setSearchParams],
);
return { handleClickDialog };
};
export const useSelectFirstDialogOnMount = () => {
const fetchDialogList = useFetchDialogList();
const dialogList = useSelectDialogList();
const { handleClickDialog } = useClickDialogCard();
const fetchList = useCallback(async () => {
const data = await fetchDialogList();
if (data.retcode === 0 && data.data.length > 0) {
handleClickDialog(data.data[0].id);
}
}, [fetchDialogList, handleClickDialog]);
useEffect(() => {
fetchList();
}, [fetchList]);
return dialogList;
};
export const useHandleItemHover = () => { export const useHandleItemHover = () => {
const [activated, setActivated] = useState<string>(''); const [activated, setActivated] = useState<string>('');
@ -226,9 +109,8 @@ export const useHandleItemHover = () => {
export const useEditDialog = () => { export const useEditDialog = () => {
const [dialog, setDialog] = useState<IDialog>({} as IDialog); const [dialog, setDialog] = useState<IDialog>({} as IDialog);
const fetchDialog = useFetchDialog(); const { fetchDialog } = useFetchManualDialog();
const submitDialog = useSetDialog(); const { setDialog: submitDialog, loading } = useSetNextDialog();
const loading = useOneNamespaceEffectsLoading('chatModel', ['setDialog']);
const { const {
visible: dialogEditVisible, visible: dialogEditVisible,
@ -255,7 +137,7 @@ export const useEditDialog = () => {
const handleShowDialogEditModal = useCallback( const handleShowDialogEditModal = useCallback(
async (dialogId?: string) => { async (dialogId?: string) => {
if (dialogId) { if (dialogId) {
const ret = await fetchDialog(dialogId, false); const ret = await fetchDialog(dialogId);
if (ret.retcode === 0) { if (ret.retcode === 0) {
setDialog(ret.data); setDialog(ret.data);
} }
@ -282,25 +164,15 @@ export const useEditDialog = () => {
//#region conversation //#region conversation
export const useFetchConversationListOnMount = () => {
const conversationList = useSelectConversationList();
const { dialogId } = useGetChatSearchParams();
const fetchConversationList = useFetchConversationList();
useEffect(() => {
fetchConversationList(dialogId);
}, [fetchConversationList, dialogId]);
return conversationList;
};
export const useSelectDerivedConversationList = () => { export const useSelectDerivedConversationList = () => {
const { t } = useTranslate('chat');
const [list, setList] = useState<Array<IConversation>>([]); const [list, setList] = useState<Array<IConversation>>([]);
let chatModel: ChatModelState = useSelector((state: any) => state.chatModel); const { data: currentDialog } = useFetchNextDialog();
const { conversationList, currentDialog } = chatModel; const { data: conversationList, loading } = useFetchNextConversationList();
const { dialogId } = useGetChatSearchParams(); const { dialogId } = useGetChatSearchParams();
const prologue = currentDialog?.prompt_config?.prologue ?? ''; const prologue = currentDialog?.prompt_config?.prologue ?? '';
const { t } = useTranslate('chat');
const addTemporaryConversation = useCallback(() => { const addTemporaryConversation = useCallback(() => {
setList((pre) => { setList((pre) => {
if (dialogId) { if (dialogId) {
@ -329,7 +201,7 @@ export const useSelectDerivedConversationList = () => {
addTemporaryConversation(); addTemporaryConversation();
}, [addTemporaryConversation]); }, [addTemporaryConversation]);
return { list, addTemporaryConversation }; return { list, addTemporaryConversation, loading };
}; };
export const useClickConversationCard = () => { export const useClickConversationCard = () => {
@ -352,7 +224,7 @@ export const useClickConversationCard = () => {
export const useSetConversation = () => { export const useSetConversation = () => {
const { dialogId } = useGetChatSearchParams(); const { dialogId } = useGetChatSearchParams();
const updateConversation = useUpdateConversation(); const { updateConversation } = useUpdateNextConversation();
const setConversation = useCallback( const setConversation = useCallback(
(message: string) => { (message: string) => {
@ -376,11 +248,8 @@ export const useSetConversation = () => {
export const useSelectCurrentConversation = () => { export const useSelectCurrentConversation = () => {
const [currentConversation, setCurrentConversation] = const [currentConversation, setCurrentConversation] =
useState<IClientConversation>({} as IClientConversation); useState<IClientConversation>({} as IClientConversation);
const { data: conversation, loading } = useFetchNextConversation();
const conversation: IClientConversation = useSelector( const { data: dialog } = useFetchNextDialog();
(state: any) => state.chatModel.currentConversation,
);
const dialog = useSelectCurrentDialog();
const { conversationId, dialogId } = useGetChatSearchParams(); const { conversationId, dialogId } = useGetChatSearchParams();
const addNewestConversation = useCallback( const addNewestConversation = useCallback(
@ -474,6 +343,7 @@ export const useSelectCurrentConversation = () => {
addNewestConversation, addNewestConversation,
removeLatestMessage, removeLatestMessage,
addNewestAnswer, addNewestAnswer,
loading,
}; };
}; };
@ -495,25 +365,15 @@ export const useScrollToBottom = (currentConversation: IClientConversation) => {
export const useFetchConversationOnMount = () => { export const useFetchConversationOnMount = () => {
const { conversationId } = useGetChatSearchParams(); const { conversationId } = useGetChatSearchParams();
const fetchConversation = useFetchConversation();
const { const {
currentConversation, currentConversation,
addNewestConversation, addNewestConversation,
removeLatestMessage, removeLatestMessage,
addNewestAnswer, addNewestAnswer,
loading,
} = useSelectCurrentConversation(); } = useSelectCurrentConversation();
const ref = useScrollToBottom(currentConversation); const ref = useScrollToBottom(currentConversation);
const fetchConversationOnMount = useCallback(() => {
if (isConversationIdExist(conversationId)) {
fetchConversation(conversationId);
}
}, [fetchConversation, conversationId]);
useEffect(() => {
fetchConversationOnMount();
}, [fetchConversationOnMount]);
return { return {
currentConversation, currentConversation,
addNewestConversation, addNewestConversation,
@ -521,6 +381,7 @@ export const useFetchConversationOnMount = () => {
removeLatestMessage, removeLatestMessage,
addNewestAnswer, addNewestAnswer,
conversationId, conversationId,
loading,
}; };
}; };
@ -655,13 +516,12 @@ export const useGetFileIcon = () => {
}; };
export const useDeleteConversation = () => { export const useDeleteConversation = () => {
const { dialogId } = useGetChatSearchParams();
const { handleClickConversation } = useClickConversationCard(); const { handleClickConversation } = useClickConversationCard();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const removeConversation = useRemoveConversation(); const { removeConversation } = useRemoveNextConversation();
const deleteConversation = (conversationIds: Array<string>) => async () => { const deleteConversation = (conversationIds: Array<string>) => async () => {
const ret = await removeConversation(conversationIds, dialogId); const ret = await removeConversation(conversationIds);
if (ret === 0) { if (ret === 0) {
handleClickConversation(''); handleClickConversation('');
} }
@ -679,13 +539,13 @@ export const useRenameConversation = () => {
const [conversation, setConversation] = useState<IClientConversation>( const [conversation, setConversation] = useState<IClientConversation>(
{} as IClientConversation, {} as IClientConversation,
); );
const fetchConversation = useFetchConversation(); const { fetchConversation } = useFetchManualConversation();
const { const {
visible: conversationRenameVisible, visible: conversationRenameVisible,
hideModal: hideConversationRenameModal, hideModal: hideConversationRenameModal,
showModal: showConversationRenameModal, showModal: showConversationRenameModal,
} = useSetModalState(); } = useSetModalState();
const updateConversation = useUpdateConversation(); const { updateConversation, loading } = useUpdateNextConversation();
const onConversationRenameOk = useCallback( const onConversationRenameOk = useCallback(
async (name: string) => { async (name: string) => {
@ -702,13 +562,9 @@ export const useRenameConversation = () => {
[updateConversation, conversation, hideConversationRenameModal], [updateConversation, conversation, hideConversationRenameModal],
); );
const loading = useOneNamespaceEffectsLoading('chatModel', [
'setConversation',
]);
const handleShowConversationRenameModal = useCallback( const handleShowConversationRenameModal = useCallback(
async (conversationId: string) => { async (conversationId: string) => {
const ret = await fetchConversation(conversationId, false); const ret = await fetchConversation(conversationId);
if (ret.retcode === 0) { if (ret.retcode === 0) {
setConversation(ret.data); setConversation(ret.data);
} }
@ -751,16 +607,6 @@ export const useClickDrawer = () => {
}; };
}; };
export const useSelectDialogListLoading = () => {
return useOneNamespaceEffectsLoading('chatModel', ['listDialog']);
};
export const useSelectConversationListLoading = () => {
return useOneNamespaceEffectsLoading('chatModel', ['listConversation']);
};
export const useSelectConversationLoading = () => {
return useOneNamespaceEffectsLoading('chatModel', ['getConversation']);
};
export const useGetSendButtonDisabled = () => { export const useGetSendButtonDisabled = () => {
const { dialogId, conversationId } = useGetChatSearchParams(); const { dialogId, conversationId } = useGetChatSearchParams();

View File

@ -26,22 +26,20 @@ import ChatConfigurationModal from './chat-configuration-modal';
import ChatContainer from './chat-container'; import ChatContainer from './chat-container';
import { import {
useClickConversationCard, useClickConversationCard,
useClickDialogCard,
useDeleteConversation, useDeleteConversation,
useDeleteDialog, useDeleteDialog,
useEditDialog, useEditDialog,
useFetchConversationListOnMount,
useFetchDialogOnMount,
useGetChatSearchParams,
useHandleItemHover, useHandleItemHover,
useRenameConversation, useRenameConversation,
useSelectConversationListLoading,
useSelectDerivedConversationList, useSelectDerivedConversationList,
useSelectDialogListLoading,
useSelectFirstDialogOnMount,
} from './hooks'; } from './hooks';
import ChatOverviewModal from '@/components/api-service/chat-overview-modal'; import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
import {
useClickDialogCard,
useFetchNextDialogList,
useGetChatSearchParams,
} from '@/hooks/chat-hooks';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { IDialog } from '@/interfaces/database/chat'; import { IDialog } from '@/interfaces/database/chat';
@ -50,14 +48,17 @@ import styles from './index.less';
const { Text } = Typography; const { Text } = Typography;
const Chat = () => { const Chat = () => {
const dialogList = useSelectFirstDialogOnMount(); const { data: dialogList, loading: dialogLoading } = useFetchNextDialogList();
const { onRemoveDialog } = useDeleteDialog(); const { onRemoveDialog } = useDeleteDialog();
const { onRemoveConversation } = useDeleteConversation(); const { onRemoveConversation } = useDeleteConversation();
const { handleClickDialog } = useClickDialogCard(); const { handleClickDialog } = useClickDialogCard();
const { handleClickConversation } = useClickConversationCard(); const { handleClickConversation } = useClickConversationCard();
const { dialogId, conversationId } = useGetChatSearchParams(); const { dialogId, conversationId } = useGetChatSearchParams();
const { list: conversationList, addTemporaryConversation } = const {
useSelectDerivedConversationList(); list: conversationList,
addTemporaryConversation,
loading: conversationLoading,
} = useSelectDerivedConversationList();
const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover(); const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover();
const { const {
activated: conversationActivated, activated: conversationActivated,
@ -81,8 +82,6 @@ const Chat = () => {
hideDialogEditModal, hideDialogEditModal,
showDialogEditModal, showDialogEditModal,
} = useEditDialog(); } = useEditDialog();
const dialogLoading = useSelectDialogListLoading();
const conversationLoading = useSelectConversationListLoading();
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const { const {
visible: overviewVisible, visible: overviewVisible,
@ -91,8 +90,6 @@ const Chat = () => {
} = useSetModalState(); } = useSetModalState();
const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>(); const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>();
useFetchDialogOnMount(dialogId, true);
const handleAppCardEnter = (id: string) => () => { const handleAppCardEnter = (id: string) => () => {
handleItemEnter(id); handleItemEnter(id);
}; };
@ -236,8 +233,6 @@ const Chat = () => {
return appItems; return appItems;
}; };
useFetchConversationListOnMount();
return ( return (
<Flex className={styles.chatWrapper}> <Flex className={styles.chatWrapper}>
<Flex className={styles.chatAppWrapper}> <Flex className={styles.chatAppWrapper}>

View File

@ -1,10 +1,9 @@
import { MessageType, SharedFrom } from '@/constants/chat'; import { MessageType, SharedFrom } from '@/constants/chat';
import { import {
useCreateSharedConversation, useCreateNextSharedConversation,
useFetchSharedConversation, useFetchNextSharedConversation,
} from '@/hooks/chat-hooks'; } from '@/hooks/chat-hooks';
import { useSendMessageWithSse } from '@/hooks/logic-hooks'; import { useSendMessageWithSse } from '@/hooks/logic-hooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
import { IAnswer, Message } from '@/interfaces/database/chat'; import { IAnswer, Message } from '@/interfaces/database/chat';
import api from '@/utils/api'; import api from '@/utils/api';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
@ -25,12 +24,12 @@ export const useCreateSharedConversationOnMount = () => {
const [currentQueryParameters] = useSearchParams(); const [currentQueryParameters] = useSearchParams();
const [conversationId, setConversationId] = useState(''); const [conversationId, setConversationId] = useState('');
const createConversation = useCreateSharedConversation(); const { createSharedConversation: createConversation } =
useCreateNextSharedConversation();
const sharedId = currentQueryParameters.get('shared_id'); const sharedId = currentQueryParameters.get('shared_id');
const userId = currentQueryParameters.get('user_id'); const userId = currentQueryParameters.get('user_id');
const setConversation = useCallback(async () => { const setConversation = useCallback(async () => {
console.info(sharedId);
if (sharedId) { if (sharedId) {
const data = await createConversation(userId ?? undefined); const data = await createConversation(userId ?? undefined);
const id = data.data?.id; const id = data.data?.id;
@ -50,10 +49,7 @@ export const useCreateSharedConversationOnMount = () => {
export const useSelectCurrentSharedConversation = (conversationId: string) => { export const useSelectCurrentSharedConversation = (conversationId: string) => {
const [currentConversation, setCurrentConversation] = const [currentConversation, setCurrentConversation] =
useState<IClientConversation>({} as IClientConversation); useState<IClientConversation>({} as IClientConversation);
const fetchConversation = useFetchSharedConversation(); const { fetchConversation, loading } = useFetchNextSharedConversation();
const loading = useOneNamespaceEffectsLoading('chatModel', [
'getExternalConversation',
]);
const ref = useScrollToBottom(currentConversation); const ref = useScrollToBottom(currentConversation);
@ -147,7 +143,8 @@ export const useSendSharedMessage = (
addNewestAnswer: (answer: IAnswer) => void, addNewestAnswer: (answer: IAnswer) => void,
) => { ) => {
const conversationId = conversation.id; const conversationId = conversation.id;
const setConversation = useCreateSharedConversation(); const { createSharedConversation: setConversation } =
useCreateNextSharedConversation();
const { handleInputChange, value, setValue } = useHandleMessageInputChange(); const { handleInputChange, value, setValue } = useHandleMessageInputChange();
const { send, answer, done } = useSendMessageWithSse( const { send, answer, done } = useSendMessageWithSse(

View File

@ -1,72 +1,14 @@
import { Authorization } from '@/constants/authorization'; import { Authorization } from '@/constants/authorization';
import { getAuthorization } from '@/utils/authorization-util'; import { getAuthorization } from '@/utils/authorization-util';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import type { GetProp, UploadFile, UploadProps } from 'antd'; import type { UploadFile, UploadProps } from 'antd';
import { Image, Input, Upload } from 'antd'; import { Image, Input, Upload } from 'antd';
import { useState } from 'react'; import { useState } from 'react';
import { useGetChatSearchParams } from '../chat/hooks';
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
const getBase64 = (file: FileType): Promise<string> =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
const InputWithUpload = () => { const InputWithUpload = () => {
const [previewOpen, setPreviewOpen] = useState(false); const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState(''); const [previewImage, setPreviewImage] = useState('');
const { conversationId } = useGetChatSearchParams(); const [fileList, setFileList] = useState<UploadFile[]>([]);
const [fileList, setFileList] = useState<UploadFile[]>([
{
uid: '-1',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-2',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-3',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-4',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-xxx',
percent: 50,
name: 'image.png',
status: 'uploading',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-5',
name: 'image.png',
status: 'error',
},
]);
const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj as FileType);
}
setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
};
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) =>
setFileList(newFileList); setFileList(newFileList);
@ -84,7 +26,6 @@ const InputWithUpload = () => {
action="/v1/document/upload_and_parse" action="/v1/document/upload_and_parse"
listType="picture-card" listType="picture-card"
fileList={fileList} fileList={fileList}
onPreview={handlePreview}
onChange={handleChange} onChange={handleChange}
multiple multiple
headers={{ [Authorization]: getAuthorization() }} headers={{ [Authorization]: getAuthorization() }}

View File

@ -70,9 +70,13 @@ const SystemInfo = () => {
key={key} key={key}
> >
{key === 'task_executor' ? ( {key === 'task_executor' ? (
info?.elapsed ? (
<TaskBarChat <TaskBarChat
data={info.elapsed as TaskExecutorElapsed} data={info.elapsed as TaskExecutorElapsed}
></TaskBarChat> ></TaskBarChat>
) : (
<Text className={styles.error}>{info.error}</Text>
)
) : ( ) : (
Object.keys(info) Object.keys(info)
.filter((x) => x !== 'status') .filter((x) => x !== 'status')

5
web/src/utils/chat.ts Normal file
View File

@ -0,0 +1,5 @@
import { EmptyConversationId } from '@/constants/chat';
export const isConversationIdExist = (conversationId: string) => {
return conversationId !== EmptyConversationId && conversationId !== '';
};