mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
perf: tool call check (#4818)
* i18n * tool call * fix: mcp create permission;Plugin unauth tip * fix: mcp create permission;Plugin unauth tip * fix: Cite modal permission * remove invalide cite * perf: prompt * filter fulltext search * fix: ts * fix: ts * fix: ts
This commit is contained in:
@@ -22,135 +22,184 @@ import { getCollectionSourceData } from '@fastgpt/global/core/dataset/collection
|
||||
import Markdown from '.';
|
||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
import { Types } from 'mongoose';
|
||||
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { useCreation } from 'ahooks';
|
||||
|
||||
const A = ({ children, chatAuthData, showAnimation, ...props }: any) => {
|
||||
export type AProps = {
|
||||
chatAuthData?: {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
chatItemDataId: string;
|
||||
} & OutLinkChatAuthProps;
|
||||
onOpenCiteModal?: (e?: {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
quoteId?: string;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
const EmptyHrefLink = function EmptyHrefLink({ content }: { content: string }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size={'xs'}
|
||||
borderRadius={'md'}
|
||||
my={1}
|
||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text: content })}
|
||||
>
|
||||
{content}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const CiteLink = React.memo(function CiteLink({
|
||||
id,
|
||||
chatAuthData,
|
||||
onOpenCiteModal,
|
||||
showAnimation
|
||||
}: { id: string; showAnimation?: boolean } & AProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const content = useMemo(() => String(children), [children]);
|
||||
|
||||
if (!Types.ObjectId.isValid(id)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const {
|
||||
data: datasetCiteData,
|
||||
loading,
|
||||
runAsync: getQuoteDataById
|
||||
} = useRequest2((id: string) => getQuoteData({ id, ...chatAuthData }), {
|
||||
manual: true
|
||||
});
|
||||
const sourceData = useMemo(
|
||||
() => getCollectionSourceData(datasetCiteData?.collection),
|
||||
[datasetCiteData?.collection]
|
||||
);
|
||||
const icon = useMemo(
|
||||
() => getSourceNameIcon({ sourceId: sourceData.sourceId, sourceName: sourceData.sourceName }),
|
||||
[sourceData]
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
isLazy
|
||||
direction="rtl"
|
||||
placement="bottom"
|
||||
strategy={'fixed'}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onOpen={() => {
|
||||
onOpen();
|
||||
if (showAnimation) return;
|
||||
getQuoteDataById(id);
|
||||
}}
|
||||
trigger={'hover'}
|
||||
gutter={4}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
|
||||
<MyIcon
|
||||
name={'core/chat/quoteSign'}
|
||||
w={'1rem'}
|
||||
color={'primary.700'}
|
||||
cursor={'pointer'}
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent boxShadow={'lg'} w={'500px'} maxW={'90vw'} py={4}>
|
||||
<MyBox isLoading={loading || showAnimation}>
|
||||
<PopoverArrow />
|
||||
<PopoverBody py={0} px={0} fontSize={'sm'}>
|
||||
<Flex px={4} pb={1} justifyContent={'space-between'}>
|
||||
<Box
|
||||
alignItems={'center'}
|
||||
fontSize={'xs'}
|
||||
border={'sm'}
|
||||
borderRadius={'sm'}
|
||||
overflow={'hidden'}
|
||||
display={'inline-flex'}
|
||||
height={6}
|
||||
mr={1}
|
||||
>
|
||||
<Flex px={1.5}>
|
||||
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box
|
||||
className={'textEllipsis'}
|
||||
wordBreak={'break-all'}
|
||||
flex={'1 0 0'}
|
||||
fontSize={'mini'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{sourceData.sourceName}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
color={'primary.600'}
|
||||
size={'xs'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
onOpenCiteModal?.({
|
||||
quoteId: id,
|
||||
sourceId: sourceData.sourceId,
|
||||
sourceName: sourceData.sourceName,
|
||||
datasetId: datasetCiteData?.collection.datasetId,
|
||||
collectionId: datasetCiteData?.collection._id
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('common:all_quotes')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box h={'300px'} overflow={'auto'} px={4}>
|
||||
<Markdown source={datasetCiteData?.q} />
|
||||
{datasetCiteData?.a && <Markdown source={datasetCiteData?.a} />}
|
||||
</Box>
|
||||
</PopoverBody>
|
||||
</MyBox>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
const A = ({
|
||||
children,
|
||||
chatAuthData,
|
||||
onOpenCiteModal,
|
||||
showAnimation,
|
||||
...props
|
||||
}: AProps & {
|
||||
children: any;
|
||||
showAnimation: boolean;
|
||||
[key: string]: any;
|
||||
}) => {
|
||||
const content = useCreation(() => String(children), [children]);
|
||||
|
||||
// empty href link
|
||||
if (!props.href && typeof children?.[0] === 'string') {
|
||||
return (
|
||||
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size={'xs'}
|
||||
borderRadius={'md'}
|
||||
my={1}
|
||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text: content })}
|
||||
>
|
||||
{content}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
);
|
||||
return <EmptyHrefLink content={content} />;
|
||||
}
|
||||
|
||||
// Cite
|
||||
if (
|
||||
(props.href?.startsWith('CITE') || props.href?.startsWith('QUOTE')) &&
|
||||
typeof children?.[0] === 'string'
|
||||
typeof content === 'string'
|
||||
) {
|
||||
if (!Types.ObjectId.isValid(content)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const {
|
||||
data: quoteData,
|
||||
loading,
|
||||
runAsync: getQuoteDataById
|
||||
} = useRequest2((id: string) => getQuoteData({ id, ...chatAuthData }), {
|
||||
manual: true
|
||||
});
|
||||
const sourceData = useMemo(
|
||||
() => getCollectionSourceData(quoteData?.collection),
|
||||
[quoteData?.collection]
|
||||
);
|
||||
const icon = useMemo(
|
||||
() => getSourceNameIcon({ sourceId: sourceData.sourceId, sourceName: sourceData.sourceName }),
|
||||
[sourceData]
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
isLazy
|
||||
direction="rtl"
|
||||
placement="bottom"
|
||||
strategy={'fixed'}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onOpen={() => {
|
||||
onOpen();
|
||||
if (showAnimation) return;
|
||||
getQuoteDataById(String(children));
|
||||
}}
|
||||
trigger={'hover'}
|
||||
gutter={4}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
|
||||
<MyIcon
|
||||
name={'core/chat/quoteSign'}
|
||||
w={'1rem'}
|
||||
color={'primary.700'}
|
||||
cursor={'pointer'}
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent boxShadow={'lg'} w={'500px'} maxW={'90vw'} py={4}>
|
||||
<MyBox isLoading={loading || showAnimation}>
|
||||
<PopoverArrow />
|
||||
<PopoverBody py={0} px={0} fontSize={'sm'}>
|
||||
<Flex px={4} pb={1} justifyContent={'space-between'}>
|
||||
<Box
|
||||
alignItems={'center'}
|
||||
fontSize={'xs'}
|
||||
border={'sm'}
|
||||
borderRadius={'sm'}
|
||||
overflow={'hidden'}
|
||||
display={'inline-flex'}
|
||||
height={6}
|
||||
mr={1}
|
||||
>
|
||||
<Flex px={1.5}>
|
||||
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box
|
||||
className={'textEllipsis'}
|
||||
wordBreak={'break-all'}
|
||||
flex={'1 0 0'}
|
||||
fontSize={'mini'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{sourceData.sourceName}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
color={'primary.600'}
|
||||
size={'xs'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
eventBus.emit(EventNameEnum.openQuoteReader, {
|
||||
quoteId: String(children),
|
||||
sourceId: sourceData.sourceId,
|
||||
sourceName: sourceData.sourceName,
|
||||
datasetId: quoteData?.collection.datasetId,
|
||||
collectionId: quoteData?.collection._id
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('common:all_quotes')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box h={'300px'} overflow={'auto'} px={4}>
|
||||
<Markdown source={quoteData?.q} />
|
||||
{quoteData?.a && <Markdown source={quoteData?.a} />}
|
||||
</Box>
|
||||
</PopoverBody>
|
||||
</MyBox>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<CiteLink
|
||||
id={content}
|
||||
chatAuthData={chatAuthData}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
showAnimation={showAnimation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,7 @@ import dynamic from 'next/dynamic';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { CodeClassNameEnum, mdTextFormat } from './utils';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import type { AProps } from './A';
|
||||
|
||||
const CodeLight = dynamic(() => import('./codeBlock/CodeLight'), { ssr: false });
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
||||
@@ -33,12 +33,7 @@ type Props = {
|
||||
showAnimation?: boolean;
|
||||
isDisabled?: boolean;
|
||||
forbidZhFormat?: boolean;
|
||||
chatAuthData?: {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
chatItemDataId: string;
|
||||
} & OutLinkChatAuthProps;
|
||||
};
|
||||
} & AProps;
|
||||
const Markdown = (props: Props) => {
|
||||
const source = props.source || '';
|
||||
|
||||
@@ -53,16 +48,25 @@ const MarkdownRender = ({
|
||||
showAnimation,
|
||||
isDisabled,
|
||||
forbidZhFormat,
|
||||
chatAuthData
|
||||
|
||||
chatAuthData,
|
||||
onOpenCiteModal
|
||||
}: Props) => {
|
||||
const components = useCreation(() => {
|
||||
return {
|
||||
img: Image,
|
||||
pre: RewritePre,
|
||||
code: Code,
|
||||
a: (props: any) => <A {...props} showAnimation={showAnimation} chatAuthData={chatAuthData} />
|
||||
a: (props: any) => (
|
||||
<A
|
||||
{...props}
|
||||
showAnimation={showAnimation}
|
||||
chatAuthData={chatAuthData}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
/>
|
||||
)
|
||||
};
|
||||
}, [chatAuthData, showAnimation]);
|
||||
}, [chatAuthData, onOpenCiteModal, showAnimation]);
|
||||
|
||||
const formatSource = useMemo(() => {
|
||||
if (showAnimation || forbidZhFormat) return source;
|
||||
|
@@ -28,10 +28,15 @@ import { isEqual } from 'lodash';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
|
||||
import dayjs from 'dayjs';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
import {
|
||||
ChatItemContext,
|
||||
type OnOpenCiteModalProps
|
||||
} from '@/web/core/chat/context/chatItemContext';
|
||||
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
const ResponseTags = dynamic(() => import('./ResponseTags'));
|
||||
|
||||
const colorMap = {
|
||||
[ChatStatusEnum.loading]: {
|
||||
@@ -88,13 +93,15 @@ const AIContentCard = React.memo(function AIContentCard({
|
||||
dataId,
|
||||
isLastChild,
|
||||
isChatting,
|
||||
questionGuides
|
||||
questionGuides,
|
||||
onOpenCiteModal
|
||||
}: {
|
||||
dataId: string;
|
||||
chatValue: ChatItemValueItemType[];
|
||||
isLastChild: boolean;
|
||||
isChatting: boolean;
|
||||
questionGuides: string[];
|
||||
onOpenCiteModal: (e?: OnOpenCiteModalProps) => void;
|
||||
}) {
|
||||
return (
|
||||
<Flex flexDirection={'column'} gap={2}>
|
||||
@@ -108,6 +115,7 @@ const AIContentCard = React.memo(function AIContentCard({
|
||||
value={value}
|
||||
isLastResponseValue={isLastChild && i === chatValue.length - 1}
|
||||
isChatting={isChatting}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -122,7 +130,6 @@ const ChatItem = (props: Props) => {
|
||||
const { type, avatar, statusBoxData, children, isLastChild, questionGuides = [], chat } = props;
|
||||
|
||||
const { isPc } = useSystem();
|
||||
const { toast } = useToast();
|
||||
|
||||
const styleMap: BoxProps = {
|
||||
...(type === ChatRoleEnum.Human
|
||||
@@ -150,7 +157,6 @@ const ChatItem = (props: Props) => {
|
||||
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
||||
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus);
|
||||
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
@@ -228,64 +234,48 @@ const ChatItem = (props: Props) => {
|
||||
return groupedValues;
|
||||
}, [chat.obj, chat.value, isChatting]);
|
||||
|
||||
const handleOpenQuoteReader = useCallback(
|
||||
({
|
||||
collectionId,
|
||||
sourceId,
|
||||
sourceName,
|
||||
datasetId,
|
||||
quoteId
|
||||
}: {
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
const onOpenCiteModal = useMemoizedFn(
|
||||
(item?: {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
quoteId?: string;
|
||||
}) => {
|
||||
if (!setQuoteData) return;
|
||||
|
||||
const collectionIdList = collectionId
|
||||
? [collectionId]
|
||||
const collectionIdList = item?.collectionId
|
||||
? [item.collectionId]
|
||||
: [...new Set(quoteList.map((item) => item.collectionId))];
|
||||
|
||||
setQuoteData({
|
||||
setCiteModalData({
|
||||
rawSearch: quoteList,
|
||||
metadata:
|
||||
collectionId && isShowReadRawSource
|
||||
item?.collectionId && isShowReadRawSource
|
||||
? {
|
||||
appId: appId,
|
||||
chatId: chatId,
|
||||
chatItemDataId: chat.dataId,
|
||||
collectionId: collectionId,
|
||||
collectionId: item.collectionId,
|
||||
collectionIdList,
|
||||
sourceId: sourceId || '',
|
||||
sourceName: sourceName || '',
|
||||
datasetId: datasetId || '',
|
||||
sourceId: item.sourceId || '',
|
||||
sourceName: item.sourceName || '',
|
||||
datasetId: item.datasetId || '',
|
||||
outLinkAuthData,
|
||||
quoteId
|
||||
quoteId: item.quoteId
|
||||
}
|
||||
: {
|
||||
appId: appId,
|
||||
chatId: chatId,
|
||||
chatItemDataId: chat.dataId,
|
||||
collectionIdList,
|
||||
sourceId: sourceId,
|
||||
sourceName: sourceName,
|
||||
sourceId: item?.sourceId,
|
||||
sourceName: item?.sourceName,
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
},
|
||||
[setQuoteData, quoteList, isShowReadRawSource, appId, chatId, chat.dataId, outLinkAuthData]
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (chat.obj !== ChatRoleEnum.AI) return;
|
||||
eventBus.on(EventNameEnum.openQuoteReader, handleOpenQuoteReader);
|
||||
return () => {
|
||||
eventBus.off(EventNameEnum.openQuoteReader);
|
||||
};
|
||||
}, [chat.obj, handleOpenQuoteReader]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
_hover={{
|
||||
@@ -362,13 +352,23 @@ const ChatItem = (props: Props) => {
|
||||
>
|
||||
{type === ChatRoleEnum.Human && <HumanContentCard chatValue={value} />}
|
||||
{type === ChatRoleEnum.AI && (
|
||||
<AIContentCard
|
||||
chatValue={value}
|
||||
dataId={chat.dataId}
|
||||
isLastChild={isLastChild && i === splitAiResponseResults.length - 1}
|
||||
isChatting={isChatting}
|
||||
questionGuides={questionGuides}
|
||||
/>
|
||||
<>
|
||||
<AIContentCard
|
||||
chatValue={value}
|
||||
dataId={chat.dataId}
|
||||
isLastChild={isLastChild && i === splitAiResponseResults.length - 1}
|
||||
isChatting={isChatting}
|
||||
questionGuides={questionGuides}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
/>
|
||||
{i === splitAiResponseResults.length - 1 && (
|
||||
<ResponseTags
|
||||
showTags={!isLastChild || !isChatting}
|
||||
historyItem={chat}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* Example: Response tags. A set of dialogs only needs to be displayed once*/}
|
||||
{i === splitAiResponseResults.length - 1 && <>{children}</>}
|
||||
|
@@ -14,17 +14,24 @@ import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
||||
import { useSize } from 'ahooks';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
|
||||
const ContextModal = dynamic(() => import('./ContextModal'));
|
||||
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
|
||||
|
||||
const ResponseTags = ({
|
||||
showTags,
|
||||
historyItem
|
||||
historyItem,
|
||||
onOpenCiteModal
|
||||
}: {
|
||||
showTags: boolean;
|
||||
historyItem: ChatSiteItemType;
|
||||
onOpenCiteModal: (e?: {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
quoteId?: string;
|
||||
}) => void;
|
||||
}) => {
|
||||
const { isPc } = useSystem();
|
||||
const { t } = useTranslation();
|
||||
@@ -80,15 +87,6 @@ const ResponseTags = ({
|
||||
}));
|
||||
}, [quoteList]);
|
||||
|
||||
const openQuoteReader = (item?: {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
}) => {
|
||||
eventBus.emit(EventNameEnum.openQuoteReader, item);
|
||||
};
|
||||
|
||||
const notEmptyTags =
|
||||
quoteList.length > 0 ||
|
||||
(llmModuleAccount === 1 && notSharePage) ||
|
||||
@@ -161,7 +159,7 @@ const ResponseTags = ({
|
||||
cursor={'pointer'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openQuoteReader(item);
|
||||
onOpenCiteModal(item);
|
||||
}}
|
||||
height={6}
|
||||
>
|
||||
@@ -216,7 +214,7 @@ const ResponseTags = ({
|
||||
cursor={'pointer'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openQuoteReader();
|
||||
onOpenCiteModal();
|
||||
}}
|
||||
>
|
||||
{t('chat:citations', { num: quoteList.length })}
|
||||
|
@@ -68,7 +68,6 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
|
||||
const ResponseTags = dynamic(() => import('./components/ResponseTags'));
|
||||
const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
|
||||
const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal'));
|
||||
const SelectMarkCollection = dynamic(() => import('./components/SelectMarkCollection'));
|
||||
@@ -1014,10 +1013,6 @@ const ChatBox = ({
|
||||
onReadUserDislike: onReadUserDislike(item)
|
||||
}}
|
||||
>
|
||||
<ResponseTags
|
||||
showTags={index !== chatRecords.length - 1 || !isChatting}
|
||||
historyItem={item}
|
||||
/>
|
||||
{/* custom feedback */}
|
||||
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
|
||||
<Box>
|
||||
@@ -1072,7 +1067,6 @@ const ChatBox = ({
|
||||
chatType,
|
||||
delOneMessage,
|
||||
externalVariableList?.length,
|
||||
isChatting,
|
||||
onAddUserDislike,
|
||||
onAddUserLike,
|
||||
onCloseCustomFeedback,
|
||||
|
@@ -30,7 +30,7 @@ import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
import { SelectOptionsComponent, FormInputComponent } from './Interactive/InteractiveComponents';
|
||||
import { extractDeepestInteractive } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { type OnOpenCiteModalProps } from '@/web/core/chat/context/chatItemContext';
|
||||
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
|
||||
import { useCreation } from 'ahooks';
|
||||
|
||||
@@ -90,11 +90,13 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
||||
const RenderText = React.memo(function RenderText({
|
||||
showAnimation,
|
||||
text,
|
||||
chatItemDataId
|
||||
chatItemDataId,
|
||||
onOpenCiteModal
|
||||
}: {
|
||||
showAnimation: boolean;
|
||||
text: string;
|
||||
chatItemDataId: string;
|
||||
onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
|
||||
}) {
|
||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||
@@ -111,7 +113,14 @@ const RenderText = React.memo(function RenderText({
|
||||
return { appId, chatId, chatItemDataId, ...outLinkAuthData };
|
||||
}, [appId, chatId, chatItemDataId, outLinkAuthData]);
|
||||
|
||||
return <Markdown source={source} showAnimation={showAnimation} chatAuthData={chatAuthData} />;
|
||||
return (
|
||||
<Markdown
|
||||
source={source}
|
||||
showAnimation={showAnimation}
|
||||
chatAuthData={chatAuthData}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const RenderTool = React.memo(
|
||||
@@ -240,12 +249,14 @@ const AIResponseBox = ({
|
||||
chatItemDataId,
|
||||
value,
|
||||
isLastResponseValue,
|
||||
isChatting
|
||||
isChatting,
|
||||
onOpenCiteModal
|
||||
}: {
|
||||
chatItemDataId: string;
|
||||
value: UserChatItemValueItemType | AIChatItemValueItemType;
|
||||
isLastResponseValue: boolean;
|
||||
isChatting: boolean;
|
||||
onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
|
||||
}) => {
|
||||
if (value.type === ChatItemValueTypeEnum.text && value.text) {
|
||||
return (
|
||||
@@ -253,6 +264,7 @@ const AIResponseBox = ({
|
||||
chatItemDataId={chatItemDataId}
|
||||
showAnimation={isChatting && isLastResponseValue}
|
||||
text={value.text.content}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -38,8 +38,8 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
@@ -81,7 +81,7 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
||||
right={0}
|
||||
h={['100%', '96%']}
|
||||
w={'100%'}
|
||||
maxW={quoteData ? ['100%', '1080px'] : ['100%', '600px']}
|
||||
maxW={datasetCiteData ? ['100%', '1080px'] : ['100%', '600px']}
|
||||
bg={'white'}
|
||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||
borderRadius={'md'}
|
||||
@@ -169,7 +169,7 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{quoteData && (
|
||||
{datasetCiteData && (
|
||||
<Box
|
||||
flex={'1 0 0'}
|
||||
w={0}
|
||||
@@ -183,9 +183,9 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
||||
borderRadius={'md'}
|
||||
>
|
||||
<ChatQuoteList
|
||||
rawSearch={quoteData.rawSearch}
|
||||
metadata={quoteData.metadata}
|
||||
onClose={() => setQuoteData(undefined)}
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
@@ -109,6 +109,10 @@ const EditForm = ({
|
||||
boxShadow:
|
||||
'0px 4px 4px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)'
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
setCurrentTool(tool);
|
||||
}}
|
||||
>
|
||||
<Flex alignItems={'center'} py={2} px={3}>
|
||||
<Box w={'20px'} fontSize={'14px'} color={'myGray.500'} fontWeight={'medium'}>
|
||||
@@ -157,23 +161,11 @@ const EditForm = ({
|
||||
hoverBg={'rgba(51, 112, 255, 0.10)'}
|
||||
hoverBorderColor={'primary.300'}
|
||||
tip={t('app:MCP_tools_detail')}
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setToolDetail(tool);
|
||||
}}
|
||||
/>
|
||||
<MyIconButton
|
||||
size={'16px'}
|
||||
icon={'core/workflow/debug'}
|
||||
p={2}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.250'}
|
||||
hoverBg={'rgba(51, 112, 255, 0.10)'}
|
||||
hoverBorderColor={'primary.300'}
|
||||
tip={t('app:MCP_tools_debug')}
|
||||
onClick={() => {
|
||||
setCurrentTool(tool);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
);
|
||||
|
@@ -26,8 +26,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
// form2AppWorkflow dependent allDatasets
|
||||
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
||||
|
||||
@@ -42,8 +42,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
|
||||
}, [appForm, setWorkflowData, t]);
|
||||
|
||||
useEffect(() => {
|
||||
setRenderEdit(!quoteData);
|
||||
}, [quoteData, setRenderEdit]);
|
||||
setRenderEdit(!datasetCiteData);
|
||||
}, [datasetCiteData, setRenderEdit]);
|
||||
|
||||
const { ChatContainer, restartChat, loading } = useChatTest({
|
||||
...workflowData,
|
||||
@@ -89,12 +89,12 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
|
||||
<ChatContainer />
|
||||
</Box>
|
||||
</MyBox>
|
||||
{quoteData && (
|
||||
{datasetCiteData && (
|
||||
<Box flex={'1 0 0'} w={0} maxW={'560px'} {...cardStyles} boxShadow={'3'}>
|
||||
<ChatQuoteList
|
||||
rawSearch={quoteData.rawSearch}
|
||||
metadata={quoteData.metadata}
|
||||
onClose={() => setQuoteData(undefined)}
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
@@ -17,7 +17,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import ConfigToolModal from './ConfigToolModal';
|
||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { checkAppUnExistError } from '@fastgpt/global/core/app/utils';
|
||||
import { formatToolError } from '@fastgpt/global/core/app/utils';
|
||||
|
||||
const ToolSelect = ({
|
||||
appForm,
|
||||
@@ -65,7 +65,7 @@ const ToolSelect = ({
|
||||
gridGap={[2, 4]}
|
||||
>
|
||||
{appForm.selectedTools.map((item) => {
|
||||
const hasError = checkAppUnExistError(item.pluginData?.error);
|
||||
const toolError = formatToolError(item.pluginData?.error);
|
||||
|
||||
return (
|
||||
<MyTooltip key={item.id} label={item.intro}>
|
||||
@@ -77,10 +77,10 @@ const ToolSelect = ({
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
borderColor={hasError ? 'red.600' : ''}
|
||||
borderColor={toolError ? 'red.600' : ''}
|
||||
_hover={{
|
||||
...hoverDeleteStyles,
|
||||
borderColor: hasError ? 'red.600' : 'primary.300'
|
||||
borderColor: toolError ? 'red.600' : 'primary.300'
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
@@ -93,7 +93,7 @@ const ToolSelect = ({
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) ||
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||
) ||
|
||||
hasError ||
|
||||
toolError ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.tool ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.toolSet
|
||||
) {
|
||||
@@ -113,21 +113,19 @@ const ToolSelect = ({
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
{hasError && (
|
||||
<MyTooltip label={t('app:app.modules.not_found_tips')}>
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={6}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t('app:app.modules.not_found')}</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
{toolError && (
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={6}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t(toolError as any)}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<DeleteIcon
|
||||
ml={2}
|
||||
|
@@ -43,8 +43,8 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
||||
});
|
||||
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
@@ -60,7 +60,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
||||
bottom={0}
|
||||
right={0}
|
||||
onClick={() => {
|
||||
setQuoteData(undefined);
|
||||
setCiteModalData(undefined);
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
@@ -72,7 +72,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
||||
top={5}
|
||||
right={0}
|
||||
h={isOpen ? '95%' : '0'}
|
||||
w={isOpen ? (quoteData ? ['100%', '960px'] : ['100%', '460px']) : '0'}
|
||||
w={isOpen ? (datasetCiteData ? ['100%', '960px'] : ['100%', '460px']) : '0'}
|
||||
bg={'white'}
|
||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||
borderRadius={'md'}
|
||||
@@ -152,7 +152,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
||||
<ChatContainer />
|
||||
</Box>
|
||||
|
||||
{quoteData && (
|
||||
{datasetCiteData && (
|
||||
<Box
|
||||
flex={'1 0 0'}
|
||||
w={0}
|
||||
@@ -166,9 +166,9 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
||||
borderRadius={'md'}
|
||||
>
|
||||
<ChatQuoteList
|
||||
rawSearch={quoteData.rawSearch}
|
||||
metadata={quoteData.metadata}
|
||||
onClose={() => setQuoteData(undefined)}
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
@@ -35,6 +35,7 @@ import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { formatToolError } from '@fastgpt/global/core/app/utils';
|
||||
|
||||
type Props = FlowNodeItemType & {
|
||||
children?: React.ReactNode | React.ReactNode[] | string;
|
||||
@@ -144,6 +145,7 @@ const NodeCard = (props: Props) => {
|
||||
/* Node header */
|
||||
const Header = useMemo(() => {
|
||||
const showHeader = node?.flowNodeType !== FlowNodeTypeEnum.comment;
|
||||
const error = formatToolError(node?.pluginData?.error);
|
||||
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
@@ -254,23 +256,19 @@ const NodeCard = (props: Props) => {
|
||||
)}
|
||||
</UseGuideModal>
|
||||
)}
|
||||
{!!node?.pluginData?.error && (
|
||||
<MyTooltip label={node?.pluginData?.error || t('app:app.modules.not_found_tips')}>
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={8}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>
|
||||
{node?.pluginData?.error || t('app:app.modules.not_found')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
{!!error && (
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={8}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t(error as any)}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||
|
@@ -3,7 +3,6 @@ import {
|
||||
type ReactNode,
|
||||
type SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState
|
||||
} from 'react';
|
||||
@@ -22,7 +21,6 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { checkAppUnExistError } from '@fastgpt/global/core/app/utils';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
const InfoModal = dynamic(() => import('./InfoModal'));
|
||||
@@ -205,16 +203,6 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
[appDetail.name, deleteApp, openConfirmDel, t]
|
||||
);
|
||||
|
||||
// check app unExist error
|
||||
useEffect(() => {
|
||||
if (appDetail.modules.some((module) => checkAppUnExistError(module.pluginData?.error))) {
|
||||
toast({
|
||||
title: t('app:app.error.unExist_app'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
}, [appDetail.modules, t, toast]);
|
||||
|
||||
const contextValue: AppContextType = useMemo(
|
||||
() => ({
|
||||
appId,
|
||||
|
@@ -46,7 +46,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
||||
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
|
||||
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
|
||||
const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
const concatHistory = useMemo(() => {
|
||||
const formatHistories: HistoryItemType[] = histories.map((item) => {
|
||||
@@ -146,7 +146,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
||||
overflow={'hidden'}
|
||||
onClick={() => {
|
||||
onChangeChatId();
|
||||
setQuoteData(undefined);
|
||||
setCiteModalData(undefined);
|
||||
}}
|
||||
>
|
||||
{t('common:core.chat.New Chat')}
|
||||
@@ -202,7 +202,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
||||
: {
|
||||
onClick: () => {
|
||||
onChangeChatId(item.id);
|
||||
setQuoteData(undefined);
|
||||
setCiteModalData(undefined);
|
||||
}
|
||||
})}
|
||||
{...(i !== concatHistory.length - 1 && {
|
||||
@@ -274,7 +274,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
||||
onDelHistory(item.id);
|
||||
if (item.id === activeChatId) {
|
||||
onChangeChatId();
|
||||
setQuoteData(undefined);
|
||||
setCiteModalData(undefined);
|
||||
}
|
||||
},
|
||||
type: 'danger'
|
||||
|
@@ -81,4 +81,5 @@ function App({ Component, pageProps }: AppPropsWithLayout) {
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default appWithTranslation(App);
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
} from '@fastgpt/global/core/app/mcpTools/utils';
|
||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export type createMCPToolsQuery = {};
|
||||
|
||||
@@ -33,7 +34,7 @@ async function handler(
|
||||
const { name, avatar, toolList, url, parentId } = req.body;
|
||||
|
||||
const { teamId, tmbId, userId } = parentId
|
||||
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
|
||||
? await authApp({ req, appId: parentId, per: WritePermissionVal, authToken: true })
|
||||
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
||||
|
||||
await checkTeamAppLimit(teamId);
|
||||
|
@@ -56,10 +56,11 @@ async function handler(
|
||||
});
|
||||
}
|
||||
|
||||
await MongoApp.findByIdAndUpdate(
|
||||
appId,
|
||||
await MongoApp.updateOne(
|
||||
{ _id: appId },
|
||||
{
|
||||
modules: [getMCPToolSetRuntimeNode({ url, toolList, name: app.name, avatar: app.avatar })]
|
||||
modules: [getMCPToolSetRuntimeNode({ url, toolList, name: app.name, avatar: app.avatar })],
|
||||
updateTime: new Date()
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
@@ -97,6 +98,7 @@ const updateMCPChildrenTool = async ({
|
||||
teamId
|
||||
});
|
||||
|
||||
// 删除 DB 里有,新的工具列表里没有的工具
|
||||
for await (const tool of dbTools) {
|
||||
if (!toolSetData.toolList.find((t) => t.name === tool.name)) {
|
||||
await onDelOneApp({
|
||||
@@ -107,6 +109,7 @@ const updateMCPChildrenTool = async ({
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 DB 里没有,新的工具列表里有的工具
|
||||
for await (const tool of toolSetData.toolList) {
|
||||
if (!dbTools.find((t) => t.name === tool.name)) {
|
||||
await onCreateApp({
|
||||
@@ -123,11 +126,12 @@ const updateMCPChildrenTool = async ({
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 DB 里有的工具
|
||||
for await (const tool of toolSetData.toolList) {
|
||||
const dbTool = dbTools.find((t) => t.name === tool.name);
|
||||
if (dbTool) {
|
||||
await MongoApp.findByIdAndUpdate(
|
||||
dbTool._id,
|
||||
await MongoApp.updateOne(
|
||||
{ _id: dbTool._id },
|
||||
{
|
||||
modules: [getMCPToolRuntimeNode({ tool, url: toolSetData.url })]
|
||||
},
|
||||
|
@@ -6,7 +6,7 @@ import { quoteDataFieldSelector, type QuoteDataItemType } from '@/service/core/c
|
||||
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
|
||||
export type GetQuoteDataProps = {
|
||||
export type GetQuoteProps = {
|
||||
datasetDataIdList: string[];
|
||||
|
||||
collectionIdList: string[];
|
||||
@@ -19,9 +19,9 @@ export type GetQuoteDataProps = {
|
||||
teamToken?: string;
|
||||
};
|
||||
|
||||
export type GetQuoteDataRes = QuoteDataItemType[];
|
||||
export type GetQuotesRes = QuoteDataItemType[];
|
||||
|
||||
async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuoteDataRes> {
|
||||
async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRes> {
|
||||
const {
|
||||
appId,
|
||||
chatId,
|
||||
|
@@ -8,6 +8,7 @@ import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/ch
|
||||
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
export type GetQuoteDataResponse = {
|
||||
collection: DatasetCollectionSchemaType;
|
||||
@@ -46,7 +47,7 @@ async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuot
|
||||
|
||||
const datasetData = await MongoDatasetData.findById(dataId);
|
||||
if (!datasetData) {
|
||||
return Promise.reject('Can not find the data');
|
||||
return Promise.reject(i18nT('common:data_not_found'));
|
||||
}
|
||||
|
||||
const [collection, { responseDetail }] = await Promise.all([
|
||||
|
@@ -59,8 +59,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
|
||||
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
@@ -148,7 +148,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
);
|
||||
|
||||
return isPc || !appId ? (
|
||||
<SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
|
||||
<SideBar externalTrigger={!!datasetCiteData}>{Children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
@@ -161,7 +161,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [t, isPc, appId, isOpenSlider, onCloseSlider, quoteData]);
|
||||
}, [t, isPc, appId, isOpenSlider, onCloseSlider, datasetCiteData]);
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
@@ -173,7 +173,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{(!quoteData || isPc) && (
|
||||
{(!datasetCiteData || isPc) && (
|
||||
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||
{/* pc always show history. */}
|
||||
@@ -222,12 +222,12 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
</PageContainer>
|
||||
)}
|
||||
|
||||
{quoteData && (
|
||||
{datasetCiteData && (
|
||||
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
|
||||
<ChatQuoteList
|
||||
rawSearch={quoteData.rawSearch}
|
||||
metadata={quoteData.metadata}
|
||||
onClose={() => setQuoteData(undefined)}
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</PageContainer>
|
||||
)}
|
||||
|
@@ -85,8 +85,8 @@ const OutLink = (props: Props) => {
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
const isResponseDetail = useContextSelector(ChatItemContext, (v) => v.isResponseDetail);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
@@ -226,7 +226,7 @@ const OutLink = (props: Props) => {
|
||||
if (showHistory !== '1') return null;
|
||||
|
||||
return isPc ? (
|
||||
<SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
|
||||
<SideBar externalTrigger={!!datasetCiteData}>{Children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
@@ -241,7 +241,7 @@ const OutLink = (props: Props) => {
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [isOpenSlider, isPc, onCloseSlider, quoteData, showHistory, t]);
|
||||
}, [isOpenSlider, isPc, onCloseSlider, datasetCiteData, showHistory, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -255,7 +255,7 @@ const OutLink = (props: Props) => {
|
||||
gap={4}
|
||||
{...(isEmbed ? { p: '0 !important', borderRadius: '0', boxShadow: 'none' } : { p: [0, 5] })}
|
||||
>
|
||||
{(!quoteData || isPc) && (
|
||||
{(!datasetCiteData || isPc) && (
|
||||
<PageContainer flex={'1 0 0'} w={0} p={'0 !important'}>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||
{RenderHistoryList}
|
||||
@@ -303,12 +303,12 @@ const OutLink = (props: Props) => {
|
||||
</PageContainer>
|
||||
)}
|
||||
|
||||
{quoteData && (
|
||||
{datasetCiteData && (
|
||||
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'} p={'0 !important'}>
|
||||
<ChatQuoteList
|
||||
rawSearch={quoteData.rawSearch}
|
||||
metadata={quoteData.metadata}
|
||||
onClose={() => setQuoteData(undefined)}
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</PageContainer>
|
||||
)}
|
||||
|
@@ -64,8 +64,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
@@ -166,7 +166,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
);
|
||||
|
||||
return isPc || !appId ? (
|
||||
<SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
|
||||
<SideBar externalTrigger={!!datasetCiteData}>{Children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
@@ -179,7 +179,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [appId, isOpenSlider, isPc, onCloseSlider, quoteData, t]);
|
||||
}, [appId, isOpenSlider, isPc, onCloseSlider, datasetCiteData, t]);
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
@@ -191,7 +191,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{(!quoteData || isPc) && (
|
||||
{(!datasetCiteData || isPc) && (
|
||||
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
||||
{RenderHistoryList}
|
||||
@@ -236,12 +236,12 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
</Flex>
|
||||
</PageContainer>
|
||||
)}
|
||||
{quoteData && (
|
||||
{datasetCiteData && (
|
||||
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
|
||||
<ChatQuoteList
|
||||
rawSearch={quoteData.rawSearch}
|
||||
metadata={quoteData.metadata}
|
||||
onClose={() => setQuoteData(undefined)}
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</PageContainer>
|
||||
)}
|
||||
|
@@ -30,6 +30,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { type McpKeyType } from '@fastgpt/global/support/mcp/type';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
|
||||
const UsageWay = dynamic(() => import('@/pageComponents/dashboard/mcp/usageWay'), {
|
||||
ssr: false
|
||||
@@ -38,6 +39,7 @@ const UsageWay = dynamic(() => import('@/pageComponents/dashboard/mcp/usageWay')
|
||||
const McpServer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
const {
|
||||
data: mcpServerList = [],
|
||||
@@ -78,7 +80,10 @@ const McpServer = () => {
|
||||
{t('dashboard_mcp:mcp_server_description')}
|
||||
</Box>
|
||||
</Box>
|
||||
<Button onClick={() => setEditMcp(defaultForm)}>
|
||||
<Button
|
||||
isDisabled={!userInfo?.permission.hasApikeyCreatePer}
|
||||
onClick={() => setEditMcp(defaultForm)}
|
||||
>
|
||||
{t('dashboard_mcp:create_mcp_server')}
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -94,7 +99,10 @@ const McpServer = () => {
|
||||
{t('dashboard_mcp:mcp_server_description')}
|
||||
</Box>
|
||||
<Flex mt={2} justifyContent={'flex-end'}>
|
||||
<Button onClick={() => setEditMcp(defaultForm)}>
|
||||
<Button
|
||||
isDisabled={!userInfo?.permission.hasApikeyCreatePer}
|
||||
onClick={() => setEditMcp(defaultForm)}
|
||||
>
|
||||
{t('dashboard_mcp:create_mcp_server')}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
@@ -29,10 +29,8 @@ import {
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { authAppByTmbId } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import {
|
||||
type PluginDataType,
|
||||
type StoreNodeItemType
|
||||
} from '@fastgpt/global/core/workflow/type/node';
|
||||
import { type StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
export const getScheduleTriggerApp = async () => {
|
||||
// 1. Find all the app
|
||||
@@ -152,6 +150,7 @@ export const checkNode = async ({
|
||||
|
||||
try {
|
||||
const { source } = await splitCombinePluginId(pluginId);
|
||||
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
await authAppByTmbId({
|
||||
tmbId: ownerTmbId,
|
||||
@@ -176,8 +175,8 @@ export const checkNode = async ({
|
||||
return {
|
||||
...node,
|
||||
pluginData: {
|
||||
error
|
||||
} as PluginDataType
|
||||
error: getErrText(error)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@@ -25,7 +25,7 @@ import type {
|
||||
getPaginationRecordsBody,
|
||||
getPaginationRecordsResponse
|
||||
} from '@/pages/api/core/chat/getPaginationRecords';
|
||||
import type { GetQuoteDataProps, GetQuoteDataRes } from '@/pages/api/core/chat/quote/getQuote';
|
||||
import type { GetQuoteProps, GetQuotesRes } from '@/pages/api/core/chat/quote/getQuote';
|
||||
import type {
|
||||
GetCollectionQuoteProps,
|
||||
GetCollectionQuoteRes
|
||||
@@ -101,8 +101,8 @@ export const getMyTokensApps = (data: AuthTeamTagTokenProps) =>
|
||||
export const getinitTeamChat = (data: { teamId: string; authToken: string; appId: string }) =>
|
||||
GET(`/proApi/core/chat/initTeamChat`, data);
|
||||
|
||||
export const getQuoteDataList = (data: GetQuoteDataProps) =>
|
||||
POST<GetQuoteDataRes>(`/core/chat/quote/getQuote`, data);
|
||||
export const getQuoteDataList = (data: GetQuoteProps) =>
|
||||
POST<GetQuotesRes>(`/core/chat/quote/getQuote`, data);
|
||||
|
||||
export const getCollectionQuote = (data: GetCollectionQuoteProps) =>
|
||||
POST<GetCollectionQuoteRes>(`/core/chat/quote/getCollectionQuote`, data);
|
||||
|
@@ -34,13 +34,13 @@ type ChatBoxDataType = {
|
||||
};
|
||||
};
|
||||
|
||||
// 知识库引用相关 type
|
||||
export type GetQuoteDataBasicProps = {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
chatItemDataId: string;
|
||||
outLinkAuthData?: OutLinkChatAuthProps;
|
||||
};
|
||||
// 获取单个集合引用
|
||||
export type GetCollectionQuoteDataProps = GetQuoteDataBasicProps & {
|
||||
quoteId?: string;
|
||||
collectionId: string;
|
||||
@@ -54,11 +54,17 @@ export type GetAllQuoteDataProps = GetQuoteDataBasicProps & {
|
||||
sourceName?: string;
|
||||
};
|
||||
export type GetQuoteProps = GetAllQuoteDataProps | GetCollectionQuoteDataProps;
|
||||
|
||||
export type QuoteDataType = {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
metadata: GetQuoteProps;
|
||||
};
|
||||
export type OnOpenCiteModalProps = {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
quoteId?: string;
|
||||
};
|
||||
|
||||
type ChatItemContextType = {
|
||||
ChatBoxRef: React.RefObject<ChatComponentRef> | null;
|
||||
@@ -74,8 +80,8 @@ type ChatItemContextType = {
|
||||
setChatBoxData: React.Dispatch<React.SetStateAction<ChatBoxDataType>>;
|
||||
isPlugin: boolean;
|
||||
|
||||
quoteData?: QuoteDataType;
|
||||
setQuoteData: React.Dispatch<React.SetStateAction<QuoteDataType | undefined>>;
|
||||
datasetCiteData?: QuoteDataType;
|
||||
setCiteModalData: React.Dispatch<React.SetStateAction<QuoteDataType | undefined>>;
|
||||
isVariableVisible: boolean;
|
||||
setIsVariableVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
} & ContextProps;
|
||||
@@ -98,8 +104,8 @@ export const ChatItemContext = createContext<ChatItemContextType>({
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
|
||||
quoteData: undefined,
|
||||
setQuoteData: function (value: React.SetStateAction<QuoteDataType | undefined>): void {
|
||||
datasetCiteData: undefined,
|
||||
setCiteModalData: function (value: React.SetStateAction<QuoteDataType | undefined>): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
isVariableVisible: true,
|
||||
@@ -124,7 +130,6 @@ const ChatItemContextProvider = ({
|
||||
} & ContextProps) => {
|
||||
const ChatBoxRef = useRef<ChatComponentRef>(null);
|
||||
const variablesForm = useForm<ChatBoxInputFormType>();
|
||||
const [quoteData, setQuoteData] = useState<QuoteDataType>();
|
||||
const [isVariableVisible, setIsVariableVisible] = useState(true);
|
||||
|
||||
const [chatBoxData, setChatBoxData] = useState<ChatBoxDataType>({
|
||||
@@ -162,6 +167,8 @@ const ChatItemContextProvider = ({
|
||||
ChatBoxRef.current?.restartChat?.();
|
||||
}, [variablesForm]);
|
||||
|
||||
const [datasetCiteData, setCiteModalData] = useState<QuoteDataType>();
|
||||
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
chatBoxData,
|
||||
@@ -180,8 +187,8 @@ const ChatItemContextProvider = ({
|
||||
// isShowFullText,
|
||||
showNodeStatus,
|
||||
|
||||
quoteData,
|
||||
setQuoteData,
|
||||
datasetCiteData,
|
||||
setCiteModalData,
|
||||
isVariableVisible,
|
||||
setIsVariableVisible
|
||||
};
|
||||
@@ -198,8 +205,8 @@ const ChatItemContextProvider = ({
|
||||
isResponseDetail,
|
||||
// isShowFullText,
|
||||
showNodeStatus,
|
||||
quoteData,
|
||||
setQuoteData,
|
||||
datasetCiteData,
|
||||
setCiteModalData,
|
||||
isVariableVisible,
|
||||
setIsVariableVisible
|
||||
]);
|
||||
|
@@ -72,7 +72,7 @@ import type {
|
||||
getTrainingErrorResponse
|
||||
} from '@/pages/api/core/dataset/training/getTrainingError';
|
||||
import type { APIFileItem } from '@fastgpt/global/core/dataset/apiDataset';
|
||||
import type { GetQuoteDataProps } from '@/pages/api/core/chat/quote/getQuote';
|
||||
import type { GetQuoteDataProps } from '@/pages/api/core/dataset/data/getQuoteData';
|
||||
import type {
|
||||
GetApiDatasetCataLogResponse,
|
||||
GetApiDatasetCataLogProps
|
||||
|
Reference in New Issue
Block a user