Browse Source

Feat: Create a conversation #3221 (#9269)

### What problem does this PR solve?

Feat: Create a conversation #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.1
balibabu 2 months ago
parent
commit
fb0426419e
No account linked to committer's email address

+ 42
- 30
web/src/hooks/use-chat-request.ts View File

@@ -1,12 +1,12 @@
import message from '@/components/ui/message';
import { ChatSearchParams } from '@/constants/chat';
import { IDialog } from '@/interfaces/database/chat';
import chatService from '@/services/chat-service';
import chatService from '@/services/next-chat-service ';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useDebounce } from 'ahooks';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { history, useSearchParams } from 'umi';
import { useParams, useSearchParams } from 'umi';
import {
useGetPaginationWithRouter,
useHandleSearchChange,
@@ -16,6 +16,7 @@ export const enum ChatApiAction {
FetchDialogList = 'fetchDialogList',
RemoveDialog = 'removeDialog',
SetDialog = 'setDialog',
FetchDialog = 'fetchDialog',
}

export const useGetChatSearchParams = () => {
@@ -52,9 +53,7 @@ export const useClickDialogCard = () => {
return { handleClickDialog };
};

export const useFetchDialogList = (pureFetch = false) => {
const { handleClickDialog } = useClickDialogCard();
const { dialogId } = useGetChatSearchParams();
export const useFetchDialogList = () => {
const { searchString, handleInputChange } = useHandleSearchChange();
const { pagination, setPagination } = useGetPaginationWithRouter();
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
@@ -63,7 +62,7 @@ export const useFetchDialogList = (pureFetch = false) => {
data,
isFetching: loading,
refetch,
} = useQuery<IDialog[]>({
} = useQuery<{ dialogs: IDialog[]; total: number }>({
queryKey: [
ChatApiAction.FetchDialogList,
{
@@ -71,27 +70,17 @@ export const useFetchDialogList = (pureFetch = false) => {
...pagination,
},
],
initialData: [],
initialData: { dialogs: [], total: 0 },
gcTime: 0,
refetchOnWindowFocus: false,
queryFn: async (...params) => {
console.log('🚀 ~ queryFn: ~ params:', params);
const { data } = await chatService.listDialog();

if (data.code === 0) {
const list: IDialog[] = data.data;
if (!pureFetch) {
if (list.length > 0) {
if (list.every((x) => x.id !== dialogId)) {
handleClickDialog(data.data[0].id);
}
} else {
history.push('/chat');
}
}
}

return data?.data ?? [];
queryFn: async () => {
const { data } = await chatService.listDialog({
keywords: debouncedSearchString,
page_size: pagination.pageSize,
page: pagination.current,
});

return data?.data ?? { dialogs: [], total: 0 };
},
});

@@ -147,17 +136,14 @@ export const useSetDialog = () => {
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.SetDialog],
mutationFn: async (params: IDialog) => {
mutationFn: async (params: Partial<IDialog>) => {
const { data } = await chatService.setDialog(params);
if (data.code === 0) {
queryClient.invalidateQueries({
exact: false,
queryKey: ['fetchDialogList'],
queryKey: [ChatApiAction.FetchDialogList],
});

queryClient.invalidateQueries({
queryKey: ['fetchDialog'],
});
message.success(
t(`message.${params.dialog_id ? 'modified' : 'created'}`),
);
@@ -168,3 +154,29 @@ export const useSetDialog = () => {

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 };
};

+ 2
- 2
web/src/pages/home/chat-list.tsx View File

@@ -2,9 +2,9 @@ import { useFetchDialogList } from '@/hooks/use-chat-request';
import { ApplicationCard } from './application-card';

export function ChatList() {
const { data } = useFetchDialogList(true);
const { data } = useFetchDialogList();

return data
return data.dialogs
.slice(0, 10)
.map((x) => (
<ApplicationCard

+ 3
- 1
web/src/pages/next-chats/chat/index.tsx View File

@@ -1,6 +1,7 @@
import { PageHeader } from '@/components/page-header';
import { Button } from '@/components/ui/button';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchDialog } from '@/hooks/use-chat-request';
import { EllipsisVertical } from 'lucide-react';
import { AppSettings } from './app-settings';
import { ChatBox } from './chat-box';
@@ -8,10 +9,11 @@ import { Sessions } from './sessions';

export default function Chat() {
const { navigateToChatList } = useNavigatePage();
useFetchDialog();

return (
<section className="h-full flex flex-col">
<PageHeader back={navigateToChatList} title="Chat app 01">
<PageHeader>
<div className="flex items-center gap-2">
<Button variant={'icon'} size={'icon'}>
<EllipsisVertical />

+ 39
- 6
web/src/pages/next-chats/hooks/use-rename-chat.ts View File

@@ -1,8 +1,33 @@
import { useSetModalState } from '@/hooks/common-hooks';
import { useSetDialog } from '@/hooks/use-chat-request';
import { IDialog } from '@/interfaces/database/chat';
import { isEmpty } from 'lodash';
import { useCallback, useState } from 'react';

const InitialData = {
name: '',
icon: '',
language: 'English',
prompt_config: {
empty_response: '',
prologue: '你好! 我是你的助理,有什么可以帮到你的吗?',
quote: true,
keyword: false,
tts: false,
system:
'你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。\n 以下是知识库:\n {knowledge}\n 以上是知识库。',
refine_multiturn: false,
use_kg: false,
reasoning: false,
parameters: [{ key: 'knowledge', optional: false }],
},
llm_id: '',
llm_setting: {},
similarity_threshold: 0.2,
vector_similarity_weight: 0.30000000000000004,
top_n: 8,
};

export const useRenameChat = () => {
const [chat, setChat] = useState<IDialog>({} as IDialog);
const {
@@ -14,10 +39,11 @@ export const useRenameChat = () => {

const onChatRenameOk = useCallback(
async (name: string) => {
const ret = await setDialog({
...chat,
const nextChat = {
...(isEmpty(chat) ? InitialData : chat),
name,
});
};
const ret = await setDialog(nextChat);

if (ret === 0) {
hideChatRenameModal();
@@ -27,19 +53,26 @@ export const useRenameChat = () => {
);

const handleShowChatRenameModal = useCallback(
async (record: IDialog) => {
setChat(record);
(record?: IDialog) => {
if (record) {
setChat(record);
}
showChatRenameModal();
},
[showChatRenameModal],
);

const handleHideModal = useCallback(() => {
hideChatRenameModal();
setChat({} as IDialog);
}, [hideChatRenameModal]);

return {
chatRenameLoading: loading,
initialChatName: chat?.name,
onChatRenameOk,
chatRenameVisible,
hideChatRenameModal,
hideChatRenameModal: handleHideModal,
showChatRenameModal: handleShowChatRenameModal,
};
};

+ 8
- 3
web/src/pages/next-chats/index.tsx View File

@@ -11,7 +11,7 @@ import { ChatCard } from './chat-card';
import { useRenameChat } from './hooks/use-rename-chat';

export default function ChatList() {
const { data: chatList, setPagination, pagination } = useFetchDialogList();
const { data, setPagination, pagination } = useFetchDialogList();
const { t } = useTranslation();
const {
initialChatName,
@@ -29,11 +29,15 @@ export default function ChatList() {
[setPagination],
);

const handleShowCreateModal = useCallback(() => {
showChatRenameModal();
}, [showChatRenameModal]);

return (
<section className="flex flex-col w-full flex-1">
<div className="px-8 pt-8">
<ListFilterBar title="Chat apps">
<Button>
<Button onClick={handleShowCreateModal}>
<Plus className="size-2.5" />
{t('chat.createChat')}
</Button>
@@ -41,7 +45,7 @@ export default function ChatList() {
</div>
<div className="flex-1 overflow-auto">
<div className="flex flex-wrap gap-4 px-8">
{chatList.map((x) => {
{data.dialogs.map((x) => {
return (
<ChatCard
key={x.id}
@@ -65,6 +69,7 @@ export default function ChatList() {
onOk={onChatRenameOk}
initialName={initialChatName}
loading={chatRenameLoading}
title={initialChatName || t('chat.createChat')}
></RenameDialog>
)}
</section>

+ 133
- 0
web/src/services/next-chat-service .ts View File

@@ -0,0 +1,133 @@
import api from '@/utils/api';
import { registerNextServer } from '@/utils/register-server';

const {
getDialog,
setDialog,
listDialog,
removeDialog,
getConversation,
getConversationSSE,
setConversation,
completeConversation,
listConversation,
removeConversation,
createToken,
listToken,
removeToken,
getStats,
createExternalConversation,
getExternalConversation,
completeExternalConversation,
uploadAndParseExternal,
deleteMessage,
thumbup,
tts,
ask,
mindmap,
getRelatedQuestions,
listNextDialog,
} = api;

const methods = {
getDialog: {
url: getDialog,
method: 'get',
},
setDialog: {
url: setDialog,
method: 'post',
},
removeDialog: {
url: removeDialog,
method: 'post',
},
listDialog: {
url: listNextDialog,
method: 'post',
},
listConversation: {
url: listConversation,
method: 'get',
},
getConversation: {
url: getConversation,
method: 'get',
},
getConversationSSE: {
url: getConversationSSE,
method: 'get',
},
setConversation: {
url: setConversation,
method: 'post',
},
completeConversation: {
url: completeConversation,
method: 'post',
},
removeConversation: {
url: removeConversation,
method: 'post',
},
createToken: {
url: createToken,
method: 'post',
},
listToken: {
url: listToken,
method: 'get',
},
removeToken: {
url: removeToken,
method: 'post',
},
getStats: {
url: getStats,
method: 'get',
},
createExternalConversation: {
url: createExternalConversation,
method: 'get',
},
getExternalConversation: {
url: getExternalConversation,
method: 'get',
},
completeExternalConversation: {
url: completeExternalConversation,
method: 'post',
},
uploadAndParseExternal: {
url: uploadAndParseExternal,
method: 'post',
},
deleteMessage: {
url: deleteMessage,
method: 'post',
},
thumbup: {
url: thumbup,
method: 'post',
},
tts: {
url: tts,
method: 'post',
},
ask: {
url: ask,
method: 'post',
},
getMindMap: {
url: mindmap,
method: 'post',
},
getRelatedQuestions: {
url: getRelatedQuestions,
method: 'post',
},
} as const;

const chatService = registerNextServer<keyof typeof methods>(methods);

export default chatService;

+ 3
- 0
web/src/utils/api.ts View File

@@ -108,6 +108,9 @@ export default {
completeExternalConversation: `${api_host}/api/completion`,
uploadAndParseExternal: `${api_host}/api/document/upload_and_parse`,

// next chat
listNextDialog: `${api_host}/dialog/next`,

// file manager
listFile: `${api_host}/file/list`,
uploadFile: `${api_host}/file/upload`,

Loading…
Cancel
Save