mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### 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)
This commit is contained in:
@ -1,170 +0,0 @@
|
||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import {
|
||||
LanguageAbbreviation,
|
||||
LanguageAbbreviationMap,
|
||||
} from '@/constants/common';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
const FormSchema = z.object({
|
||||
visibleAvatar: z.boolean(),
|
||||
locale: z.string(),
|
||||
});
|
||||
|
||||
type IProps = IModalProps<any> & {
|
||||
token: string;
|
||||
from: SharedFrom;
|
||||
beta: string;
|
||||
isAgent: boolean;
|
||||
};
|
||||
|
||||
function EmbedDialog({
|
||||
hideModal,
|
||||
token = '',
|
||||
from,
|
||||
beta = '',
|
||||
isAgent,
|
||||
}: IProps) {
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
visibleAvatar: false,
|
||||
locale: '',
|
||||
},
|
||||
});
|
||||
|
||||
const values = useWatch({ control: form.control });
|
||||
|
||||
const languageOptions = useMemo(() => {
|
||||
return Object.values(LanguageAbbreviation).map((x) => ({
|
||||
label: LanguageAbbreviationMap[x],
|
||||
value: x,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const generateIframeSrc = useCallback(() => {
|
||||
const { visibleAvatar, locale } = values;
|
||||
let src = `${location.origin}/next-chat/share?shared_id=${token}&from=${from}&auth=${beta}`;
|
||||
if (visibleAvatar) {
|
||||
src += '&visible_avatar=1';
|
||||
}
|
||||
if (locale) {
|
||||
src += `&locale=${locale}`;
|
||||
}
|
||||
return src;
|
||||
}, [beta, from, token, values]);
|
||||
|
||||
const text = useMemo(() => {
|
||||
const iframeSrc = generateIframeSrc();
|
||||
return `
|
||||
~~~ html
|
||||
<iframe
|
||||
src="${iframeSrc}"
|
||||
style="width: 100%; height: 100%; min-height: 600px"
|
||||
frameborder="0"
|
||||
>
|
||||
</iframe>
|
||||
~~~
|
||||
`;
|
||||
}, [generateIframeSrc]);
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={hideModal}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('embedIntoSite', { keyPrefix: 'common' })}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<section className="w-full overflow-auto space-y-5 text-sm text-text-secondary">
|
||||
<Form {...form}>
|
||||
<form className="space-y-5">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="visibleAvatar"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('avatarHidden')}</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
></Switch>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="locale"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('locale')}</FormLabel>
|
||||
<FormControl>
|
||||
<SelectWithSearch
|
||||
{...field}
|
||||
options={languageOptions}
|
||||
></SelectWithSearch>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
<div>
|
||||
<span>Embed code</span>
|
||||
<HightLightMarkdown>{text}</HightLightMarkdown>
|
||||
</div>
|
||||
<div className=" font-medium mt-4 mb-1">
|
||||
{t(isAgent ? 'flow' : 'chat', { keyPrefix: 'header' })}
|
||||
<span className="ml-1 inline-block">ID</span>
|
||||
</div>
|
||||
<div className="bg-bg-card rounded-lg flex justify-between p-2">
|
||||
<span>{token} </span>
|
||||
<CopyToClipboard text={token}></CopyToClipboard>
|
||||
</div>
|
||||
<a
|
||||
className="cursor-pointer text-accent-primary inline-block"
|
||||
href={
|
||||
isAgent
|
||||
? 'https://ragflow.io/docs/dev/http_api_reference#create-session-with-agent'
|
||||
: 'https://ragflow.io/docs/dev/http_api_reference#create-session-with-chat-assistant'
|
||||
}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t('howUseId', { keyPrefix: isAgent ? 'flow' : 'chat' })}
|
||||
</a>
|
||||
</section>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(EmbedDialog);
|
||||
@ -128,7 +128,7 @@ function AgentForm({ node }: INextOperatorForm) {
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
{isSubAgent && <DescriptionField></DescriptionField>}
|
||||
<LargeModelFormField></LargeModelFormField>
|
||||
<LargeModelFormField showTTSModel></LargeModelFormField>
|
||||
{findLlmByUuid(llmId)?.model_type === LlmModelType.Image2text && (
|
||||
<QueryVariable
|
||||
name="visual_files_var"
|
||||
|
||||
65
web/src/pages/agent/hooks/use-send-shared-message.ts
Normal file
65
web/src/pages/agent/hooks/use-send-shared-message.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { IEventList } from '@/hooks/use-send-message';
|
||||
import { useSendAgentMessage } from '@/pages/agent/chat/use-send-agent-message';
|
||||
import trim from 'lodash/trim';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useSearchParams } from 'umi';
|
||||
|
||||
export const useSendButtonDisabled = (value: string) => {
|
||||
return trim(value) === '';
|
||||
};
|
||||
|
||||
export const useGetSharedChatSearchParams = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const data_prefix = 'data_';
|
||||
const data = Object.fromEntries(
|
||||
searchParams
|
||||
.entries()
|
||||
.filter(([key]) => key.startsWith(data_prefix))
|
||||
.map(([key, value]) => [key.replace(data_prefix, ''), value]),
|
||||
);
|
||||
return {
|
||||
from: searchParams.get('from') as SharedFrom,
|
||||
sharedId: searchParams.get('shared_id'),
|
||||
locale: searchParams.get('locale'),
|
||||
data: data,
|
||||
visibleAvatar: searchParams.get('visible_avatar')
|
||||
? searchParams.get('visible_avatar') !== '1'
|
||||
: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const useSendNextSharedMessage = (
|
||||
addEventList: (data: IEventList, messageId: string) => void,
|
||||
) => {
|
||||
const { from, sharedId: conversationId } = useGetSharedChatSearchParams();
|
||||
const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`;
|
||||
|
||||
const [params, setParams] = useState<any[]>([]);
|
||||
|
||||
const {
|
||||
visible: parameterDialogVisible,
|
||||
hideModal: hideParameterDialog,
|
||||
showModal: showParameterDialog,
|
||||
} = useSetModalState();
|
||||
|
||||
const ret = useSendAgentMessage(url, addEventList, params);
|
||||
|
||||
const ok = useCallback(
|
||||
(params: any[]) => {
|
||||
setParams(params);
|
||||
hideParameterDialog();
|
||||
},
|
||||
[hideParameterDialog],
|
||||
);
|
||||
|
||||
return {
|
||||
...ret,
|
||||
hasError: false,
|
||||
parameterDialogVisible,
|
||||
hideParameterDialog,
|
||||
showParameterDialog,
|
||||
ok,
|
||||
};
|
||||
};
|
||||
@ -1,18 +1,13 @@
|
||||
import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import {
|
||||
useSetModalState,
|
||||
useShowDeleteConfirm,
|
||||
useTranslate,
|
||||
} from '@/hooks/common-hooks';
|
||||
import { useShowDeleteConfirm } from '@/hooks/common-hooks';
|
||||
import {
|
||||
useCreateSystemToken,
|
||||
useFetchManualSystemTokenList,
|
||||
useFetchSystemTokenList,
|
||||
useRemoveSystemToken,
|
||||
} from '@/hooks/user-setting-hooks';
|
||||
import { IStats } from '@/interfaces/database/chat';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { message } from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useOperateApiKey = (idKey: string, dialogId?: string) => {
|
||||
@ -62,94 +57,11 @@ export const useSelectChartStatsList = (): ChartStatsType => {
|
||||
}, {} as ChartStatsType);
|
||||
};
|
||||
|
||||
export const useShowTokenEmptyError = () => {
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const showTokenEmptyError = useCallback(() => {
|
||||
message.error(t('tokenError'));
|
||||
}, [t]);
|
||||
return { showTokenEmptyError };
|
||||
};
|
||||
|
||||
export const useShowBetaEmptyError = () => {
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const showBetaEmptyError = useCallback(() => {
|
||||
message.error(t('betaError'));
|
||||
}, [t]);
|
||||
return { showBetaEmptyError };
|
||||
};
|
||||
|
||||
const getUrlWithToken = (token: string, from: string = 'chat') => {
|
||||
const { protocol, host } = window.location;
|
||||
return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`;
|
||||
};
|
||||
|
||||
export const useFetchTokenListBeforeOtherStep = () => {
|
||||
const { showTokenEmptyError } = useShowTokenEmptyError();
|
||||
const { showBetaEmptyError } = useShowBetaEmptyError();
|
||||
|
||||
const { data: tokenList, fetchSystemTokenList } =
|
||||
useFetchManualSystemTokenList();
|
||||
|
||||
let token = '',
|
||||
beta = '';
|
||||
|
||||
if (Array.isArray(tokenList) && tokenList.length > 0) {
|
||||
token = tokenList[0].token;
|
||||
beta = tokenList[0].beta;
|
||||
}
|
||||
|
||||
token =
|
||||
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
|
||||
|
||||
const handleOperate = useCallback(async () => {
|
||||
const ret = await fetchSystemTokenList();
|
||||
const list = ret;
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
if (!list[0].beta) {
|
||||
showBetaEmptyError();
|
||||
return false;
|
||||
}
|
||||
return list[0]?.token;
|
||||
} else {
|
||||
showTokenEmptyError();
|
||||
return false;
|
||||
}
|
||||
}, [fetchSystemTokenList, showBetaEmptyError, showTokenEmptyError]);
|
||||
|
||||
return {
|
||||
token,
|
||||
beta,
|
||||
handleOperate,
|
||||
};
|
||||
};
|
||||
|
||||
export const useShowEmbedModal = () => {
|
||||
const {
|
||||
visible: embedVisible,
|
||||
hideModal: hideEmbedModal,
|
||||
showModal: showEmbedModal,
|
||||
} = useSetModalState();
|
||||
|
||||
const { handleOperate, token, beta } = useFetchTokenListBeforeOtherStep();
|
||||
|
||||
const handleShowEmbedModal = useCallback(async () => {
|
||||
const succeed = await handleOperate();
|
||||
if (succeed) {
|
||||
showEmbedModal();
|
||||
}
|
||||
}, [handleOperate, showEmbedModal]);
|
||||
|
||||
return {
|
||||
showEmbedModal: handleShowEmbedModal,
|
||||
hideEmbedModal,
|
||||
embedVisible,
|
||||
embedToken: token,
|
||||
beta,
|
||||
};
|
||||
};
|
||||
|
||||
export const usePreviewChat = (idKey: string) => {
|
||||
const { handleOperate } = useFetchTokenListBeforeOtherStep();
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import EmbedDialog from '@/components/embed-dialog';
|
||||
import { useShowEmbedModal } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
@ -35,7 +37,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'umi';
|
||||
import AgentCanvas from './canvas';
|
||||
import { DropdownProvider } from './canvas/context';
|
||||
import EmbedDialog from './embed-dialog';
|
||||
import { useHandleExportOrImportJsonFile } from './hooks/use-export-json';
|
||||
import { useFetchDataOnMount } from './hooks/use-fetch-data';
|
||||
import { useGetBeginNodeDataInputs } from './hooks/use-get-begin-query';
|
||||
@ -44,7 +45,6 @@ import {
|
||||
useSaveGraphBeforeOpeningDebugDrawer,
|
||||
useWatchAgentChange,
|
||||
} from './hooks/use-save-graph';
|
||||
import { useShowEmbedModal } from './hooks/use-show-dialog';
|
||||
import { SettingDialog } from './setting-dialog';
|
||||
import { UploadAgentDialog } from './upload-agent-dialog';
|
||||
import { useAgentHistoryManager } from './use-agent-history-manager';
|
||||
|
||||
233
web/src/pages/agent/share/index.tsx
Normal file
233
web/src/pages/agent/share/index.tsx
Normal file
@ -0,0 +1,233 @@
|
||||
import { EmbedContainer } from '@/components/embed-container';
|
||||
import { FileUploadProps } from '@/components/file-upload';
|
||||
import { NextMessageInput } from '@/components/message-input/next';
|
||||
import MessageItem from '@/components/next-message-item';
|
||||
import PdfDrawer from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import {
|
||||
useFetchExternalAgentInputs,
|
||||
useUploadCanvasFileWithProgress,
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import i18n from '@/locales/config';
|
||||
import DebugContent from '@/pages/agent/debug-content';
|
||||
import { useCacheChatLog } from '@/pages/agent/hooks/use-cache-chat-log';
|
||||
import { useAwaitCompentData } from '@/pages/agent/hooks/use-chat-logic';
|
||||
import { IInputs } from '@/pages/agent/interface';
|
||||
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { forwardRef, useCallback, useState } from 'react';
|
||||
import {
|
||||
useGetSharedChatSearchParams,
|
||||
useSendNextSharedMessage,
|
||||
} from '../hooks/use-send-shared-message';
|
||||
import { ParameterDialog } from './parameter-dialog';
|
||||
|
||||
const ChatContainer = () => {
|
||||
const {
|
||||
sharedId: conversationId,
|
||||
locale,
|
||||
visibleAvatar,
|
||||
} = useGetSharedChatSearchParams();
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
|
||||
const { uploadCanvasFile, loading } =
|
||||
useUploadCanvasFileWithProgress(conversationId);
|
||||
const {
|
||||
addEventList,
|
||||
setCurrentMessageId,
|
||||
currentEventListWithoutMessageById,
|
||||
clearEventList,
|
||||
} = useCacheChatLog();
|
||||
const {
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
findReferenceByMessageId,
|
||||
appendUploadResponseList,
|
||||
parameterDialogVisible,
|
||||
showParameterDialog,
|
||||
sendFormMessage,
|
||||
addNewestOneAnswer,
|
||||
ok,
|
||||
resetSession,
|
||||
} = useSendNextSharedMessage(addEventList);
|
||||
const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
|
||||
derivedMessages,
|
||||
sendFormMessage,
|
||||
canvasId: conversationId as string,
|
||||
});
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
const { data: inputsData } = useFetchExternalAgentInputs();
|
||||
const [agentInfo, setAgentInfo] = useState<IInputs>({
|
||||
avatar: '',
|
||||
title: '',
|
||||
inputs: {},
|
||||
prologue: '',
|
||||
});
|
||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||
useCallback(
|
||||
async (files, options) => {
|
||||
const ret = await uploadCanvasFile({ files, options });
|
||||
appendUploadResponseList(ret.data, files);
|
||||
},
|
||||
[appendUploadResponseList, uploadCanvasFile],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (locale && i18n.language !== locale) {
|
||||
i18n.changeLanguage(locale);
|
||||
}
|
||||
}, [locale, visibleAvatar]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const { avatar, title, inputs } = inputsData;
|
||||
setAgentInfo({
|
||||
avatar,
|
||||
title,
|
||||
inputs: inputs,
|
||||
prologue: '',
|
||||
});
|
||||
}, [inputsData, setAgentInfo]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inputsData.prologue) {
|
||||
addNewestOneAnswer({
|
||||
answer: inputsData.prologue,
|
||||
});
|
||||
}
|
||||
}, [inputsData.prologue, addNewestOneAnswer]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) {
|
||||
showParameterDialog();
|
||||
}
|
||||
}, [inputsData, showParameterDialog]);
|
||||
|
||||
const handleInputsModalOk = (params: any[]) => {
|
||||
ok(params);
|
||||
};
|
||||
const handleReset = () => {
|
||||
resetSession();
|
||||
clearEventList();
|
||||
};
|
||||
if (!conversationId) {
|
||||
return <div>empty</div>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<EmbedContainer
|
||||
title={agentInfo.title}
|
||||
avatar={agentInfo.avatar}
|
||||
handleReset={handleReset}
|
||||
>
|
||||
<div className="flex flex-1 flex-col p-2.5 h-[90vh] m-3">
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-1 flex-col overflow-auto scrollbar-auto m-auto w-5/6',
|
||||
)}
|
||||
ref={messageContainerRef}
|
||||
>
|
||||
<div>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
visibleAvatar={visibleAvatar}
|
||||
conversationId={conversationId}
|
||||
currentEventListWithoutMessageById={
|
||||
currentEventListWithoutMessageById
|
||||
}
|
||||
setCurrentMessageId={setCurrentMessageId}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
item={message}
|
||||
nickname="You"
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages?.length - 1 === i
|
||||
}
|
||||
isShare={true}
|
||||
avatarDialog={agentInfo.avatar}
|
||||
agentName={agentInfo.title}
|
||||
index={i}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
showLikeButton={false}
|
||||
showLoudspeaker={false}
|
||||
showLog={false}
|
||||
sendLoading={sendLoading}
|
||||
>
|
||||
{message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 === i && (
|
||||
<DebugContent
|
||||
parameters={buildInputList(message)}
|
||||
message={message}
|
||||
ok={handleOk(message)}
|
||||
isNext={false}
|
||||
btnText={'Submit'}
|
||||
></DebugContent>
|
||||
)}
|
||||
{message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 !== i && (
|
||||
<div>
|
||||
<div>{message?.data?.tips}</div>
|
||||
|
||||
<div>
|
||||
{buildInputList(message)?.map((item) => item.value)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</MessageItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div ref={scrollRef} />
|
||||
</div>
|
||||
<div className="flex w-full justify-center mb-8">
|
||||
<div className="w-5/6">
|
||||
<NextMessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError || isWaitting}
|
||||
sendDisabled={sendDisabled || isWaitting}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
onUpload={handleUploadFile}
|
||||
isUploading={loading || isWaitting}
|
||||
></NextMessageInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EmbedContainer>
|
||||
{visible && (
|
||||
<PdfDrawer
|
||||
visible={visible}
|
||||
hideModal={hideModal}
|
||||
documentId={documentId}
|
||||
chunk={selectedChunk}
|
||||
></PdfDrawer>
|
||||
)}
|
||||
{parameterDialogVisible && (
|
||||
<ParameterDialog
|
||||
// hideModal={hideParameterDialog}
|
||||
ok={handleInputsModalOk}
|
||||
data={agentInfo.inputs}
|
||||
></ParameterDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(ChatContainer);
|
||||
@ -1,3 +1,5 @@
|
||||
import EmbedDialog from '@/components/embed-dialog';
|
||||
import { useShowEmbedModal } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
@ -9,6 +11,7 @@ import {
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import {
|
||||
@ -20,6 +23,7 @@ import { cn } from '@/lib/utils';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { ArrowUpRight, LogOut, Send } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'umi';
|
||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||
import { ChatSettings } from './app-settings/chat-settings';
|
||||
import { MultipleChatBox } from './chat-box/multiple-chat-box';
|
||||
@ -29,6 +33,7 @@ import { useAddChatBox } from './use-add-box';
|
||||
import { useSwitchDebugMode } from './use-switch-debug-mode';
|
||||
|
||||
export default function Chat() {
|
||||
const { id } = useParams();
|
||||
const { navigateToChatList } = useNavigatePage();
|
||||
const { data } = useFetchDialog();
|
||||
const { t } = useTranslation();
|
||||
@ -46,6 +51,9 @@ export default function Chat() {
|
||||
hasThreeChatBox,
|
||||
} = useAddChatBox();
|
||||
|
||||
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
||||
useShowEmbedModal();
|
||||
|
||||
const { conversationId, isNew } = useGetChatSearchParams();
|
||||
|
||||
const { isDebugMode, switchDebugMode } = useSwitchDebugMode();
|
||||
@ -87,7 +95,7 @@ export default function Chat() {
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<Button>
|
||||
<Button onClick={showEmbedModal}>
|
||||
<Send />
|
||||
{t('common.embedIntoSite')}
|
||||
</Button>
|
||||
@ -133,6 +141,16 @@ export default function Chat() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
{embedVisible && (
|
||||
<EmbedDialog
|
||||
visible={embedVisible}
|
||||
hideModal={hideEmbedModal}
|
||||
token={id!}
|
||||
from={SharedFrom.Chat}
|
||||
beta={beta}
|
||||
isAgent={false}
|
||||
></EmbedDialog>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +1,20 @@
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { IEventList } from '@/hooks/use-send-message';
|
||||
import { useSendAgentMessage } from '@/pages/agent/chat/use-send-agent-message';
|
||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
||||
import { useCreateNextSharedConversation } from '@/hooks/chat-hooks';
|
||||
import {
|
||||
useHandleMessageInputChange,
|
||||
useSelectDerivedMessages,
|
||||
useSendMessageWithSse,
|
||||
} from '@/hooks/logic-hooks';
|
||||
import { Message } from '@/interfaces/database/chat';
|
||||
import { message } from 'antd';
|
||||
import { get } from 'lodash';
|
||||
import trim from 'lodash/trim';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'umi';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
const isCompletionError = (res: any) =>
|
||||
res && (res?.response.status !== 200 || res?.data?.code !== 0);
|
||||
|
||||
export const useSendButtonDisabled = (value: string) => {
|
||||
return trim(value) === '';
|
||||
@ -30,36 +40,114 @@ export const useGetSharedChatSearchParams = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useSendNextSharedMessage = (
|
||||
addEventList: (data: IEventList, messageId: string) => void,
|
||||
) => {
|
||||
const { from, sharedId: conversationId } = useGetSharedChatSearchParams();
|
||||
const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`;
|
||||
|
||||
const [params, setParams] = useState<any[]>([]);
|
||||
|
||||
export const useSendSharedMessage = () => {
|
||||
const {
|
||||
visible: parameterDialogVisible,
|
||||
hideModal: hideParameterDialog,
|
||||
showModal: showParameterDialog,
|
||||
} = useSetModalState();
|
||||
from,
|
||||
sharedId: conversationId,
|
||||
data: data,
|
||||
} = useGetSharedChatSearchParams();
|
||||
const { createSharedConversation: setConversation } =
|
||||
useCreateNextSharedConversation();
|
||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||
const { send, answer, done, stopOutputMessage } = useSendMessageWithSse(
|
||||
`/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`,
|
||||
);
|
||||
const {
|
||||
derivedMessages,
|
||||
removeLatestMessage,
|
||||
addNewestAnswer,
|
||||
addNewestQuestion,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeAllMessages,
|
||||
} = useSelectDerivedMessages();
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
const ret = useSendAgentMessage(url, addEventList, params);
|
||||
const sendMessage = useCallback(
|
||||
async (message: Message, id?: string) => {
|
||||
const res = await send({
|
||||
conversation_id: id ?? conversationId,
|
||||
quote: true,
|
||||
question: message.content,
|
||||
session_id: get(derivedMessages, '0.session_id'),
|
||||
});
|
||||
|
||||
const ok = useCallback(
|
||||
(params: any[]) => {
|
||||
setParams(params);
|
||||
hideParameterDialog();
|
||||
if (isCompletionError(res)) {
|
||||
// cancel loading
|
||||
setValue(message.content);
|
||||
removeLatestMessage();
|
||||
}
|
||||
},
|
||||
[hideParameterDialog],
|
||||
[send, conversationId, derivedMessages, setValue, removeLatestMessage],
|
||||
);
|
||||
|
||||
const handleSendMessage = useCallback(
|
||||
async (message: Message) => {
|
||||
if (conversationId !== '') {
|
||||
sendMessage(message);
|
||||
} else {
|
||||
const data = await setConversation('user id');
|
||||
if (data.code === 0) {
|
||||
const id = data.data.id;
|
||||
sendMessage(message, id);
|
||||
}
|
||||
}
|
||||
},
|
||||
[conversationId, setConversation, sendMessage],
|
||||
);
|
||||
|
||||
const fetchSessionId = useCallback(async () => {
|
||||
const payload = { question: '' };
|
||||
const ret = await send({ ...payload, ...data });
|
||||
if (isCompletionError(ret)) {
|
||||
message.error(ret?.data.message);
|
||||
setHasError(true);
|
||||
}
|
||||
}, [send]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSessionId();
|
||||
}, [fetchSessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (answer.answer) {
|
||||
addNewestAnswer(answer);
|
||||
}
|
||||
}, [answer, addNewestAnswer]);
|
||||
|
||||
const handlePressEnter = useCallback(
|
||||
(documentIds: string[]) => {
|
||||
if (trim(value) === '') return;
|
||||
const id = uuid();
|
||||
if (done) {
|
||||
setValue('');
|
||||
addNewestQuestion({
|
||||
content: value,
|
||||
doc_ids: documentIds,
|
||||
id,
|
||||
role: MessageType.User,
|
||||
});
|
||||
handleSendMessage({
|
||||
content: value.trim(),
|
||||
id,
|
||||
role: MessageType.User,
|
||||
});
|
||||
}
|
||||
},
|
||||
[addNewestQuestion, done, handleSendMessage, setValue, value],
|
||||
);
|
||||
|
||||
return {
|
||||
...ret,
|
||||
hasError: false,
|
||||
parameterDialogVisible,
|
||||
hideParameterDialog,
|
||||
showParameterDialog,
|
||||
ok,
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading: !done,
|
||||
loading: false,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeAllMessages,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,166 +1,75 @@
|
||||
import { FileUploadProps } from '@/components/file-upload';
|
||||
import { EmbedContainer } from '@/components/embed-container';
|
||||
import { NextMessageInput } from '@/components/message-input/next';
|
||||
import MessageItem from '@/components/next-message-item';
|
||||
import MessageItem from '@/components/message-item';
|
||||
import PdfDrawer from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useFetchAppConf } from '@/hooks/logic-hooks';
|
||||
import {
|
||||
useFetchExternalAgentInputs,
|
||||
useUploadCanvasFileWithProgress,
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
||||
import { useFetchNextConversationSSE } from '@/hooks/chat-hooks';
|
||||
import { useFetchFlowSSE } from '@/hooks/flow-hooks';
|
||||
import { useFetchExternalChatInfo } from '@/hooks/use-chat-request';
|
||||
import i18n from '@/locales/config';
|
||||
import DebugContent from '@/pages/agent/debug-content';
|
||||
import { useCacheChatLog } from '@/pages/agent/hooks/use-cache-chat-log';
|
||||
import { useAwaitCompentData } from '@/pages/agent/hooks/use-chat-logic';
|
||||
import { IInputs } from '@/pages/agent/interface';
|
||||
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { RefreshCcw } from 'lucide-react';
|
||||
import React, { forwardRef, useCallback, useState } from 'react';
|
||||
import React, { forwardRef, useMemo } from 'react';
|
||||
import {
|
||||
useGetSharedChatSearchParams,
|
||||
useSendNextSharedMessage,
|
||||
useSendSharedMessage,
|
||||
} from '../hooks/use-send-shared-message';
|
||||
import { ParameterDialog } from './parameter-dialog';
|
||||
import { buildMessageItemReference } from '../utils';
|
||||
|
||||
const ChatContainer = () => {
|
||||
const {
|
||||
sharedId: conversationId,
|
||||
from,
|
||||
locale,
|
||||
visibleAvatar,
|
||||
} = useGetSharedChatSearchParams();
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
|
||||
const { uploadCanvasFile, loading } =
|
||||
useUploadCanvasFileWithProgress(conversationId);
|
||||
const {
|
||||
addEventList,
|
||||
setCurrentMessageId,
|
||||
currentEventListWithoutMessageById,
|
||||
clearEventList,
|
||||
} = useCacheChatLog();
|
||||
const {
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
findReferenceByMessageId,
|
||||
appendUploadResponseList,
|
||||
parameterDialogVisible,
|
||||
showParameterDialog,
|
||||
sendFormMessage,
|
||||
addNewestOneAnswer,
|
||||
ok,
|
||||
resetSession,
|
||||
} = useSendNextSharedMessage(addEventList);
|
||||
const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
|
||||
derivedMessages,
|
||||
sendFormMessage,
|
||||
canvasId: conversationId as string,
|
||||
});
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeAllMessages,
|
||||
} = useSendSharedMessage();
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
const appConf = useFetchAppConf();
|
||||
const { data: inputsData } = useFetchExternalAgentInputs();
|
||||
const [agentInfo, setAgentInfo] = useState<IInputs>({
|
||||
avatar: '',
|
||||
title: '',
|
||||
inputs: {},
|
||||
prologue: '',
|
||||
});
|
||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||
useCallback(
|
||||
async (files, options) => {
|
||||
const ret = await uploadCanvasFile({ files, options });
|
||||
appendUploadResponseList(ret.data, files);
|
||||
},
|
||||
[appendUploadResponseList, uploadCanvasFile],
|
||||
);
|
||||
const { data: chatInfo } = useFetchExternalChatInfo();
|
||||
|
||||
const useFetchAvatar = useMemo(() => {
|
||||
return from === SharedFrom.Agent
|
||||
? useFetchFlowSSE
|
||||
: useFetchNextConversationSSE;
|
||||
}, [from]);
|
||||
React.useEffect(() => {
|
||||
if (locale && i18n.language !== locale) {
|
||||
i18n.changeLanguage(locale);
|
||||
}
|
||||
}, [locale, visibleAvatar]);
|
||||
const { data: avatarData } = useFetchAvatar();
|
||||
|
||||
React.useEffect(() => {
|
||||
const { avatar, title, inputs } = inputsData;
|
||||
setAgentInfo({
|
||||
avatar,
|
||||
title,
|
||||
inputs: inputs,
|
||||
prologue: '',
|
||||
});
|
||||
}, [inputsData, setAgentInfo]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inputsData.prologue) {
|
||||
addNewestOneAnswer({
|
||||
answer: inputsData.prologue,
|
||||
});
|
||||
}
|
||||
}, [inputsData.prologue, addNewestOneAnswer]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) {
|
||||
showParameterDialog();
|
||||
}
|
||||
}, [inputsData, showParameterDialog]);
|
||||
|
||||
const handleInputsModalOk = (params: any[]) => {
|
||||
ok(params);
|
||||
};
|
||||
const handleReset = () => {
|
||||
resetSession();
|
||||
clearEventList();
|
||||
};
|
||||
if (!conversationId) {
|
||||
return <div>empty</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="h-[100vh] flex justify-center items-center">
|
||||
<div className="w-40 flex gap-2 absolute left-3 top-12 items-center">
|
||||
<img src="/logo.svg" alt="" />
|
||||
<span className="text-2xl font-bold">{appConf.appName}</span>
|
||||
</div>
|
||||
<div className=" w-[80vw] border rounded-lg">
|
||||
<div className="flex justify-between items-center border-b p-3">
|
||||
<div className="flex gap-2 items-center">
|
||||
<RAGFlowAvatar
|
||||
avatar={agentInfo.avatar}
|
||||
name={agentInfo.title}
|
||||
isPerson
|
||||
/>
|
||||
<div className="text-xl text-foreground">{agentInfo.title}</div>
|
||||
</div>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
className="text-sm text-foreground cursor-pointer"
|
||||
onClick={() => {
|
||||
handleReset();
|
||||
}}
|
||||
>
|
||||
<div className="flex gap-1 items-center">
|
||||
<RefreshCcw size={14} />
|
||||
<span className="text-lg ">Reset</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<>
|
||||
<EmbedContainer
|
||||
title={chatInfo.title}
|
||||
avatar={chatInfo.avatar}
|
||||
handleReset={removeAllMessages}
|
||||
>
|
||||
<div className="flex flex-1 flex-col p-2.5 h-[90vh] m-3">
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-1 flex-col overflow-auto scrollbar-auto m-auto w-5/6',
|
||||
)}
|
||||
className={
|
||||
'flex flex-1 flex-col overflow-auto scrollbar-auto m-auto w-5/6'
|
||||
}
|
||||
ref={messageContainerRef}
|
||||
>
|
||||
<div>
|
||||
@ -168,51 +77,27 @@ const ChatContainer = () => {
|
||||
return (
|
||||
<MessageItem
|
||||
visibleAvatar={visibleAvatar}
|
||||
conversationId={conversationId}
|
||||
currentEventListWithoutMessageById={
|
||||
currentEventListWithoutMessageById
|
||||
}
|
||||
setCurrentMessageId={setCurrentMessageId}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
avatarDialog={avatarData?.avatar}
|
||||
item={message}
|
||||
nickname="You"
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
reference={buildMessageItemReference(
|
||||
{
|
||||
message: derivedMessages,
|
||||
reference: [],
|
||||
},
|
||||
message,
|
||||
)}
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages?.length - 1 === i
|
||||
}
|
||||
isShare={true}
|
||||
avatarDialog={agentInfo.avatar}
|
||||
agentName={agentInfo.title}
|
||||
index={i}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
showLikeButton={false}
|
||||
showLoudspeaker={false}
|
||||
showLog={false}
|
||||
sendLoading={sendLoading}
|
||||
>
|
||||
{message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 === i && (
|
||||
<DebugContent
|
||||
parameters={buildInputList(message)}
|
||||
message={message}
|
||||
ok={handleOk(message)}
|
||||
isNext={false}
|
||||
btnText={'Submit'}
|
||||
></DebugContent>
|
||||
)}
|
||||
{message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 !== i && (
|
||||
<div>
|
||||
<div>{message?.data?.tips}</div>
|
||||
|
||||
<div>
|
||||
{buildInputList(message)?.map((item) => item.value)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</MessageItem>
|
||||
></MessageItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@ -223,20 +108,20 @@ const ChatContainer = () => {
|
||||
<NextMessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError || isWaitting}
|
||||
sendDisabled={sendDisabled || isWaitting}
|
||||
disabled={hasError}
|
||||
sendDisabled={sendDisabled}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
uploadMethod="external_upload_and_parse"
|
||||
showUploadIcon={false}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
onUpload={handleUploadFile}
|
||||
isUploading={loading || isWaitting}
|
||||
></NextMessageInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EmbedContainer>
|
||||
{visible && (
|
||||
<PdfDrawer
|
||||
visible={visible}
|
||||
@ -245,14 +130,7 @@ const ChatContainer = () => {
|
||||
chunk={selectedChunk}
|
||||
></PdfDrawer>
|
||||
)}
|
||||
{parameterDialogVisible && (
|
||||
<ParameterDialog
|
||||
// hideModal={hideParameterDialog}
|
||||
ok={handleInputsModalOk}
|
||||
data={agentInfo.inputs}
|
||||
></ParameterDialog>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||
import { Modal } from '@/components/ui/modal/modal';
|
||||
import { RAGFlowSelect } from '@/components/ui/select';
|
||||
@ -9,7 +10,6 @@ import {
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { message } from 'antd';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFetchTokenListBeforeOtherStep } from '../agent/hooks/use-show-dialog';
|
||||
|
||||
type IEmbedAppModalProps = {
|
||||
open: any;
|
||||
|
||||
Reference in New Issue
Block a user