diff --git a/web/src/hooks/use-llm-request.ts b/web/src/hooks/use-llm-request.ts index 93970b63c..9608e4737 100644 --- a/web/src/hooks/use-llm-request.ts +++ b/web/src/hooks/use-llm-request.ts @@ -8,9 +8,13 @@ import { } from '@/interfaces/database/llm'; import { buildLlmUuid } from '@/utils/llm-util'; +export const enum LLMApiAction { + LlmList = 'llmList', +} + export const useFetchLlmList = (modelType?: LlmModelType) => { const { data } = useQuery({ - queryKey: ['llmList'], + queryKey: [LLMApiAction.LlmList], initialData: {}, queryFn: async () => { const { data } = await userService.llm_list({ model_type: modelType }); diff --git a/web/src/hooks/use-user-setting-request.tsx b/web/src/hooks/use-user-setting-request.tsx new file mode 100644 index 000000000..10a63409e --- /dev/null +++ b/web/src/hooks/use-user-setting-request.tsx @@ -0,0 +1,464 @@ +import message from '@/components/ui/message'; +import { LanguageTranslationMap } from '@/constants/common'; +import { ResponseGetType } from '@/interfaces/database/base'; +import { IToken } from '@/interfaces/database/chat'; +import { ITenantInfo } from '@/interfaces/database/knowledge'; +import { ILangfuseConfig } from '@/interfaces/database/system'; +import { + ISystemStatus, + ITenant, + ITenantUser, + IUserInfo, +} from '@/interfaces/database/user-setting'; +import { ISetLangfuseConfigRequestBody } from '@/interfaces/request/system'; +import userService, { + addTenantUser, + agreeTenant, + deleteTenantUser, + listTenant, + listTenantUser, +} from '@/services/user-service'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { Modal } from 'antd'; +import DOMPurify from 'dompurify'; +import { isEmpty } from 'lodash'; +import { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { history } from 'umi'; + +export const enum UserSettingApiAction { + UserInfo = 'userInfo', + TenantInfo = 'tenantInfo', + SaveSetting = 'saveSetting', + FetchManualSystemTokenList = 'fetchManualSystemTokenList', + FetchSystemTokenList = 'fetchSystemTokenList', + RemoveSystemToken = 'removeSystemToken', + CreateSystemToken = 'createSystemToken', + ListTenantUser = 'listTenantUser', + AddTenantUser = 'addTenantUser', + DeleteTenantUser = 'deleteTenantUser', + ListTenant = 'listTenant', + AgreeTenant = 'agreeTenant', + SetLangfuseConfig = 'setLangfuseConfig', + DeleteLangfuseConfig = 'deleteLangfuseConfig', + FetchLangfuseConfig = 'fetchLangfuseConfig', +} + +export const useFetchUserInfo = (): ResponseGetType => { + const { i18n } = useTranslation(); + + const { data, isFetching: loading } = useQuery({ + queryKey: [UserSettingApiAction.UserInfo], + initialData: {}, + gcTime: 0, + queryFn: async () => { + const { data } = await userService.user_info(); + if (data.code === 0) { + i18n.changeLanguage( + LanguageTranslationMap[ + data.data.language as keyof typeof LanguageTranslationMap + ], + ); + } + return data?.data ?? {}; + }, + }); + + return { data, loading }; +}; + +export const useFetchTenantInfo = ( + showEmptyModelWarn = false, +): ResponseGetType => { + const { t } = useTranslation(); + const { data, isFetching: loading } = useQuery({ + queryKey: [UserSettingApiAction.TenantInfo], + initialData: {}, + gcTime: 0, + queryFn: async () => { + const { data: res } = await userService.get_tenant_info(); + if (res.code === 0) { + // llm_id is chat_id + // asr_id is speech2txt + const { data } = res; + if ( + showEmptyModelWarn && + (isEmpty(data.embd_id) || isEmpty(data.llm_id)) + ) { + Modal.warning({ + title: t('common.warn'), + content: ( +
+ ), + onOk() { + history.push('/user-setting/model'); + }, + }); + } + data.chat_id = data.llm_id; + data.speech2text_id = data.asr_id; + + return data; + } + + return res; + }, + }); + + return { data, loading }; +}; + +export const useSelectParserList = (): Array<{ + value: string; + label: string; +}> => { + const { data: tenantInfo } = useFetchTenantInfo(true); + + const parserList = useMemo(() => { + const parserArray: Array = tenantInfo?.parser_ids?.split(',') ?? []; + return parserArray.map((x) => { + const arr = x.split(':'); + return { value: arr[0], label: arr[1] }; + }); + }, [tenantInfo]); + + return parserList; +}; + +export const useSaveSetting = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.SaveSetting], + mutationFn: async ( + userInfo: { new_password: string } | Partial, + ) => { + const { data } = await userService.setting(userInfo); + if (data.code === 0) { + message.success(t('message.modified')); + queryClient.invalidateQueries({ queryKey: ['userInfo'] }); + } + return data?.code; + }, + }); + + return { data, loading, saveSetting: mutateAsync }; +}; + +export const useFetchSystemVersion = () => { + const [version, setVersion] = useState(''); + const [loading, setLoading] = useState(false); + + const fetchSystemVersion = useCallback(async () => { + try { + setLoading(true); + const { data } = await userService.getSystemVersion(); + if (data.code === 0) { + setVersion(data.data); + setLoading(false); + } + } catch (error) { + setLoading(false); + } + }, []); + + return { fetchSystemVersion, version, loading }; +}; + +export const useFetchSystemStatus = () => { + const [systemStatus, setSystemStatus] = useState( + {} as ISystemStatus, + ); + const [loading, setLoading] = useState(false); + + const fetchSystemStatus = useCallback(async () => { + setLoading(true); + const { data } = await userService.getSystemStatus(); + if (data.code === 0) { + setSystemStatus(data.data); + setLoading(false); + } + }, []); + + return { + systemStatus, + fetchSystemStatus, + loading, + }; +}; + +export const useFetchManualSystemTokenList = () => { + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.FetchManualSystemTokenList], + mutationFn: async () => { + const { data } = await userService.listToken(); + + return data?.data ?? []; + }, + }); + + return { data, loading, fetchSystemTokenList: mutateAsync }; +}; + +export const useFetchSystemTokenList = () => { + const { + data, + isFetching: loading, + refetch, + } = useQuery({ + queryKey: [UserSettingApiAction.FetchSystemTokenList], + initialData: [], + gcTime: 0, + queryFn: async () => { + const { data } = await userService.listToken(); + + return data?.data ?? []; + }, + }); + + return { data, loading, refetch }; +}; + +export const useRemoveSystemToken = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.RemoveSystemToken], + mutationFn: async (token: string) => { + const { data } = await userService.removeToken({}, token); + if (data.code === 0) { + message.success(t('message.deleted')); + queryClient.invalidateQueries({ + queryKey: [UserSettingApiAction.FetchSystemTokenList], + }); + } + return data?.data ?? []; + }, + }); + + return { data, loading, removeToken: mutateAsync }; +}; + +export const useCreateSystemToken = () => { + const queryClient = useQueryClient(); + + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.CreateSystemToken], + mutationFn: async (params: Record) => { + const { data } = await userService.createToken(params); + if (data.code === 0) { + queryClient.invalidateQueries({ + queryKey: [UserSettingApiAction.FetchSystemTokenList], + }); + } + return data?.data ?? []; + }, + }); + + return { data, loading, createToken: mutateAsync }; +}; + +export const useListTenantUser = () => { + const { data: tenantInfo } = useFetchTenantInfo(); + const tenantId = tenantInfo.tenant_id; + const { + data, + isFetching: loading, + refetch, + } = useQuery({ + queryKey: [UserSettingApiAction.ListTenantUser, tenantId], + initialData: [], + gcTime: 0, + enabled: !!tenantId, + queryFn: async () => { + const { data } = await listTenantUser(tenantId); + + return data?.data ?? []; + }, + }); + + return { data, loading, refetch }; +}; + +export const useAddTenantUser = () => { + const { data: tenantInfo } = useFetchTenantInfo(); + const queryClient = useQueryClient(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.AddTenantUser], + mutationFn: async (email: string) => { + const { data } = await addTenantUser(tenantInfo.tenant_id, email); + if (data.code === 0) { + queryClient.invalidateQueries({ + queryKey: [UserSettingApiAction.ListTenantUser], + }); + } + return data?.code; + }, + }); + + return { data, loading, addTenantUser: mutateAsync }; +}; + +export const useDeleteTenantUser = () => { + const { data: tenantInfo } = useFetchTenantInfo(); + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.DeleteTenantUser], + mutationFn: async ({ + userId, + tenantId, + }: { + userId: string; + tenantId?: string; + }) => { + const { data } = await deleteTenantUser({ + tenantId: tenantId ?? tenantInfo.tenant_id, + userId, + }); + if (data.code === 0) { + message.success(t('message.deleted')); + queryClient.invalidateQueries({ + queryKey: [UserSettingApiAction.ListTenantUser], + }); + queryClient.invalidateQueries({ + queryKey: [UserSettingApiAction.ListTenant], + }); + } + return data?.data ?? []; + }, + }); + + return { data, loading, deleteTenantUser: mutateAsync }; +}; + +export const useListTenant = () => { + const { data: tenantInfo } = useFetchTenantInfo(); + const tenantId = tenantInfo.tenant_id; + const { + data, + isFetching: loading, + refetch, + } = useQuery({ + queryKey: [UserSettingApiAction.ListTenant, tenantId], + initialData: [], + gcTime: 0, + enabled: !!tenantId, + queryFn: async () => { + const { data } = await listTenant(); + + return data?.data ?? []; + }, + }); + + return { data, loading, refetch }; +}; + +export const useAgreeTenant = () => { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.AgreeTenant], + mutationFn: async (tenantId: string) => { + const { data } = await agreeTenant(tenantId); + if (data.code === 0) { + message.success(t('message.operated')); + queryClient.invalidateQueries({ + queryKey: [UserSettingApiAction.ListTenant], + }); + } + return data?.data ?? []; + }, + }); + + return { data, loading, agreeTenant: mutateAsync }; +}; + +export const useSetLangfuseConfig = () => { + const { t } = useTranslation(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.SetLangfuseConfig], + mutationFn: async (params: ISetLangfuseConfigRequestBody) => { + const { data } = await userService.setLangfuseConfig(params); + if (data.code === 0) { + message.success(t('message.operated')); + } + return data?.code; + }, + }); + + return { data, loading, setLangfuseConfig: mutateAsync }; +}; + +export const useDeleteLangfuseConfig = () => { + const { t } = useTranslation(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [UserSettingApiAction.DeleteLangfuseConfig], + mutationFn: async () => { + const { data } = await userService.deleteLangfuseConfig(); + if (data.code === 0) { + message.success(t('message.deleted')); + } + return data?.code; + }, + }); + + return { data, loading, deleteLangfuseConfig: mutateAsync }; +}; + +export const useFetchLangfuseConfig = () => { + const { data, isFetching: loading } = useQuery({ + queryKey: [UserSettingApiAction.FetchLangfuseConfig], + gcTime: 0, + queryFn: async () => { + const { data } = await userService.getLangfuseConfig(); + + return data?.data; + }, + }); + + return { data, loading }; +}; diff --git a/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx b/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx index e71ad0b50..a18d945bc 100644 --- a/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx +++ b/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx @@ -2,6 +2,8 @@ import { LargeModelFormFieldWithoutFilter } from '@/components/large-model-form- import { LlmSettingSchema } from '@/components/llm-setting-items/next'; import { NextMessageInput } from '@/components/message-input/next'; import MessageItem from '@/components/message-item'; +import PdfDrawer from '@/components/pdf-drawer'; +import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Form } from '@/components/ui/form'; @@ -54,7 +56,8 @@ type ChatCardProps = { } & Pick< MultipleChatBoxProps, 'controller' | 'removeChatBox' | 'addChatBox' | 'chatBoxIds' ->; +> & + Pick, 'clickDocumentButton'>; const ChatCard = forwardRef(function ChatCard( { @@ -66,6 +69,7 @@ const ChatCard = forwardRef(function ChatCard( chatBoxIds, derivedMessages, sendLoading, + clickDocumentButton, }: ChatCardProps, ref, ) { @@ -178,6 +182,7 @@ const ChatCard = forwardRef(function ChatCard( removeMessageById={removeMessageById} regenerateMessage={regenerateMessage} sendLoading={sendLoading} + clickDocumentButton={clickDocumentButton} > ); })} @@ -211,6 +216,8 @@ export function MultipleChatBox({ const { conversationId } = useGetChatSearchParams(); const disabled = useGetSendButtonDisabled(); const sendDisabled = useSendButtonDisabled(value); + const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = + useClickDrawer(); return (
@@ -227,6 +234,7 @@ export function MultipleChatBox({ derivedMessages={messageRecord[id]} ref={setFormRef(id)} sendLoading={sendLoading} + clickDocumentButton={clickDocumentButton} > ))} @@ -246,6 +254,14 @@ export function MultipleChatBox({ onUpload={handleUploadFile} /> + {visible && ( + + )}
); } diff --git a/web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx b/web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx index de6406a67..323661776 100644 --- a/web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx +++ b/web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx @@ -1,5 +1,7 @@ import { NextMessageInput } from '@/components/message-input/next'; import MessageItem from '@/components/message-item'; +import PdfDrawer from '@/components/pdf-drawer'; +import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { MessageType } from '@/constants/chat'; import { useFetchConversation, @@ -43,6 +45,8 @@ export function SingleChatBox({ controller }: IProps) { const { data: conversation } = useFetchConversation(); const disabled = useGetSendButtonDisabled(); const sendDisabled = useSendButtonDisabled(value); + const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = + useClickDrawer(); return (
@@ -68,7 +72,7 @@ export function SingleChatBox({ controller }: IProps) { }, message, )} - // clickDocumentButton={clickDocumentButton} + clickDocumentButton={clickDocumentButton} index={i} removeMessageById={removeMessageById} regenerateMessage={regenerateMessage} @@ -94,6 +98,14 @@ export function SingleChatBox({ controller }: IProps) { onUpload={handleUploadFile} isUploading={isUploading} /> + {visible && ( + + )}
); } diff --git a/web/src/pages/next-chats/chat/index.tsx b/web/src/pages/next-chats/chat/index.tsx index 1545402d3..34be965ae 100644 --- a/web/src/pages/next-chats/chat/index.tsx +++ b/web/src/pages/next-chats/chat/index.tsx @@ -109,13 +109,12 @@ export default function Chat() { - + -
{conversation.name}
- +
{conversation.name}