Files
ragflow/web/src/hooks/use-chat-request.ts
balibabu 9d0fed601d Feat: Displays the embedded page of the chat module #3221 (#9532)
### What problem does this PR solve?

Feat: Displays the embedded page of the chat module #3221
Feat: Let the agen operator support the selection of tts model #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
2025-08-18 18:02:13 +08:00

496 lines
13 KiB
TypeScript

import message from '@/components/ui/message';
import { ChatSearchParams } from '@/constants/chat';
import {
IConversation,
IDialog,
IExternalChatInfo,
} from '@/interfaces/database/chat';
import { IAskRequestBody } from '@/interfaces/request/chat';
import { IClientConversation } from '@/pages/next-chats/chat/interface';
import { useGetSharedChatSearchParams } from '@/pages/next-chats/hooks/use-send-shared-message';
import { isConversationIdExist } from '@/pages/next-chats/utils';
import chatService from '@/services/next-chat-service';
import { buildMessageListWithUuid, getConversationId } from '@/utils/chat';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useDebounce } from 'ahooks';
import { has } from 'lodash';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams, useSearchParams } from 'umi';
import {
useGetPaginationWithRouter,
useHandleSearchChange,
} from './logic-hooks';
import { useHandleSearchStrChange } from './logic-hooks/use-change-search';
export const enum ChatApiAction {
FetchDialogList = 'fetchDialogList',
RemoveDialog = 'removeDialog',
SetDialog = 'setDialog',
FetchDialog = 'fetchDialog',
FetchConversationList = 'fetchConversationList',
FetchConversation = 'fetchConversation',
UpdateConversation = 'updateConversation',
RemoveConversation = 'removeConversation',
DeleteMessage = 'deleteMessage',
FetchMindMap = 'fetchMindMap',
FetchRelatedQuestions = 'fetchRelatedQuestions',
UploadAndParse = 'upload_and_parse',
FetchExternalChatInfo = 'fetchExternalChatInfo',
}
export const useGetChatSearchParams = () => {
const [currentQueryParameters] = useSearchParams();
return {
dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
conversationId:
currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
isNew: currentQueryParameters.get(ChatSearchParams.isNew) || '',
};
};
export const useClickDialogCard = () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, 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 useFetchDialogList = () => {
const { searchString, handleInputChange } = useHandleSearchChange();
const { pagination, setPagination } = useGetPaginationWithRouter();
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
const {
data,
isFetching: loading,
refetch,
} = useQuery<{ dialogs: IDialog[]; total: number }>({
queryKey: [
ChatApiAction.FetchDialogList,
{
debouncedSearchString,
...pagination,
},
],
initialData: { dialogs: [], total: 0 },
gcTime: 0,
refetchOnWindowFocus: false,
queryFn: async () => {
const { data } = await chatService.listDialog(
{
params: {
keywords: debouncedSearchString,
page_size: pagination.pageSize,
page: pagination.current,
},
data: {},
},
true,
);
return data?.data ?? { dialogs: [], total: 0 };
},
});
const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback(
(e) => {
handleInputChange(e);
},
[handleInputChange],
);
return {
data,
loading,
refetch,
searchString,
handleInputChange: onInputChange,
pagination: { ...pagination, total: data?.total },
setPagination,
};
};
export const useRemoveDialog = () => {
const queryClient = useQueryClient();
const { t } = useTranslation();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.RemoveDialog],
mutationFn: async (dialogIds: string[]) => {
const { data } = await chatService.removeDialog({ dialogIds });
if (data.code === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchDialogList'] });
message.success(t('message.deleted'));
}
return data.code;
},
});
return { data, loading, removeDialog: mutateAsync };
};
export const useSetDialog = () => {
const queryClient = useQueryClient();
const { t } = useTranslation();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.SetDialog],
mutationFn: async (params: Partial<IDialog>) => {
const { data } = await chatService.setDialog(params);
if (data.code === 0) {
queryClient.invalidateQueries({
exact: false,
queryKey: [ChatApiAction.FetchDialogList],
});
queryClient.invalidateQueries({
queryKey: [ChatApiAction.FetchDialog],
});
message.success(
t(`message.${params.dialog_id ? 'modified' : 'created'}`),
);
}
return data?.code;
},
});
return { data, loading, setDialog: mutateAsync };
};
export const useFetchDialog = () => {
const { id } = useParams();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IDialog>({
queryKey: [ChatApiAction.FetchDialog, id],
gcTime: 0,
initialData: {} as IDialog,
enabled: !!id,
refetchOnWindowFocus: false,
queryFn: async () => {
const { data } = await chatService.getDialog(
{ params: { dialogId: id } },
true,
);
return data?.data ?? ({} as IDialog);
},
});
return { data, loading, refetch };
};
//#region Conversation
export const useClickConversationCard = () => {
const [currentQueryParameters, setSearchParams] = useSearchParams();
const newQueryParameters: URLSearchParams = useMemo(
() => new URLSearchParams(currentQueryParameters.toString()),
[currentQueryParameters],
);
const handleClickConversation = useCallback(
(conversationId: string, isNew: string) => {
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
newQueryParameters.set(ChatSearchParams.isNew, isNew);
setSearchParams(newQueryParameters);
},
[setSearchParams, newQueryParameters],
);
return { handleClickConversation };
};
export const useFetchConversationList = () => {
const { id } = useParams();
const { handleClickConversation } = useClickConversationCard();
const { searchString, handleInputChange } = useHandleSearchStrChange();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IConversation[]>({
queryKey: [ChatApiAction.FetchConversationList, id],
initialData: [],
gcTime: 0,
refetchOnWindowFocus: false,
enabled: !!id,
select(data) {
return searchString
? data.filter((x) => x.name.includes(searchString))
: data;
},
queryFn: async () => {
const { data } = await chatService.listConversation(
{ params: { dialog_id: id } },
true,
);
if (data.code === 0) {
if (data.data.length > 0) {
handleClickConversation(data.data[0].id, '');
} else {
handleClickConversation('', '');
}
}
return data?.data;
},
});
return { data, loading, refetch, searchString, handleInputChange };
};
export const useFetchConversation = () => {
const { isNew, conversationId } = useGetChatSearchParams();
const { sharedId } = useGetSharedChatSearchParams();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IClientConversation>({
queryKey: [ChatApiAction.FetchConversation, conversationId],
initialData: {} as IClientConversation,
// enabled: isConversationIdExist(conversationId),
gcTime: 0,
refetchOnWindowFocus: false,
queryFn: async () => {
if (
isNew !== 'true' &&
isConversationIdExist(sharedId || conversationId)
) {
const { data } = await chatService.getConversation(
{
params: {
conversationId: conversationId || sharedId,
},
},
true,
);
const conversation = data?.data ?? {};
const messageList = buildMessageListWithUuid(conversation?.message);
return { ...conversation, message: messageList };
}
return { message: [] };
},
});
return { data, loading, refetch };
};
export const useUpdateConversation = () => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.UpdateConversation],
mutationFn: async (params: Record<string, any>) => {
const { data } = await chatService.setConversation({
...params,
conversation_id: params.conversation_id
? params.conversation_id
: getConversationId(),
});
if (data.code === 0) {
queryClient.invalidateQueries({
queryKey: [ChatApiAction.FetchConversationList],
});
message.success(t(`message.modified`));
}
return data;
},
});
return { data, loading, updateConversation: mutateAsync };
};
export const useRemoveConversation = () => {
const queryClient = useQueryClient();
const { dialogId } = useGetChatSearchParams();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.RemoveConversation],
mutationFn: async (conversationIds: string[]) => {
const { data } = await chatService.removeConversation({
conversationIds,
dialogId,
});
if (data.code === 0) {
queryClient.invalidateQueries({
queryKey: [ChatApiAction.FetchConversationList],
});
}
return data.code;
},
});
return { data, loading, removeConversation: mutateAsync };
};
export const useDeleteMessage = () => {
const { conversationId } = useGetChatSearchParams();
const { t } = useTranslation();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.DeleteMessage],
mutationFn: async (messageId: string) => {
const { data } = await chatService.deleteMessage({
messageId,
conversationId,
});
if (data.code === 0) {
message.success(t(`message.deleted`));
}
return data.code;
},
});
return { data, loading, deleteMessage: mutateAsync };
};
export function useUploadAndParseFile() {
const { conversationId } = useGetChatSearchParams();
const { t } = useTranslation();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.UploadAndParse],
mutationFn: async (file: File) => {
const formData = new FormData();
formData.append('file', file);
formData.append('conversation_id', conversationId);
const { data } = await chatService.uploadAndParse(formData);
if (data.code === 0) {
message.success(t(`message.uploaded`));
}
return data;
},
});
return { data, loading, uploadAndParseFile: mutateAsync };
}
export const useFetchExternalChatInfo = () => {
const { sharedId: id } = useGetSharedChatSearchParams();
const {
data,
isFetching: loading,
refetch,
} = useQuery<IExternalChatInfo>({
queryKey: [ChatApiAction.FetchExternalChatInfo, id],
gcTime: 0,
initialData: {} as IExternalChatInfo,
enabled: !!id,
refetchOnWindowFocus: false,
queryFn: async () => {
const { data } = await chatService.fetchExternalChatInfo(id!);
return data?.data;
},
});
return { data, loading, refetch };
};
//#endregion
//#region search page
export const useFetchMindMap = () => {
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.FetchMindMap],
gcTime: 0,
mutationFn: async (params: IAskRequestBody) => {
try {
const ret = await chatService.getMindMap(params);
return ret?.data?.data ?? {};
} catch (error: any) {
if (has(error, 'message')) {
message.error(error.message);
}
return [];
}
},
});
return { data, loading, fetchMindMap: mutateAsync };
};
export const useFetchRelatedQuestions = () => {
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.FetchRelatedQuestions],
gcTime: 0,
mutationFn: async (question: string): Promise<string[]> => {
const { data } = await chatService.getRelatedQuestions({ question });
return data?.data ?? [];
},
});
return { data, loading, fetchRelatedQuestions: mutateAsync };
};
//#endregion