mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Share agent dialog box externally #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
File diff suppressed because one or more lines are too long
@ -27,6 +27,7 @@ interface IProps {
|
||||
showLikeButton: boolean;
|
||||
audioBinary?: string;
|
||||
showLoudspeaker?: boolean;
|
||||
showLog?: boolean;
|
||||
}
|
||||
|
||||
export const AssistantGroupButton = ({
|
||||
@ -36,6 +37,7 @@ export const AssistantGroupButton = ({
|
||||
audioBinary,
|
||||
showLikeButton,
|
||||
showLoudspeaker = true,
|
||||
showLog = true,
|
||||
}: IProps) => {
|
||||
const { visible, hideModal, showModal, onFeedbackOk, loading } =
|
||||
useSendFeedback(messageId);
|
||||
@ -91,9 +93,11 @@ export const AssistantGroupButton = ({
|
||||
<PromptIcon style={{ fontSize: '16px' }} />
|
||||
</Radio.Button>
|
||||
)}
|
||||
<ToggleGroupItem value="f" onClick={handleShowLogSheet}>
|
||||
<NotebookText className="size-4" />
|
||||
</ToggleGroupItem>
|
||||
{showLog && (
|
||||
<ToggleGroupItem value="f" onClick={handleShowLogSheet}>
|
||||
<NotebookText className="size-4" />
|
||||
</ToggleGroupItem>
|
||||
)}
|
||||
</ToggleGroup>
|
||||
{visible && (
|
||||
<FeedbackModal
|
||||
|
||||
@ -49,6 +49,7 @@ interface IProps
|
||||
index: number;
|
||||
showLikeButton?: boolean;
|
||||
showLoudspeaker?: boolean;
|
||||
showLog?: boolean;
|
||||
}
|
||||
|
||||
function MessageItem({
|
||||
@ -65,6 +66,7 @@ function MessageItem({
|
||||
showLoudspeaker = true,
|
||||
visibleAvatar = true,
|
||||
children,
|
||||
showLog,
|
||||
}: IProps) {
|
||||
const { theme } = useTheme();
|
||||
const isAssistant = item.role === MessageType.Assistant;
|
||||
@ -141,6 +143,7 @@ function MessageItem({
|
||||
showLikeButton={showLikeButton}
|
||||
audioBinary={item.audio_binary}
|
||||
showLoudspeaker={showLoudspeaker}
|
||||
showLog={showLog}
|
||||
></AssistantGroupButton>
|
||||
) : (
|
||||
<UserGroupButton
|
||||
|
||||
@ -7,6 +7,7 @@ export enum UserSettingRouteKey {
|
||||
System = 'system',
|
||||
Api = 'api',
|
||||
Team = 'team',
|
||||
MCP = 'mcp',
|
||||
Logout = 'logout',
|
||||
}
|
||||
|
||||
|
||||
@ -39,10 +39,14 @@ export const useNavigatePage = () => {
|
||||
navigate(Routes.Chat);
|
||||
}, [navigate]);
|
||||
|
||||
const navigateToAgentList = useCallback(() => {
|
||||
const navigateToAgents = useCallback(() => {
|
||||
navigate(Routes.Agents);
|
||||
}, [navigate]);
|
||||
|
||||
const navigateToAgentList = useCallback(() => {
|
||||
navigate(Routes.AgentList);
|
||||
}, [navigate]);
|
||||
|
||||
const navigateToAgent = useCallback(
|
||||
(id: string) => () => {
|
||||
navigate(`${Routes.Agent}/${id}`);
|
||||
@ -114,11 +118,12 @@ export const useNavigatePage = () => {
|
||||
navigateToChunkParsedResult,
|
||||
getQueryString,
|
||||
navigateToChunk,
|
||||
navigateToAgentList,
|
||||
navigateToAgents,
|
||||
navigateToAgent,
|
||||
navigateToAgentTemplates,
|
||||
navigateToSearchList,
|
||||
navigateToSearch,
|
||||
navigateToFiles,
|
||||
navigateToAgentList,
|
||||
};
|
||||
};
|
||||
|
||||
@ -35,6 +35,7 @@ export const enum AgentApiAction {
|
||||
FetchInputForm = 'fetchInputForm',
|
||||
FetchVersionList = 'fetchVersionList',
|
||||
FetchVersion = 'fetchVersion',
|
||||
FetchAgentAvatar = 'fetchAgentAvatar',
|
||||
}
|
||||
|
||||
export const EmptyDsl = {
|
||||
@ -444,3 +445,32 @@ export const useFetchVersion = (
|
||||
|
||||
return { data, loading };
|
||||
};
|
||||
|
||||
export const useFetchAgentAvatar = (): {
|
||||
data: IFlow;
|
||||
loading: boolean;
|
||||
refetch: () => void;
|
||||
} => {
|
||||
const { sharedId } = useGetSharedChatSearchParams();
|
||||
|
||||
const {
|
||||
data,
|
||||
isFetching: loading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: [AgentApiAction.FetchAgentAvatar],
|
||||
initialData: {} as IFlow,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
if (!sharedId) return {};
|
||||
const { data } = await agentService.fetchAgentAvatar(sharedId);
|
||||
|
||||
return data?.data ?? {};
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, refetch };
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ const RagHeader = () => {
|
||||
{ path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon },
|
||||
{ path: '/chat', name: t('chat'), icon: MessageOutlined },
|
||||
{ path: '/search', name: t('search'), icon: SearchOutlined },
|
||||
{ path: '/flow', name: t('flow'), icon: GraphIcon },
|
||||
{ path: '/agent-list', name: t('flow'), icon: GraphIcon },
|
||||
{ path: '/file', name: t('fileManager'), icon: FileIcon },
|
||||
],
|
||||
[t],
|
||||
|
||||
@ -732,6 +732,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
||||
view: 'View',
|
||||
modelsToBeAddedTooltip:
|
||||
'If your model provider is not listed but claims to be "OpenAI-compatible", select the OpenAI-API-compatible card to add the relevant model(s). ',
|
||||
mcp: 'MCP',
|
||||
},
|
||||
message: {
|
||||
registered: 'Registered!',
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useGetFileIcon } from '@/pages/chat/hooks';
|
||||
import { Spin } from 'antd';
|
||||
|
||||
import { useSendNextMessage } from './hooks';
|
||||
import { useSendAgentMessage } from './use-send-agent-message';
|
||||
|
||||
import MessageInput from '@/components/message-input';
|
||||
import MessageItem from '@/components/next-message-item';
|
||||
@ -25,13 +24,12 @@ const AgentChatBox = () => {
|
||||
handleInputChange,
|
||||
handlePressEnter,
|
||||
value,
|
||||
loading,
|
||||
ref,
|
||||
derivedMessages,
|
||||
stopOutputMessage,
|
||||
sendFormMessage,
|
||||
findReferenceByMessageId,
|
||||
} = useSendNextMessage();
|
||||
} = useSendAgentMessage();
|
||||
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
@ -73,36 +71,36 @@ const AgentChatBox = () => {
|
||||
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div>
|
||||
<Spin spinning={loading}>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages.length - 1 === i
|
||||
}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
nickname={userInfo.nickname}
|
||||
avatar={userInfo.avatar}
|
||||
avatarDialog={canvasInfo.avatar}
|
||||
item={message}
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
index={i}
|
||||
showLikeButton={false}
|
||||
sendLoading={sendLoading}
|
||||
>
|
||||
<DebugContent
|
||||
parameters={buildInputList(message)}
|
||||
ok={handleOk(message)}
|
||||
isNext={false}
|
||||
btnText={'Submit'}
|
||||
></DebugContent>
|
||||
</MessageItem>
|
||||
);
|
||||
})}
|
||||
</Spin>
|
||||
{/* <Spin spinning={sendLoading}> */}
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages.length - 1 === i
|
||||
}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
nickname={userInfo.nickname}
|
||||
avatar={userInfo.avatar}
|
||||
avatarDialog={canvasInfo.avatar}
|
||||
item={message}
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
index={i}
|
||||
showLikeButton={false}
|
||||
sendLoading={sendLoading}
|
||||
>
|
||||
<DebugContent
|
||||
parameters={buildInputList(message)}
|
||||
ok={handleOk(message)}
|
||||
isNext={false}
|
||||
btnText={'Submit'}
|
||||
></DebugContent>
|
||||
</MessageItem>
|
||||
);
|
||||
})}
|
||||
{/* </Spin> */}
|
||||
</div>
|
||||
<div ref={ref} />
|
||||
</div>
|
||||
|
||||
@ -4,7 +4,6 @@ import {
|
||||
useHandleMessageInputChange,
|
||||
useSelectDerivedMessages,
|
||||
} from '@/hooks/logic-hooks';
|
||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||
import {
|
||||
IEventList,
|
||||
IInputEvent,
|
||||
@ -30,37 +29,7 @@ import { BeginQuery } from '../interface';
|
||||
import useGraphStore from '../store';
|
||||
import { receiveMessageError } from '../utils';
|
||||
|
||||
export const useSelectNextMessages = () => {
|
||||
const { data: flowDetail, loading } = useFetchAgent();
|
||||
const reference = flowDetail.dsl.retrieval;
|
||||
const {
|
||||
derivedMessages,
|
||||
ref,
|
||||
addNewestQuestion,
|
||||
addNewestAnswer,
|
||||
removeLatestMessage,
|
||||
removeMessageById,
|
||||
removeMessagesAfterCurrentMessage,
|
||||
addNewestOneQuestion,
|
||||
addNewestOneAnswer,
|
||||
} = useSelectDerivedMessages();
|
||||
|
||||
return {
|
||||
reference,
|
||||
loading,
|
||||
derivedMessages,
|
||||
ref,
|
||||
addNewestQuestion,
|
||||
addNewestAnswer,
|
||||
removeLatestMessage,
|
||||
removeMessageById,
|
||||
addNewestOneQuestion,
|
||||
addNewestOneAnswer,
|
||||
removeMessagesAfterCurrentMessage,
|
||||
};
|
||||
};
|
||||
|
||||
function findMessageFromList(eventList: IEventList) {
|
||||
export function findMessageFromList(eventList: IEventList) {
|
||||
const messageEventList = eventList.filter(
|
||||
(x) => x.event === MessageEventType.Message,
|
||||
) as IMessageEvent[];
|
||||
@ -101,7 +70,7 @@ function findMessageFromList(eventList: IEventList) {
|
||||
};
|
||||
}
|
||||
|
||||
function findInputFromList(eventList: IEventList) {
|
||||
export function findInputFromList(eventList: IEventList) {
|
||||
const inputEvent = eventList.find(
|
||||
(x) => x.event === MessageEventType.UserInputs,
|
||||
) as IInputEvent;
|
||||
@ -120,7 +89,7 @@ export function getLatestError(eventList: IEventList) {
|
||||
return get(eventList.at(-1), 'data.outputs._ERROR');
|
||||
}
|
||||
|
||||
const useGetBeginNodePrologue = () => {
|
||||
export const useGetBeginNodePrologue = () => {
|
||||
const getNode = useGraphStore((state) => state.getNode);
|
||||
|
||||
return useMemo(() => {
|
||||
@ -131,31 +100,61 @@ const useGetBeginNodePrologue = () => {
|
||||
}, [getNode]);
|
||||
};
|
||||
|
||||
export const useSendNextMessage = () => {
|
||||
export function useFindMessageReference(answerList: IEventList) {
|
||||
const [messageEndEventList, setMessageEndEventList] = useState<
|
||||
IMessageEndEvent[]
|
||||
>([]);
|
||||
|
||||
const findReferenceByMessageId = useCallback(
|
||||
(messageId: string) => {
|
||||
const event = messageEndEventList.find(
|
||||
(item) => item.message_id === messageId,
|
||||
);
|
||||
if (event) {
|
||||
return (event?.data as IMessageEndData)?.reference;
|
||||
}
|
||||
},
|
||||
[messageEndEventList],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const messageEndEvent = answerList.find(
|
||||
(x) => x.event === MessageEventType.MessageEnd,
|
||||
);
|
||||
if (messageEndEvent) {
|
||||
setMessageEndEventList((list) => {
|
||||
const nextList = [...list];
|
||||
if (
|
||||
nextList.every((x) => x.message_id !== messageEndEvent.message_id)
|
||||
) {
|
||||
nextList.push(messageEndEvent as IMessageEndEvent);
|
||||
}
|
||||
return nextList;
|
||||
});
|
||||
}
|
||||
}, [answerList]);
|
||||
|
||||
return { findReferenceByMessageId };
|
||||
}
|
||||
|
||||
export const useSendAgentMessage = (url?: string) => {
|
||||
const { id: agentId } = useParams();
|
||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||
const inputs = useSelectBeginNodeDataInputs();
|
||||
const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE(
|
||||
url || api.runCanvas,
|
||||
);
|
||||
const { findReferenceByMessageId } = useFindMessageReference(answerList);
|
||||
const prologue = useGetBeginNodePrologue();
|
||||
const {
|
||||
reference,
|
||||
loading,
|
||||
derivedMessages,
|
||||
ref,
|
||||
removeLatestMessage,
|
||||
removeMessageById,
|
||||
addNewestOneQuestion,
|
||||
addNewestOneAnswer,
|
||||
} = useSelectNextMessages();
|
||||
const { id: agentId } = useParams();
|
||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||
const { refetch } = useFetchAgent();
|
||||
} = useSelectDerivedMessages();
|
||||
const { addEventList } = useContext(AgentChatLogContext);
|
||||
const inputs = useSelectBeginNodeDataInputs();
|
||||
const [messageEndEventList, setMessageEndEventList] = useState<
|
||||
IMessageEndEvent[]
|
||||
>([]);
|
||||
|
||||
const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE(
|
||||
api.runCanvas,
|
||||
);
|
||||
|
||||
const prologue = useGetBeginNodePrologue();
|
||||
|
||||
const sendMessage = useCallback(
|
||||
async ({ message }: { message: Message; messages?: Message[] }) => {
|
||||
@ -182,62 +181,12 @@ export const useSendNextMessage = () => {
|
||||
setValue(message.content);
|
||||
removeLatestMessage();
|
||||
} else {
|
||||
refetch(); // pull the message list after sending the message successfully
|
||||
// refetch(); // pull the message list after sending the message successfully
|
||||
}
|
||||
},
|
||||
[agentId, send, inputs, setValue, removeLatestMessage, refetch],
|
||||
[agentId, send, inputs, setValue, removeLatestMessage],
|
||||
);
|
||||
|
||||
const handleSendMessage = useCallback(
|
||||
async (message: Message) => {
|
||||
sendMessage({ message });
|
||||
},
|
||||
[sendMessage],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const messageEndEvent = answerList.find(
|
||||
(x) => x.event === MessageEventType.MessageEnd,
|
||||
);
|
||||
if (messageEndEvent) {
|
||||
setMessageEndEventList((list) => {
|
||||
const nextList = [...list];
|
||||
if (
|
||||
nextList.every((x) => x.message_id !== messageEndEvent.message_id)
|
||||
) {
|
||||
nextList.push(messageEndEvent as IMessageEndEvent);
|
||||
}
|
||||
return nextList;
|
||||
});
|
||||
}
|
||||
}, [addEventList.length, answerList]);
|
||||
|
||||
useEffect(() => {
|
||||
const { content, id } = findMessageFromList(answerList);
|
||||
const inputAnswer = findInputFromList(answerList);
|
||||
if (answerList.length > 0) {
|
||||
addNewestOneAnswer({
|
||||
answer: content || getLatestError(answerList),
|
||||
id: id,
|
||||
...inputAnswer,
|
||||
});
|
||||
}
|
||||
}, [answerList, addNewestOneAnswer]);
|
||||
|
||||
const handlePressEnter = useCallback(() => {
|
||||
if (trim(value) === '') return;
|
||||
const id = uuid();
|
||||
if (done) {
|
||||
setValue('');
|
||||
handleSendMessage({ id, content: value.trim(), role: MessageType.User });
|
||||
}
|
||||
addNewestOneQuestion({
|
||||
content: value,
|
||||
id,
|
||||
role: MessageType.User,
|
||||
});
|
||||
}, [value, done, addNewestOneQuestion, setValue, handleSendMessage]);
|
||||
|
||||
const sendFormMessage = useCallback(
|
||||
(body: { id?: string; inputs: Record<string, BeginQuery> }) => {
|
||||
send(body);
|
||||
@ -251,17 +200,33 @@ export const useSendNextMessage = () => {
|
||||
[addNewestOneQuestion, send],
|
||||
);
|
||||
|
||||
const findReferenceByMessageId = useCallback(
|
||||
(messageId: string) => {
|
||||
const event = messageEndEventList.find(
|
||||
(item) => item.message_id === messageId,
|
||||
);
|
||||
if (event) {
|
||||
return (event?.data as IMessageEndData)?.reference;
|
||||
}
|
||||
},
|
||||
[messageEndEventList],
|
||||
);
|
||||
const handlePressEnter = useCallback(() => {
|
||||
if (trim(value) === '') return;
|
||||
const id = uuid();
|
||||
if (done) {
|
||||
setValue('');
|
||||
sendMessage({
|
||||
message: { id, content: value.trim(), role: MessageType.User },
|
||||
});
|
||||
}
|
||||
addNewestOneQuestion({
|
||||
content: value,
|
||||
id,
|
||||
role: MessageType.User,
|
||||
});
|
||||
}, [value, done, addNewestOneQuestion, setValue, sendMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
const { content, id } = findMessageFromList(answerList);
|
||||
const inputAnswer = findInputFromList(answerList);
|
||||
if (answerList.length > 0) {
|
||||
addNewestOneAnswer({
|
||||
answer: content || getLatestError(answerList),
|
||||
id: id,
|
||||
...inputAnswer,
|
||||
});
|
||||
}
|
||||
}, [answerList, addNewestOneAnswer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (prologue) {
|
||||
@ -272,7 +237,9 @@ export const useSendNextMessage = () => {
|
||||
}, [addNewestOneAnswer, agentId, prologue, send, sendFormMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
addEventList(answerList);
|
||||
if (typeof addEventList === 'function') {
|
||||
addEventList(answerList);
|
||||
}
|
||||
}, [addEventList, answerList]);
|
||||
|
||||
return {
|
||||
@ -280,8 +247,6 @@ export const useSendNextMessage = () => {
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading: !done,
|
||||
reference,
|
||||
loading,
|
||||
derivedMessages,
|
||||
ref,
|
||||
removeMessageById,
|
||||
@ -68,7 +68,7 @@ function EmbedDialog({
|
||||
|
||||
const generateIframeSrc = useCallback(() => {
|
||||
const { visibleAvatar, locale } = values;
|
||||
let src = `${location.origin}/chat/share?shared_id=${token}&from=${from}&auth=${beta}`;
|
||||
let src = `${location.origin}/next-chat/share?shared_id=${token}&from=${from}&auth=${beta}`;
|
||||
if (visibleAvatar) {
|
||||
src += '&visible_avatar=1';
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { ReactFlowProvider } from '@xyflow/react';
|
||||
import {
|
||||
ChevronDown,
|
||||
@ -37,7 +38,10 @@ import AgentCanvas from './canvas';
|
||||
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';
|
||||
import {
|
||||
useGetBeginNodeDataInputs,
|
||||
useGetBeginNodeDataQueryIsSafe,
|
||||
} from './hooks/use-get-begin-query';
|
||||
import { useOpenDocument } from './hooks/use-open-document';
|
||||
import {
|
||||
useSaveGraph,
|
||||
@ -67,6 +71,8 @@ export default function Agent() {
|
||||
showModal: showChatDrawer,
|
||||
} = useSetModalState();
|
||||
const { t } = useTranslation();
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
|
||||
const openDocument = useOpenDocument();
|
||||
const {
|
||||
handleExportJson,
|
||||
@ -76,7 +82,7 @@ export default function Agent() {
|
||||
hideFileUploadModal,
|
||||
} = useHandleExportOrImportJsonFile();
|
||||
const { saveGraph, loading } = useSaveGraph();
|
||||
const { flowDetail } = useFetchDataOnMount();
|
||||
const { flowDetail: agentDetail } = useFetchDataOnMount();
|
||||
const inputs = useGetBeginNodeDataInputs();
|
||||
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
|
||||
const handleRunAgent = useCallback(() => {
|
||||
@ -95,6 +101,8 @@ export default function Agent() {
|
||||
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
||||
useShowEmbedModal();
|
||||
|
||||
const isBeginNodeDataQuerySafe = useGetBeginNodeDataQueryIsSafe();
|
||||
|
||||
return (
|
||||
<section className="h-full">
|
||||
<PageHeader>
|
||||
@ -107,7 +115,7 @@ export default function Agent() {
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{flowDetail.title}</BreadcrumbPage>
|
||||
<BreadcrumbPage>{agentDetail.title}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
@ -154,7 +162,13 @@ export default function Agent() {
|
||||
{t('flow.export')}
|
||||
</AgentDropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<AgentDropdownMenuItem onClick={showEmbedModal}>
|
||||
<AgentDropdownMenuItem
|
||||
onClick={showEmbedModal}
|
||||
disabled={
|
||||
!isBeginNodeDataQuerySafe ||
|
||||
userInfo.nickname !== agentDetail.nickname
|
||||
}
|
||||
>
|
||||
<ScreenShare />
|
||||
{t('common.embedIntoSite')}
|
||||
</AgentDropdownMenuItem>
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchAgentTemplates, useSetAgent } from '@/hooks/use-agent-request';
|
||||
@ -57,10 +65,21 @@ export default function AgentTemplates() {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader
|
||||
back={navigateToAgentList}
|
||||
title={t('flow.createGraph')}
|
||||
></PageHeader>
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToAgentList}>
|
||||
Agent
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{t('flow.createGraph')}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[94vh] overflow-auto px-8">
|
||||
{list?.map((x) => {
|
||||
return (
|
||||
|
||||
@ -1,20 +1,7 @@
|
||||
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 { SharedFrom } from '@/constants/chat';
|
||||
import { useSendAgentMessage } from '@/pages/agent/chat/use-send-agent-message';
|
||||
import trim from 'lodash/trim';
|
||||
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) === '';
|
||||
@ -40,110 +27,14 @@ export const useGetSharedChatSearchParams = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useSendSharedMessage = () => {
|
||||
const {
|
||||
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,
|
||||
ref,
|
||||
removeLatestMessage,
|
||||
addNewestAnswer,
|
||||
addNewestQuestion,
|
||||
} = useSelectDerivedMessages();
|
||||
const [hasError, setHasError] = useState(false);
|
||||
export function useSendNextSharedMessage() {
|
||||
const { from, sharedId: conversationId } = useGetSharedChatSearchParams();
|
||||
const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`;
|
||||
|
||||
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'),
|
||||
});
|
||||
|
||||
if (isCompletionError(res)) {
|
||||
// cancel loading
|
||||
setValue(message.content);
|
||||
removeLatestMessage();
|
||||
}
|
||||
},
|
||||
[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);
|
||||
}
|
||||
}, [data, send]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSessionId();
|
||||
}, [fetchSessionId, send]);
|
||||
|
||||
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],
|
||||
);
|
||||
const ret = useSendAgentMessage(url);
|
||||
|
||||
return {
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading: !done,
|
||||
ref,
|
||||
loading: false,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
...ret,
|
||||
hasError: false,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
.chatWrapper {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.chatContainer {
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
.messageContainer {
|
||||
overflow-y: auto;
|
||||
padding-right: 6px;
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,115 @@
|
||||
import ChatContainer from './large';
|
||||
import MessageInput from '@/components/message-input';
|
||||
import MessageItem from '@/components/next-message-item';
|
||||
import PdfDrawer from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
||||
import { useFetchNextConversationSSE } from '@/hooks/chat-hooks';
|
||||
import { useFetchAgentAvatar } from '@/hooks/use-agent-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import i18n from '@/locales/config';
|
||||
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import React, { forwardRef, useMemo } from 'react';
|
||||
import {
|
||||
useGetSharedChatSearchParams,
|
||||
useSendNextSharedMessage,
|
||||
} from '../hooks/use-send-shared-message';
|
||||
|
||||
import styles from './index.less';
|
||||
const ChatContainer = () => {
|
||||
const {
|
||||
sharedId: conversationId,
|
||||
from,
|
||||
locale,
|
||||
visibleAvatar,
|
||||
} = useGetSharedChatSearchParams();
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
|
||||
const {
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading,
|
||||
ref,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
findReferenceByMessageId,
|
||||
} = useSendNextSharedMessage();
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
|
||||
const useFetchAvatar = useMemo(() => {
|
||||
return from === SharedFrom.Agent
|
||||
? useFetchAgentAvatar
|
||||
: useFetchNextConversationSSE;
|
||||
}, [from]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (locale && i18n.language !== locale) {
|
||||
i18n.changeLanguage(locale);
|
||||
}
|
||||
}, [locale, visibleAvatar]);
|
||||
const { data: avatarData } = useFetchAvatar();
|
||||
|
||||
if (!conversationId) {
|
||||
return <div>empty</div>;
|
||||
}
|
||||
|
||||
const SharedChat = () => {
|
||||
return (
|
||||
<div className={styles.chatWrapper}>
|
||||
<ChatContainer></ChatContainer>
|
||||
</div>
|
||||
<section className="h-[100vh]">
|
||||
<section className={cn('flex flex-1 flex-col p-2.5 h-full')}>
|
||||
<div className={cn('flex flex-1 flex-col overflow-auto pr-2')}>
|
||||
<div>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
visibleAvatar={visibleAvatar}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
avatarDialog={avatarData.avatar}
|
||||
item={message}
|
||||
nickname="You"
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages?.length - 1 === i
|
||||
}
|
||||
index={i}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
showLikeButton={false}
|
||||
showLoudspeaker={false}
|
||||
showLog={false}
|
||||
></MessageItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div ref={ref} />
|
||||
</div>
|
||||
|
||||
<MessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError}
|
||||
sendDisabled={sendDisabled}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
uploadMethod="external_upload_and_parse"
|
||||
showUploadIcon={false}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
></MessageInput>
|
||||
</section>
|
||||
{visible && (
|
||||
<PdfDrawer
|
||||
visible={visible}
|
||||
hideModal={hideModal}
|
||||
documentId={documentId}
|
||||
chunk={selectedChunk}
|
||||
></PdfDrawer>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default SharedChat;
|
||||
export default forwardRef(ChatContainer);
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
import MessageInput from '@/components/message-input';
|
||||
import MessageItem from '@/components/message-item';
|
||||
import PdfDrawer from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
||||
import { useFetchNextConversationSSE } from '@/hooks/chat-hooks';
|
||||
import { useFetchFlowSSE } from '@/hooks/flow-hooks';
|
||||
import i18n from '@/locales/config';
|
||||
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { Flex, Spin } from 'antd';
|
||||
import React, { forwardRef, useMemo } from 'react';
|
||||
import {
|
||||
useGetSharedChatSearchParams,
|
||||
useSendSharedMessage,
|
||||
} from '../hooks/use-send-shared-message';
|
||||
import { buildMessageItemReference } from '../utils';
|
||||
import styles from './index.less';
|
||||
|
||||
const ChatContainer = () => {
|
||||
const {
|
||||
sharedId: conversationId,
|
||||
from,
|
||||
locale,
|
||||
visibleAvatar,
|
||||
} = useGetSharedChatSearchParams();
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
|
||||
const {
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading,
|
||||
loading,
|
||||
ref,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
} = useSendSharedMessage();
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
|
||||
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();
|
||||
|
||||
if (!conversationId) {
|
||||
return <div>empty</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||
<Flex flex={1} vertical className={styles.messageContainer}>
|
||||
<div>
|
||||
<Spin spinning={loading}>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
visibleAvatar={visibleAvatar}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
avatarDialog={avatarData?.avatar}
|
||||
item={message}
|
||||
nickname="You"
|
||||
reference={buildMessageItemReference(
|
||||
{
|
||||
message: derivedMessages,
|
||||
reference: [],
|
||||
},
|
||||
message,
|
||||
)}
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages?.length - 1 === i
|
||||
}
|
||||
index={i}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
showLikeButton={false}
|
||||
showLoudspeaker={false}
|
||||
></MessageItem>
|
||||
);
|
||||
})}
|
||||
</Spin>
|
||||
</div>
|
||||
<div ref={ref} />
|
||||
</Flex>
|
||||
|
||||
<MessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError}
|
||||
sendDisabled={sendDisabled}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
uploadMethod="external_upload_and_parse"
|
||||
showUploadIcon={false}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
></MessageInput>
|
||||
</Flex>
|
||||
{visible && (
|
||||
<PdfDrawer
|
||||
visible={visible}
|
||||
hideModal={hideModal}
|
||||
documentId={documentId}
|
||||
chunk={selectedChunk}
|
||||
></PdfDrawer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(ChatContainer);
|
||||
@ -1,8 +1,7 @@
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { EmptyConversationId, MessageType } from '@/constants/chat';
|
||||
import { IConversation, IReference } from '@/interfaces/database/chat';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { EmptyConversationId } from './constants';
|
||||
import { IMessage } from './interface';
|
||||
import { IMessage } from '../chat/interface';
|
||||
|
||||
export const isConversationIdExist = (conversationId: string) => {
|
||||
return conversationId !== EmptyConversationId && conversationId !== '';
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
ProfileIcon,
|
||||
TeamIcon,
|
||||
} from '@/assets/icon/Icon';
|
||||
import { IconFont } from '@/components/icon-font';
|
||||
import { LLMFactory } from '@/constants/llm';
|
||||
import { UserSettingRouteKey } from '@/constants/setting';
|
||||
import { MonitorOutlined } from '@ant-design/icons';
|
||||
@ -18,6 +19,9 @@ export const UserSettingIconMap = {
|
||||
[UserSettingRouteKey.Team]: <TeamIcon />,
|
||||
[UserSettingRouteKey.Logout]: <LogOutIcon />,
|
||||
[UserSettingRouteKey.Api]: <ApiIcon />,
|
||||
[UserSettingRouteKey.MCP]: (
|
||||
<IconFont name="mcp" className="size-6"></IconFont>
|
||||
),
|
||||
};
|
||||
|
||||
export * from '@/constants/setting';
|
||||
|
||||
@ -8,6 +8,7 @@ export enum Routes {
|
||||
Agent = '/agent',
|
||||
AgentTemplates = '/agent-templates',
|
||||
Agents = '/agents',
|
||||
AgentList = '/agent-list',
|
||||
Searches = '/next-searches',
|
||||
Search = '/next-search',
|
||||
Chats = '/next-chats',
|
||||
@ -53,6 +54,11 @@ const routes = [
|
||||
component: '@/pages/chat/share',
|
||||
layout: false,
|
||||
},
|
||||
{
|
||||
path: '/next-chat/share',
|
||||
component: '@/pages/next-chats/share',
|
||||
layout: false,
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: '@/layouts',
|
||||
@ -135,6 +141,10 @@ const routes = [
|
||||
path: '/user-setting/api',
|
||||
component: '@/pages/user-setting/setting-api',
|
||||
},
|
||||
{
|
||||
path: `/user-setting${Routes.Mcp}`,
|
||||
component: `@/pages${Routes.ProfileMcp}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -145,6 +155,10 @@ const routes = [
|
||||
path: '/flow',
|
||||
component: '@/pages/flow/list',
|
||||
},
|
||||
{
|
||||
path: Routes.AgentList,
|
||||
component: `@/pages/${Routes.Agents}`,
|
||||
},
|
||||
{
|
||||
path: '/flow/:id',
|
||||
component: '@/pages/flow',
|
||||
|
||||
@ -20,6 +20,7 @@ const {
|
||||
fetchVersionList,
|
||||
fetchVersion,
|
||||
fetchCanvas,
|
||||
fetchAgentAvatar,
|
||||
} = api;
|
||||
|
||||
const methods = {
|
||||
@ -95,6 +96,10 @@ const methods = {
|
||||
url: inputForm,
|
||||
method: 'get',
|
||||
},
|
||||
fetchAgentAvatar: {
|
||||
url: fetchAgentAvatar,
|
||||
method: 'get',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const agentService = registerNextServer<keyof typeof methods>(methods);
|
||||
|
||||
@ -151,6 +151,7 @@ export default {
|
||||
fetchVersionList: (id: string) => `${api_host}/canvas/getlistversion/${id}`,
|
||||
fetchVersion: (id: string) => `${api_host}/canvas/getversion/${id}`,
|
||||
fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`,
|
||||
fetchAgentAvatar: (id: string) => `${api_host}/canvas/getsse/${id}`,
|
||||
|
||||
// mcp server
|
||||
listMcpServer: `${api_host}/mcp_server/list`,
|
||||
|
||||
Reference in New Issue
Block a user