From 581a54fbbbb7790c213ab39e28eabb56aaa87232 Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 7 Aug 2025 09:41:03 +0800 Subject: [PATCH] Feat: Search conversation by name #3221 (#9283) ### What problem does this PR solve? Feat: Search conversation by name #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/use-chat-request.ts | 250 ++++++++++++++++++++- web/src/pages/next-chats/chat/index.tsx | 35 ++- web/src/pages/next-chats/chat/interface.ts | 33 +++ web/src/pages/next-chats/chat/sessions.tsx | 21 +- web/src/pages/next-chats/index.tsx | 9 +- 5 files changed, 318 insertions(+), 30 deletions(-) create mode 100644 web/src/pages/next-chats/chat/interface.ts diff --git a/web/src/hooks/use-chat-request.ts b/web/src/hooks/use-chat-request.ts index 62f05047c..d7e5a3642 100644 --- a/web/src/hooks/use-chat-request.ts +++ b/web/src/hooks/use-chat-request.ts @@ -1,9 +1,15 @@ import message from '@/components/ui/message'; import { ChatSearchParams } from '@/constants/chat'; -import { IDialog } from '@/interfaces/database/chat'; +import { IConversation, IDialog } 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'; @@ -17,6 +23,13 @@ export const enum ChatApiAction { RemoveDialog = 'removeDialog', SetDialog = 'setDialog', FetchDialog = 'fetchDialog', + FetchConversationList = 'fetchConversationList', + FetchConversation = 'fetchConversation', + UpdateConversation = 'updateConversation', + RemoveConversation = 'removeConversation', + DeleteMessage = 'deleteMessage', + FetchMindMap = 'fetchMindMap', + FetchRelatedQuestions = 'fetchRelatedQuestions', } export const useGetChatSearchParams = () => { @@ -74,11 +87,17 @@ export const useFetchDialogList = () => { gcTime: 0, refetchOnWindowFocus: false, queryFn: async () => { - const { data } = await chatService.listDialog({ - keywords: debouncedSearchString, - page_size: pagination.pageSize, - page: pagination.current, - }); + const { data } = await chatService.listDialog( + { + params: { + keywords: debouncedSearchString, + page_size: pagination.pageSize, + page: pagination.current, + }, + data: {}, + }, + true, + ); return data?.data ?? { dialogs: [], total: 0 }; }, @@ -180,3 +199,222 @@ export const useFetchDialog = () => { 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 { + data, + isFetching: loading, + refetch, + } = useQuery({ + queryKey: [ChatApiAction.FetchConversationList, id], + initialData: [], + gcTime: 0, + refetchOnWindowFocus: false, + enabled: !!id, + 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 }; +}; + +export const useFetchConversation = () => { + const { isNew, conversationId } = useGetChatSearchParams(); + const { sharedId } = useGetSharedChatSearchParams(); + const { + data, + isFetching: loading, + refetch, + } = useQuery({ + 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({ + conversationId: conversationId || sharedId, + }); + + 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) => { + 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 }; +}; + +//#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 => { + const { data } = await chatService.getRelatedQuestions({ question }); + + return data?.data ?? []; + }, + }); + + return { data, loading, fetchRelatedQuestions: mutateAsync }; +}; +//#endregion diff --git a/web/src/pages/next-chats/chat/index.tsx b/web/src/pages/next-chats/chat/index.tsx index f133501c5..7211d30db 100644 --- a/web/src/pages/next-chats/chat/index.tsx +++ b/web/src/pages/next-chats/chat/index.tsx @@ -1,27 +1,40 @@ import { PageHeader } from '@/components/page-header'; -import { Button } from '@/components/ui/button'; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from '@/components/ui/breadcrumb'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchDialog } from '@/hooks/use-chat-request'; -import { EllipsisVertical } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; import { AppSettings } from './app-settings'; import { ChatBox } from './chat-box'; import { Sessions } from './sessions'; export default function Chat() { const { navigateToChatList } = useNavigatePage(); - useFetchDialog(); + const { data } = useFetchDialog(); + const { t } = useTranslation(); return (
-
- - -
+ + + + + {t('chat.chat')} + + + + + {data.name} + + +
diff --git a/web/src/pages/next-chats/chat/interface.ts b/web/src/pages/next-chats/chat/interface.ts new file mode 100644 index 000000000..570c6da21 --- /dev/null +++ b/web/src/pages/next-chats/chat/interface.ts @@ -0,0 +1,33 @@ +import { IConversation, IReference, Message } from '@/interfaces/database/chat'; +import { FormInstance } from 'antd'; + +export interface ISegmentedContentProps { + show: boolean; + form: FormInstance; + setHasError: (hasError: boolean) => void; +} + +export interface IVariable { + temperature: number; + top_p: number; + frequency_penalty: number; + presence_penalty: number; + max_tokens: number; +} + +export interface VariableTableDataType { + key: string; + variable: string; + optional: boolean; +} + +export type IPromptConfigParameters = Omit; + +export interface IMessage extends Message { + id: string; + reference?: IReference; // the latest news has reference +} + +export interface IClientConversation extends IConversation { + message: IMessage[]; +} diff --git a/web/src/pages/next-chats/chat/sessions.tsx b/web/src/pages/next-chats/chat/sessions.tsx index d996bb4d0..6e7013dfa 100644 --- a/web/src/pages/next-chats/chat/sessions.tsx +++ b/web/src/pages/next-chats/chat/sessions.tsx @@ -1,15 +1,15 @@ +import { MoreButton } from '@/components/more-button'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; -import { EllipsisVertical, Plus } from 'lucide-react'; +import { useFetchConversationList } from '@/hooks/use-chat-request'; +import { Plus } from 'lucide-react'; function SessionCard() { return ( - - + + xxx - + ); @@ -17,20 +17,19 @@ function SessionCard() { export function Sessions() { const sessionList = new Array(10).fill(1); + const {} = useFetchConversationList(); return (
- - Sessions - -
{sessionList.map((x) => ( - + ))}
diff --git a/web/src/pages/next-chats/index.tsx b/web/src/pages/next-chats/index.tsx index 747bff50d..ad82f1b2e 100644 --- a/web/src/pages/next-chats/index.tsx +++ b/web/src/pages/next-chats/index.tsx @@ -11,7 +11,8 @@ import { ChatCard } from './chat-card'; import { useRenameChat } from './hooks/use-rename-chat'; export default function ChatList() { - const { data, setPagination, pagination } = useFetchDialogList(); + const { data, setPagination, pagination, handleInputChange, searchString } = + useFetchDialogList(); const { t } = useTranslation(); const { initialChatName, @@ -36,7 +37,11 @@ export default function ChatList() { return (
- +