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:
Archer
2025-05-15 15:51:34 +08:00
committed by GitHub
parent a6c80684d1
commit 4e83840c14
48 changed files with 721 additions and 642 deletions

View File

@@ -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}
/>
);
}

View File

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

View File

@@ -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}</>}

View File

@@ -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 })}

View File

@@ -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,

View File

@@ -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}
/>
);
}

View File

@@ -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>
)}

View File

@@ -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>
);

View File

@@ -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>
)}

View File

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

View File

@@ -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>
)}

View File

@@ -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} />

View File

@@ -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,

View File

@@ -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'

View File

@@ -81,4 +81,5 @@ function App({ Component, pageProps }: AppPropsWithLayout) {
);
}
// @ts-ignore
export default appWithTranslation(App);

View File

@@ -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);

View File

@@ -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 })]
},

View File

@@ -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,

View File

@@ -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([

View File

@@ -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>
)}

View File

@@ -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>
)}

View File

@@ -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>
)}

View File

@@ -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>

View File

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

View File

@@ -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);

View File

@@ -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
]);

View File

@@ -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