v4.6.2-alpah (#511)

This commit is contained in:
Archer
2023-11-24 15:29:43 +08:00
committed by GitHub
parent 60f752629f
commit 9cb4280a16
208 changed files with 5396 additions and 3500 deletions

View File

@@ -3,7 +3,7 @@ import { Image } from '@chakra-ui/react';
import type { ImageProps } from '@chakra-ui/react';
import { LOGO_ICON } from '@fastgpt/global/core/chat/constants';
const Avatar = ({ w = '30px', ...props }: ImageProps) => {
const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
return (
<Image
fallbackSrc={LOGO_ICON}
@@ -14,6 +14,7 @@ const Avatar = ({ w = '30px', ...props }: ImageProps) => {
w={w}
h={w}
p={'1px'}
src={src || LOGO_ICON}
{...props}
/>
);

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
import { ModalBody, Box, useTheme, Flex, Image } from '@chakra-ui/react';
import { ChatItemType } from '@fastgpt/global/core/chat/type';
import MyModal from '../MyModal';
@@ -16,13 +16,13 @@ const ContextModal = ({
<MyModal
isOpen={true}
onClose={onClose}
iconSrc="/imgs/modal/chatHistory.svg"
title={`完整对话记录(${context.length}条)`}
h={['90vh', '80vh']}
minW={['90vw', '600px']}
isCentered
>
<ModalBody
pt={0}
whiteSpace={'pre-wrap'}
textAlign={'justify'}
wordBreak={'break-all'}

View File

@@ -33,7 +33,12 @@ const FeedbackModal = ({
});
return (
<MyModal isOpen={true} onClose={onClose} title={t('chat.Feedback Modal')}>
<MyModal
isOpen={true}
onClose={onClose}
iconSrc="/imgs/modal/badAnswer.svg"
title={t('chat.Feedback Modal')}
>
<ModalBody>
<Textarea
ref={ref}

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react';
import { ModalBody, Box, useTheme, Flex, Progress, Link } from '@chakra-ui/react';
import { ModalBody, Box, useTheme, Flex, Progress, Link, Image } from '@chakra-ui/react';
import { getDatasetDataItemById } from '@/web/core/dataset/api';
import { useLoading } from '@/web/common/hooks/useLoading';
import { useToast } from '@/web/common/hooks/useToast';
@@ -68,6 +68,7 @@ const QuoteModal = ({
h={['90vh', '80vh']}
isCentered
minW={['90vw', '600px']}
iconSrc="/imgs/modal/quote.svg"
title={
<Box>
({rawSearch.length})
@@ -77,7 +78,7 @@ const QuoteModal = ({
</Box>
}
>
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} wordBreak={'break-all'}>
<ModalBody whiteSpace={'pre-wrap'} textAlign={'justify'} wordBreak={'break-all'}>
{rawSearch.map((item, i) => (
<Box
key={i}

View File

@@ -36,7 +36,12 @@ const ReadFeedbackModal = ({
});
return (
<MyModal isOpen={true} onClose={onClose} title={t('chat.Feedback Modal')}>
<MyModal
isOpen={true}
onClose={onClose}
iconSrc="/imgs/modal/readFeedback.svg"
title={t('chat.Feedback Modal')}
>
<ModalBody>{content}</ModalBody>
<ModalFooter>
<Button mr={2} isLoading={isLoading} variant={'base'} onClick={mutate}>

View File

@@ -35,7 +35,7 @@ const SelectMarkCollection = ({
const theme = useTheme();
const [selectedDatasetId, setSelectedDatasetId] = useState<string>();
const [selectedDatasetCollectionIds, setSelectedDatasetCollectionIds] = useState<string[]>([]);
const { paths, parentId, setParentId, datasets, isLoading } = useDatasetSelect();
const { paths, parentId, setParentId, datasets, isFetching } = useDatasetSelect();
return (
<>
@@ -107,7 +107,7 @@ const SelectMarkCollection = ({
</ModalBody>
<ModalFooter>
<Button
isLoading={isLoading}
isLoading={isFetching}
isDisabled={!selectedDatasetId}
onClick={() => {
setAdminMarkData({ ...adminMarkData, datasetId: selectedDatasetId });

View File

@@ -2,31 +2,33 @@ import React, { useMemo, useState } from 'react';
import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
import { useTranslation } from 'next-i18next';
import { ModuleTemplatesFlat } from '@/constants/flow/ModuleTemplate';
import { moduleTemplatesFlat } from '@/web/core/modules/template/system';
import Tabs from '../Tabs';
import MyModal from '../MyModal';
import MyTooltip from '../MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
import Markdown from '../Markdown';
function Row({ label, value }: { label: string; value?: string | number | React.ReactNode }) {
function Row({ label, value }: { label: string; value?: string | number }) {
const theme = useTheme();
const strValue = `${value}`;
const isCodeBlock = strValue.startsWith('~~~json');
return value !== undefined && value !== '' && value !== 'undefined' ? (
<Box mb={2}>
<Box fontSize={['sm', 'md']} mb={1} flex={'0 0 90px'}>
<Box mb={3}>
<Box fontSize={['sm', 'md']} mb={isCodeBlock ? 0 : 1} flex={'0 0 90px'}>
{label}:
</Box>
<Box
borderRadius={'lg'}
border={theme.borders.base}
px={3}
py={1}
position={'relative'}
whiteSpace={'pre-wrap'}
borderRadius={'md'}
fontSize={'sm'}
{...(isCodeBlock
? { transform: 'translateY(-3px)' }
: { px: 3, py: 1, border: theme.borders.base })}
>
{value}
<Markdown source={strValue} />
</Box>
</Box>
) : null;
@@ -39,7 +41,6 @@ const WholeResponseModal = ({
response: ChatHistoryItemResType[];
onClose: () => void;
}) => {
const theme = useTheme();
const { t } = useTranslation();
const list = useMemo(
@@ -51,7 +52,8 @@ const WholeResponseModal = ({
mr={2}
src={
item.moduleLogo ||
ModuleTemplatesFlat.find((template) => item.moduleType === template.flowType)?.logo
moduleTemplatesFlat.find((template) => item.moduleType === template.flowType)
?.avatar
}
alt={''}
w={['14px', '16px']}
@@ -75,6 +77,7 @@ const WholeResponseModal = ({
onClose={onClose}
h={['90vh', '80vh']}
w={['90vw', '500px']}
iconSrc="/imgs/modal/wholeRecord.svg"
title={
<Flex alignItems={'center'}>
{t('chat.Complete Response')}
@@ -102,37 +105,26 @@ const WholeResponseModal = ({
/>
<Row label={t('chat.response.module tokens')} value={`${activeModule?.tokens}`} />
<Row label={t('chat.response.module model')} value={activeModule?.model} />
<Row label={t('chat.response.module query')} value={activeModule?.query} />
{/* ai chat */}
<Row label={t('chat.response.module question')} value={activeModule?.question} />
<Row label={t('chat.response.module temperature')} value={activeModule?.temperature} />
<Row label={t('chat.response.module maxToken')} value={activeModule?.maxToken} />
<Row
label={t('chat.response.module quoteList')}
value={(() => {
try {
JSON.stringify(activeModule.quoteList, null, 2);
} catch (error) {
return '';
}
})()}
/>
<Row
label={t('chat.response.module historyPreview')}
value={(() => {
if (!activeModule?.historyPreview) return '';
return (
<>
{activeModule.historyPreview.map((item, i) => (
<Box key={i} _notLast={{ mb: 3, borderBottom: theme.borders.base }} pb={3}>
<Box fontWeight={'bold'}>{item.obj}</Box>
<Box>{item.value}</Box>
</Box>
))}
</>
);
return activeModule.historyPreview
.map((item, i) => `**${item.obj}**\n${item.value}`)
.join('\n---\n');
})()}
/>
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
<Row
label={t('chat.response.module quoteList')}
value={`~~~json\n${JSON.stringify(activeModule.quoteList, null, 2)}`}
/>
)}
{/* dataset search */}
<Row label={t('chat.response.module similarity')} value={activeModule?.similarity} />
@@ -143,15 +135,7 @@ const WholeResponseModal = ({
label={t('chat.response.module cq')}
value={(() => {
if (!activeModule?.cqList) return '';
return (
<Box as={'ol'} px={3}>
{activeModule.cqList.map((item) => (
<Box key={item.key} as={'li'}>
{item.value}
</Box>
))}
</Box>
);
return activeModule.cqList.map((item) => `* ${item.value}`).join('\n');
})()}
/>
<Row label={t('chat.response.module cq result')} value={activeModule?.cqResult} />
@@ -161,50 +145,34 @@ const WholeResponseModal = ({
label={t('chat.response.module extract description')}
value={activeModule?.extractDescription}
/>
<Row
label={t('chat.response.module extract result')}
value={(() => {
try {
return JSON.stringify(activeModule?.extractResult, null, 2);
} catch (error) {
return '';
}
})()}
/>
{activeModule?.extractResult && (
<Row
label={t('chat.response.module extract result')}
value={`~~~json\n${JSON.stringify(activeModule?.extractResult, null, 2)}`}
/>
)}
{/* http */}
<Row
label={t('chat.response.module http body')}
value={(() => {
try {
return JSON.stringify(activeModule?.body, null, 2);
} catch (error) {
return '';
}
})()}
/>
<Row
label={t('chat.response.module http result')}
value={(() => {
try {
return JSON.stringify(activeModule?.httpResult, null, 2);
} catch (error) {
return '';
}
})()}
/>
{activeModule?.body && (
<Row
label={t('chat.response.module http body')}
value={`~~~json\n${JSON.stringify(activeModule?.body, null, 2)}`}
/>
)}
{activeModule?.httpResult && (
<Row
label={t('chat.response.module http result')}
value={`~~~json\n${JSON.stringify(activeModule?.httpResult, null, 2)}`}
/>
)}
{/* plugin */}
<Row
label={t('chat.response.plugin output')}
value={(() => {
try {
return JSON.stringify(activeModule?.pluginOutput, null, 2);
} catch (error) {
return '';
}
})()}
/>
{activeModule?.pluginOutput && (
<Row
label={t('chat.response.plugin output')}
value={`~~~json\n${JSON.stringify(activeModule?.pluginOutput, null, 2)}`}
/>
)}
</Box>
</Flex>
</MyModal>

View File

@@ -33,14 +33,13 @@ import { eventBus } from '@/web/common/utils/eventbus';
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
import { useMarkdown } from '@/web/common/hooks/useMarkdown';
import { ModuleItemType } from '@fastgpt/global/core/module/type.d';
import { VariableInputEnum } from '@/constants/app';
import { VariableInputEnum } from '@fastgpt/global/core/module/constants';
import { useForm } from 'react-hook-form';
import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
import { fileDownload } from '@/web/common/file/utils';
import { htmlTemplate } from '@/constants/common';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants';
import { useTranslation } from 'next-i18next';
import { customAlphabet } from 'nanoid';
import { adminUpdateChatFeedback, userUpdateChatFeedback } from '@/web/core/chat/api';
@@ -60,9 +59,10 @@ const SelectMarkCollection = dynamic(() => import('./SelectMarkCollection'));
import styles from './index.module.scss';
import { postQuestionGuide } from '@/web/core/ai/api';
import { splitGuideModule } from '@/global/core/app/modules/utils';
import { AppTTSConfigType } from '@/types/app';
import { splitGuideModule } from '@fastgpt/global/core/module/utils';
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
import MessageInput from './MessageInput';
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
@@ -105,7 +105,7 @@ type Props = {
onUpdateVariable?: (e: Record<string, any>) => void;
onStartChat?: (e: StartChatFnProps) => Promise<{
responseText: string;
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[];
[ModuleOutputKeyEnum.responseData]: ChatHistoryItemResType[];
isNewChat?: boolean;
}>;
onDelMessage?: (e: { contentId?: string; index: number }) => void;
@@ -760,6 +760,9 @@ const ChatBox = (
variant={'outline'}
colorScheme={'gray'}
size={'xs'}
whiteSpace={'pre-wrap'}
h={'auto'}
py={1}
onClick={() => {
resetInputVal(item);
}}

View File

@@ -13,7 +13,12 @@ const md = `
const CommunityModal = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation();
return (
<MyModal isOpen={true} onClose={onClose} title={t('home.Community')}>
<MyModal
isOpen={true}
onClose={onClose}
iconSrc="/imgs/modal/concat.svg"
title={t('home.Community')}
>
<ModalBody textAlign={'center'}>
<Markdown source={md} />
</ModalBody>

View File

@@ -8,6 +8,7 @@ const unAuthPage: { [key: string]: boolean } = {
'/': true,
'/login': true,
'/login/provider': true,
'/login/fastlogin': true,
'/appStore': true,
'/chat/share': true
};

View File

@@ -22,6 +22,7 @@ const pcUnShowLayoutRoute: Record<string, boolean> = {
'/': true,
'/login': true,
'/login/provider': true,
'/login/fastlogin': true,
'/chat/share': true,
'/app/edit': true,
'/chat': true
@@ -30,6 +31,7 @@ const phoneUnShowLayoutRoute: Record<string, boolean> = {
'/': true,
'/login': true,
'/login/provider': true,
'/login/fastlogin': true,
'/chat/share': true
};

View File

@@ -6,10 +6,12 @@ import {
ModalHeader,
ModalCloseButton,
ModalContentProps,
Box
Box,
Image
} from '@chakra-ui/react';
interface Props extends ModalContentProps {
iconSrc?: string;
title?: any;
isCentered?: boolean;
isOpen: boolean;
@@ -19,6 +21,7 @@ interface Props extends ModalContentProps {
const MyModal = ({
isOpen,
onClose,
iconSrc,
title,
children,
isCentered,
@@ -51,8 +54,9 @@ const MyModal = ({
background={'#FBFBFC'}
borderBottom={'1px solid #F4F6F8'}
roundedTop={'lg'}
py={3}
py={'10px'}
>
{iconSrc && <Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />}
{title}
<Box flex={1} />
{onClose && <ModalCloseButton position={'relative'} top={0} right={0} />}

View File

@@ -19,7 +19,7 @@ const PromptTemplate = ({
const [selectTemplateTitle, setSelectTemplateTitle] = useState<PromptTemplateItem>();
return (
<MyModal isOpen title={title} onClose={onClose} isCentered>
<MyModal isOpen title={title} onClose={onClose} iconSrc="/imgs/modal/prompt.svg">
<ModalBody h="100%" w={'600px'} maxW={'90vw'} overflowY={'auto'}>
<Grid gridTemplateColumns={['1fr', '1fr 1fr']} gridGap={4}>
{templates.map((item) => (

View File

@@ -4,8 +4,7 @@ import { useQuery } from '@tanstack/react-query';
import React, { Dispatch, useMemo, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { Box, Flex, ModalHeader } from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import { Box, Flex } from '@chakra-ui/react';
import ParentPaths from '@/components/common/ParentPaths';
type PathItemType = {
@@ -29,12 +28,12 @@ const DatasetSelectContainer = ({
children: React.ReactNode;
}) => {
const { t } = useTranslation();
const { isPc } = useSystemStore();
return (
<MyModal isOpen={isOpen} onClose={onClose} w={'100%'} maxW={['90vw', '900px']} isCentered>
<Flex flexDirection={'column'} h={'90vh'}>
<ModalHeader fontWeight={'normal'}>
<MyModal
iconSrc="/imgs/module/db.png"
title={
<Box fontWeight={'normal'}>
<ParentPaths
paths={paths.map((path, i) => ({
parentId: path.parentId,
@@ -50,7 +49,15 @@ const DatasetSelectContainer = ({
{tips}
</Box>
)}
</ModalHeader>
</Box>
}
isOpen={isOpen}
onClose={onClose}
w={'100%'}
maxW={['90vw', '900px']}
isCentered
>
<Flex flexDirection={'column'} h={'90vh'}>
<Box flex={'1 0 0'}>{children}</Box>
</Flex>
</MyModal>
@@ -58,10 +65,9 @@ const DatasetSelectContainer = ({
};
export function useDatasetSelect() {
const { t } = useTranslation();
const [parentId, setParentId] = useState<string>();
const [parentId, setParentId] = useState<string>('');
const { data, isLoading } = useQuery(['loadDatasetData', parentId], () =>
const { data, isFetching } = useQuery(['loadDatasetData', parentId], () =>
Promise.all([getDatasets({ parentId }), getDatasetPaths(parentId)])
);
@@ -72,7 +78,7 @@ export function useDatasetSelect() {
setParentId,
datasets: data?.[0] || [],
paths,
isLoading
isFetching
};
}

View File

@@ -1,13 +1,13 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'next-i18next';
import { EditFormType } from '@/web/core/app/basicSettings';
import { useForm } from 'react-hook-form';
import {
Box,
BoxProps,
Button,
Flex,
Image,
Link,
ModalBody,
ModalFooter,
@@ -19,9 +19,12 @@ import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Prompt_QuotePromptList, Prompt_QuoteTemplateList } from '@/global/core/prompt/AIChat';
import { chatModelList, feConfigs } from '@/web/common/system/staticData';
import MySlider from '@/components/Slider';
import { SystemInputEnum } from '@/constants/app';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import dynamic from 'next/dynamic';
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d';
import type { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type.d';
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
const PromptTemplate = dynamic(() => import('@/components/PromptTemplate'));
@@ -29,12 +32,14 @@ const AIChatSettingsModal = ({
isAdEdit,
onClose,
onSuccess,
defaultData
defaultData,
simpleModeTemplate = SimpleModeTemplate_FastGPT_Universal
}: {
isAdEdit?: boolean;
onClose: () => void;
onSuccess: (e: EditFormType['chatModel']) => void;
defaultData: EditFormType['chatModel'];
onSuccess: (e: AIChatModuleProps) => void;
defaultData: AIChatModuleProps;
simpleModeTemplate?: AppSimpleEditConfigTemplateType;
}) => {
const { t } = useTranslation();
const [refresh, setRefresh] = useState(false);
@@ -49,7 +54,10 @@ const AIChatSettingsModal = ({
}>();
const tokenLimit = useMemo(() => {
return chatModelList.find((item) => item.model === getValues('model'))?.maxResponse || 4000;
return (
chatModelList.find((item) => item.model === getValues(ModuleInputKeyEnum.aiModel))
?.maxResponse || 4000
);
}, [getValues, refresh]);
const LabelStyles: BoxProps = {
@@ -63,8 +71,9 @@ const AIChatSettingsModal = ({
return (
<MyModal
isOpen
iconSrc="/imgs/module/AI.png"
title={
<Flex alignItems={'flex-end'}>
<>
{t('app.AI Advanced Settings')}
{feConfigs?.docUrl && (
<Link
@@ -78,13 +87,13 @@ const AIChatSettingsModal = ({
</Link>
)}
</Flex>
</>
}
isCentered
w={'700px'}
h={['90vh', 'auto']}
>
<ModalBody flex={['1 0 0', 'auto']} overflowY={'auto'}>
<ModalBody flex={['1 0 0', 'auto']} overflowY={'auto'} minH={'40vh'}>
{isAdEdit && (
<Flex alignItems={'center'}>
<Box {...LabelStyles} w={'80px'}>
@@ -92,112 +101,123 @@ const AIChatSettingsModal = ({
</Box>
<Box flex={1} ml={'10px'}>
<Switch
isChecked={getValues(SystemInputEnum.isResponseAnswerText)}
isChecked={getValues(ModuleInputKeyEnum.aiChatIsResponseText)}
size={'lg'}
onChange={(e) => {
const value = e.target.checked;
setValue(SystemInputEnum.isResponseAnswerText, value);
setValue(ModuleInputKeyEnum.aiChatIsResponseText, value);
setRefresh((state) => !state);
}}
/>
</Box>
</Flex>
)}
<Flex alignItems={'center'} mb={10} mt={isAdEdit ? 8 : 5}>
<Box {...LabelStyles} mr={2} w={'80px'}>
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '严谨', value: 0 },
{ label: '发散', value: 10 }
]}
width={'95%'}
min={0}
max={10}
value={getValues('temperature')}
onChange={(e) => {
setValue('temperature', e);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
<Flex alignItems={'center'} mt={12} mb={10}>
<Box {...LabelStyles} mr={2} w={'80px'}>
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '100', value: 100 },
{ label: `${tokenLimit}`, value: tokenLimit }
]}
width={'95%'}
min={100}
max={tokenLimit}
step={50}
value={getValues('maxToken')}
onChange={(val) => {
setValue('maxToken', val);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
<Box>
<Flex {...LabelStyles} mb={1}>
<MyTooltip
label={t('template.Quote Content Tip', {
default: Prompt_QuoteTemplateList[0].value
})}
forceShow
>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<Box
{...selectTemplateBtn}
onClick={() =>
setSelectTemplateData({
title: '选择知识库提示词模板',
templates: Prompt_QuoteTemplateList
})
}
>
{simpleModeTemplate?.systemForm?.aiSettings?.temperature && (
<Flex alignItems={'center'} mb={10} mt={isAdEdit ? 8 : 5}>
<Box {...LabelStyles} mr={2} w={'80px'}>
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '严谨', value: 0 },
{ label: '发散', value: 10 }
]}
width={'95%'}
min={0}
max={10}
value={getValues(ModuleInputKeyEnum.aiChatTemperature)}
onChange={(e) => {
setValue(ModuleInputKeyEnum.aiChatTemperature, e);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
<Textarea
rows={6}
placeholder={
t('template.Quote Content Tip', { default: Prompt_QuoteTemplateList[0].value }) || ''
}
borderColor={'myGray.100'}
{...register('quoteTemplate')}
/>
</Box>
<Box mt={4}>
<Flex {...LabelStyles} mb={1}>
<MyTooltip
label={t('template.Quote Prompt Tip', { default: Prompt_QuotePromptList[0].value })}
forceShow
>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
)}
{simpleModeTemplate?.systemForm?.aiSettings?.maxToken && (
<Flex alignItems={'center'} mt={12} mb={10}>
<Box {...LabelStyles} mr={2} w={'80px'}>
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '100', value: 100 },
{ label: `${tokenLimit}`, value: tokenLimit }
]}
width={'95%'}
min={100}
max={tokenLimit}
step={50}
value={getValues(ModuleInputKeyEnum.aiChatMaxToken)}
onChange={(val) => {
setValue(ModuleInputKeyEnum.aiChatMaxToken, val);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
<Textarea
rows={11}
placeholder={
t('template.Quote Prompt Tip', { default: Prompt_QuotePromptList[0].value }) || ''
}
borderColor={'myGray.100'}
{...register('quotePrompt')}
/>
</Box>
)}
{simpleModeTemplate?.systemForm?.aiSettings?.quoteTemplate && (
<Box>
<Flex {...LabelStyles} mb={1}>
<MyTooltip
label={t('template.Quote Content Tip', {
default: Prompt_QuoteTemplateList[0].value
})}
forceShow
>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<Box
{...selectTemplateBtn}
onClick={() =>
setSelectTemplateData({
title: '选择知识库提示词模板',
templates: Prompt_QuoteTemplateList
})
}
>
</Box>
</Flex>
<Textarea
rows={6}
placeholder={
t('template.Quote Content Tip', { default: Prompt_QuoteTemplateList[0].value }) ||
''
}
borderColor={'myGray.100'}
{...register(ModuleInputKeyEnum.aiChatQuoteTemplate)}
/>
</Box>
)}
{simpleModeTemplate?.systemForm?.aiSettings?.quotePrompt && (
<Box mt={4}>
<Flex {...LabelStyles} mb={1}>
<MyTooltip
label={t('template.Quote Prompt Tip', { default: Prompt_QuotePromptList[0].value })}
forceShow
>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
</Flex>
<Textarea
rows={11}
placeholder={
t('template.Quote Prompt Tip', { default: Prompt_QuotePromptList[0].value }) || ''
}
borderColor={'myGray.100'}
{...register(ModuleInputKeyEnum.aiChatQuotePrompt)}
/>
</Box>
)}
</ModalBody>
<ModalFooter>
<Button variant={'base'} onClick={onClose}>
@@ -215,8 +235,8 @@ const AIChatSettingsModal = ({
onSuccess={(e) => {
const quoteVal = e.value;
const promptVal = Prompt_QuotePromptList.find((item) => item.title === e.title)?.value;
setValue('quoteTemplate', quoteVal);
setValue('quotePrompt', promptVal);
setValue(ModuleInputKeyEnum.aiChatQuoteTemplate, quoteVal);
setValue(ModuleInputKeyEnum.aiChatQuotePrompt, promptVal);
}}
/>
)}

View File

@@ -10,7 +10,8 @@ import {
Textarea,
Grid,
Divider,
Switch
Switch,
Image
} from '@chakra-ui/react';
import Avatar from '@/components/Avatar';
import { useForm } from 'react-hook-form';
@@ -28,47 +29,44 @@ import { feConfigs } from '@/web/common/system/staticData';
import DatasetSelectContainer, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
import { useLoading } from '@/web/common/hooks/useLoading';
import EmptyTip from '@/components/EmptyTip';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
export type KbParamsType = {
searchSimilarity: number;
searchLimit: number;
searchEmptyText: string;
rerank: boolean;
};
type DatasetParamsProps = AppSimpleEditFormType['dataset'];
export const DatasetSelectModal = ({
isOpen,
activeDatasets = [],
defaultSelectedDatasets = [],
onChange,
onClose
}: {
isOpen: boolean;
activeDatasets: SelectedDatasetType;
defaultSelectedDatasets: SelectedDatasetType;
onChange: (e: SelectedDatasetType) => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const { allDatasets } = useDatasetStore();
const [selectedKbList, setSelectedKbList] = useState<SelectedDatasetType>(
activeDatasets.filter((dataset) => {
const [selectedDatasets, setSelectedDatasets] = useState<SelectedDatasetType>(
defaultSelectedDatasets.filter((dataset) => {
return allDatasets.find((item) => item._id === dataset.datasetId);
})
);
const { toast } = useToast();
const { paths, setParentId, datasets, isLoading } = useDatasetSelect();
const { paths, setParentId, datasets, isFetching } = useDatasetSelect();
const { Loading } = useLoading();
const filterKbList = useMemo(() => {
const filterDatasets = useMemo(() => {
return {
selected: allDatasets.filter((item) =>
selectedKbList.find((dataset) => dataset.datasetId === item._id)
selectedDatasets.find((dataset) => dataset.datasetId === item._id)
),
unSelected: datasets.filter(
(item) => !selectedKbList.find((dataset) => dataset.datasetId === item._id)
(item) => !selectedDatasets.find((dataset) => dataset.datasetId === item._id)
)
};
}, [datasets, allDatasets, selectedKbList]);
}, [datasets, allDatasets, selectedDatasets]);
return (
<DatasetSelectContainer
@@ -88,7 +86,7 @@ export const DatasetSelectModal = ({
]}
gridGap={3}
>
{filterKbList.selected.map((item) =>
{filterDatasets.selected.map((item) =>
(() => {
return (
<Card
@@ -109,7 +107,7 @@ export const DatasetSelectModal = ({
cursor={'pointer'}
_hover={{ color: 'red.500' }}
onClick={() => {
setSelectedKbList((state) =>
setSelectedDatasets((state) =>
state.filter((kb) => kb.datasetId !== item._id)
);
}}
@@ -121,7 +119,7 @@ export const DatasetSelectModal = ({
)}
</Grid>
{filterKbList.selected.length > 0 && <Divider my={3} />}
{filterDatasets.selected.length > 0 && <Divider my={3} />}
<Grid
gridTemplateColumns={[
@@ -131,7 +129,7 @@ export const DatasetSelectModal = ({
]}
gridGap={3}
>
{filterKbList.unSelected.map((item) =>
{filterDatasets.unSelected.map((item) =>
(() => {
return (
<MyTooltip
@@ -155,7 +153,7 @@ export const DatasetSelectModal = ({
if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id);
} else if (item.type === DatasetTypeEnum.dataset) {
const vectorModel = selectedKbList[0]?.vectorModel?.model;
const vectorModel = selectedDatasets[0]?.vectorModel?.model;
if (vectorModel && vectorModel !== item.vectorModel.model) {
return toast({
@@ -163,7 +161,7 @@ export const DatasetSelectModal = ({
title: t('dataset.Select Dataset Tips')
});
}
setSelectedKbList((state) => [
setSelectedDatasets((state) => [
...state,
{ datasetId: item._id, vectorModel: item.vectorModel }
]);
@@ -199,26 +197,26 @@ export const DatasetSelectModal = ({
})()
)}
</Grid>
{filterKbList.unSelected.length === 0 && <EmptyTip text={t('common.folder.empty')} />}
{filterDatasets.unSelected.length === 0 && <EmptyTip text={t('common.folder.empty')} />}
</ModalBody>
<ModalFooter>
<Button
onClick={() => {
// filter out the dataset that is not in the kList
const filterKbList = selectedKbList.filter((dataset) => {
const filterDatasets = selectedDatasets.filter((dataset) => {
return allDatasets.find((item) => item._id === dataset.datasetId);
});
onClose();
onChange(filterKbList);
onChange(filterDatasets);
}}
>
{t('common.Done')}
</Button>
</ModalFooter>
<Loading fixed={false} loading={isLoading} />
<Loading fixed={false} loading={isFetching} />
</Flex>
</DatasetSelectContainer>
);
@@ -226,24 +224,30 @@ export const DatasetSelectModal = ({
export const DatasetParamsModal = ({
searchEmptyText,
searchLimit,
searchSimilarity,
limit,
similarity,
rerank,
onClose,
onChange
}: KbParamsType & { onClose: () => void; onChange: (e: KbParamsType) => void }) => {
}: DatasetParamsProps & { onClose: () => void; onChange: (e: DatasetParamsProps) => void }) => {
const [refresh, setRefresh] = useState(false);
const { register, setValue, getValues, handleSubmit } = useForm<KbParamsType>({
const { register, setValue, getValues, handleSubmit } = useForm<DatasetParamsProps>({
defaultValues: {
searchEmptyText,
searchLimit,
searchSimilarity,
limit,
similarity,
rerank
}
});
return (
<MyModal isOpen={true} onClose={onClose} title={'搜索参数调整'} minW={['90vw', '600px']}>
<MyModal
isOpen={true}
onClose={onClose}
iconSrc="/imgs/modal/params.svg"
title={'搜索参数调整'}
minW={['90vw', '600px']}
>
<Flex flexDirection={'column'}>
<ModalBody>
{feConfigs?.isPlus && (
@@ -256,9 +260,9 @@ export const DatasetParamsModal = ({
</Box>
<Switch
size={'lg'}
isChecked={getValues('rerank')}
isChecked={getValues(ModuleInputKeyEnum.datasetStartReRank)}
onChange={(e) => {
setValue('rerank', e.target.checked);
setValue(ModuleInputKeyEnum.datasetStartReRank, e.target.checked);
setRefresh(!refresh);
}}
/>
@@ -282,9 +286,9 @@ export const DatasetParamsModal = ({
min={0}
max={1}
step={0.01}
value={getValues('searchSimilarity')}
value={getValues(ModuleInputKeyEnum.datasetSimilarity)}
onChange={(val) => {
setValue('searchSimilarity', val);
setValue(ModuleInputKeyEnum.datasetSimilarity, val);
setRefresh(!refresh);
}}
/>
@@ -301,9 +305,9 @@ export const DatasetParamsModal = ({
]}
min={1}
max={20}
value={getValues('searchLimit')}
value={getValues(ModuleInputKeyEnum.datasetLimit)}
onChange={(val) => {
setValue('searchLimit', val);
setValue(ModuleInputKeyEnum.datasetLimit, val);
setRefresh(!refresh);
}}
/>

View File

@@ -15,7 +15,7 @@ import { streamFetch } from '@/web/common/api/fetch';
import MyTooltip from '@/components/MyTooltip';
import { useUserStore } from '@/web/support/user/useUserStore';
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
import { getGuideModule } from '@/global/core/app/modules/utils';
import { getGuideModule } from '@fastgpt/global/core/module/utils';
import { checkChatSupportSelectFileByModules } from '@/web/core/chat/utils';
export type ChatTestComponentRef = {

View File

@@ -28,11 +28,8 @@ import React, {
import { customAlphabet } from 'nanoid';
import { appModule2FlowEdge, appModule2FlowNode } from '@/utils/adapt';
import { useToast } from '@/web/common/hooks/useToast';
import {
FlowNodeInputTypeEnum,
FlowNodeTypeEnum,
FlowNodeValTypeEnum
} from '@fastgpt/global/core/module/node/constant';
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
import { useTranslation } from 'next-i18next';
import { ModuleItemType } from '@fastgpt/global/core/module/type.d';
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
@@ -42,6 +39,7 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
export type useFlowProviderStoreType = {
reactFlowWrapper: null | React.RefObject<HTMLDivElement>;
mode: 'app' | 'plugin';
filterAppIds: string[];
nodes: Node<FlowModuleItemType, string | undefined>[];
setNodes: Dispatch<SetStateAction<Node<FlowModuleItemType, string | undefined>[]>>;
@@ -66,6 +64,7 @@ export type useFlowProviderStoreType = {
const StateContext = createContext<useFlowProviderStoreType>({
reactFlowWrapper: null,
mode: 'app',
filterAppIds: [],
nodes: [],
setNodes: function (
@@ -118,9 +117,11 @@ const StateContext = createContext<useFlowProviderStoreType>({
export const useFlowProviderStore = () => useContext(StateContext);
export const FlowProvider = ({
mode,
filterAppIds = [],
children
}: {
mode: useFlowProviderStoreType['mode'];
filterAppIds?: string[];
children: React.ReactNode;
}) => {
@@ -173,7 +174,7 @@ export const FlowProvider = ({
const source = nodes.find((node) => node.id === connect.source)?.data;
const sourceType = (() => {
if (source?.flowType === FlowNodeTypeEnum.classifyQuestion) {
return FlowNodeValTypeEnum.boolean;
return ModuleDataTypeEnum.boolean;
}
if (source?.flowType === FlowNodeTypeEnum.pluginInput) {
return source?.inputs.find((input) => input.key === connect.sourceHandle)?.valueType;
@@ -192,8 +193,8 @@ export const FlowProvider = ({
});
}
if (
sourceType !== FlowNodeValTypeEnum.any &&
targetType !== FlowNodeValTypeEnum.any &&
sourceType !== ModuleDataTypeEnum.any &&
targetType !== ModuleDataTypeEnum.any &&
sourceType !== targetType
) {
return toast({
@@ -328,10 +329,9 @@ export const FlowProvider = ({
const node = nodes.find((node) => node.id === nodeId);
if (!node) return nodes;
const template = {
logo: node.data.logo,
avatar: node.data.avatar,
name: node.data.name,
intro: node.data.intro,
description: node.data.description,
flowType: node.data.flowType,
inputs: node.data.inputs,
outputs: node.data.outputs,
@@ -407,6 +407,7 @@ export const FlowProvider = ({
const value = {
reactFlowWrapper,
mode,
filterAppIds,
nodes,
setNodes,
@@ -444,13 +445,13 @@ export function flowNode2Modules({
const modules: ModuleItemType[] = nodes.map((item) => ({
moduleId: item.data.moduleId,
name: item.data.name,
logo: item.data.logo,
avatar: item.data.avatar,
flowType: item.data.flowType,
showStatus: item.data.showStatus,
position: item.position,
inputs: item.data.inputs.map((item) => ({
...item,
connected: item.connected ?? item.type !== FlowNodeInputTypeEnum.target
connected: Boolean(item.value ?? item.connected ?? item.type !== FlowNodeInputTypeEnum.target)
})),
outputs: item.data.outputs.map((item) => ({
...item,

View File

@@ -12,7 +12,13 @@ const ImportSettings = ({ onClose }: { onClose: () => void }) => {
const { setNodes, setEdges, initData } = useFlowProviderStore();
return (
<MyModal isOpen w={'600px'} onClose={onClose} title={t('app.Import Config')}>
<MyModal
isOpen
w={'600px'}
onClose={onClose}
iconSrc="/imgs/modal/params.svg"
title={t('app.Import Configs')}
>
<ModalBody>
<Textarea
placeholder={t('app.Paste Config') || 'app.Paste Config'}
@@ -38,7 +44,7 @@ const ImportSettings = ({ onClose }: { onClose: () => void }) => {
onClose();
} catch (error) {
toast({
title: t('app.Import Config Failed')
title: t('app.Import Configs Failed')
});
}
}}

View File

@@ -1,8 +1,17 @@
import React, { useCallback, useMemo } from 'react';
import { Box, Flex } from '@chakra-ui/react';
import {
Box,
Flex,
Accordion,
AccordionItem,
AccordionButton,
AccordionPanel,
AccordionIcon,
useTheme
} from '@chakra-ui/react';
import type {
FlowModuleTemplateType,
SystemModuleTemplateType
moduleTemplateListType
} from '@fastgpt/global/core/module/type.d';
import { useViewport, XYPosition } from 'reactflow';
import { useSystemStore } from '@/web/common/system/useSystemStore';
@@ -16,32 +25,31 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
import MyIcon from '@/components/Icon';
import EmptyTip from '@/components/EmptyTip';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { getPluginModuleDetail } from '@/web/core/plugin/api';
import { getPreviewPluginModule } from '@/web/core/plugin/api';
import { useToast } from '@/web/common/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { moduleTemplatesList } from '@/web/core/modules/template/system';
import { ModuleTemplateTypeEnum } from '@fastgpt/global/core/module/constants';
enum TemplateTypeEnum {
system = 'system',
combine = 'combine'
plugin = 'plugin'
}
export type ModuleTemplateProps = {
systemTemplates: SystemModuleTemplateType;
pluginTemplates: SystemModuleTemplateType;
show2Plugin?: boolean;
systemTemplates: FlowModuleTemplateType[];
pluginTemplates: FlowModuleTemplateType[];
};
const ModuleTemplateList = ({
systemTemplates,
pluginTemplates,
show2Plugin = false,
isOpen,
onClose
}: ModuleTemplateProps & {
isOpen: boolean;
onClose: () => void;
}) => {
const router = useRouter();
const { t } = useTranslation();
const [templateType, setTemplateType] = React.useState(TemplateTypeEnum.system);
@@ -53,9 +61,9 @@ const ModuleTemplateList = ({
child: <RenderList templates={systemTemplates} onClose={onClose} />
},
{
type: TemplateTypeEnum.combine,
type: TemplateTypeEnum.plugin,
label: t('plugin.Plugin Module'),
child: <RenderList templates={pluginTemplates} onClose={onClose} />
child: <RenderList templates={pluginTemplates} onClose={onClose} isPlugin />
}
],
[pluginTemplates, onClose, systemTemplates, t]
@@ -112,20 +120,6 @@ const ModuleTemplateList = ({
{item.label}
</Box>
))}
<Box flex={1} />
{show2Plugin && templateType === TemplateTypeEnum.combine && (
<Flex
alignItems={'center'}
_hover={{ textDecoration: 'underline' }}
cursor={'pointer'}
onClick={() => router.push('/plugin/list')}
>
<Box fontSize={'sm'} transform={'translateY(-1px)'}>
{t('plugin.To Edit Plugin')}
</Box>
<MyIcon name={'common/rightArrowLight'} w={'12px'} />
</Flex>
)}
</Flex>
{TemplateItem}
</Flex>
@@ -135,47 +129,56 @@ const ModuleTemplateList = ({
export default React.memo(ModuleTemplateList);
var RenderList = React.memo(function RenderList({
const RenderList = React.memo(function RenderList({
templates,
isPlugin = false,
onClose
}: {
templates: {
label: string;
list: FlowModuleTemplateType[];
}[];
templates: FlowModuleTemplateType[];
isPlugin?: boolean;
onClose: () => void;
}) {
const { t } = useTranslation();
const router = useRouter();
const { isPc } = useSystemStore();
const { setNodes, reactFlowWrapper } = useFlowProviderStore();
const { x, y, zoom } = useViewport();
const { setLoading } = useSystemStore();
const { toast } = useToast();
const formatTemplates = useMemo<moduleTemplateListType>(() => {
const copy: moduleTemplateListType = JSON.parse(JSON.stringify(moduleTemplatesList));
templates.forEach((item) => {
const index = copy.findIndex((template) => template.type === item.templateType);
if (index === -1) return;
copy[index].list.push(item);
});
return copy.filter((item) => item.list.length > 0);
}, [templates]);
const onAddNode = useCallback(
async ({ template, position }: { template: FlowModuleTemplateType; position: XYPosition }) => {
if (!reactFlowWrapper?.current) return;
let templateModule = { ...template };
// get plugin module
try {
if (templateModule.flowType === FlowNodeTypeEnum.pluginModule) {
setLoading(true);
const pluginModule = await getPluginModuleDetail(templateModule.id);
templateModule = {
...templateModule,
...pluginModule
};
const templateModule = await (async () => {
try {
// get plugin preview module
if (template.flowType === FlowNodeTypeEnum.pluginModule) {
setLoading(true);
const res = await getPreviewPluginModule(template.id);
setLoading(false);
return res;
}
return { ...template };
} catch (e) {
toast({
status: 'error',
title: getErrText(e, t('plugin.Get Plugin Module Detail Failed'))
});
setLoading(false);
return Promise.reject(e);
}
} catch (e) {
return toast({
status: 'error',
title: getErrText(e, t('plugin.Get Plugin Module Detail Failed'))
});
} finally {
setLoading(false);
}
})();
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
const mouseX = (position.x - reactFlowBounds.left - x) / zoom - 100;
@@ -196,46 +199,73 @@ var RenderList = React.memo(function RenderList({
[reactFlowWrapper, setLoading, setNodes, t, toast, x, y, zoom]
);
const list = useMemo(() => templates.map((item) => item.list).flat(), [templates]);
return list.length === 0 ? (
return templates.length === 0 ? (
<EmptyTip text={t('app.module.No Modules')} />
) : (
<Box flex={'1 0 0'} overflow={'overlay'}>
<Box w={['100%', '330px']} mx={'auto'}>
{list.map((item) => (
<Flex
key={item.id}
alignItems={'center'}
p={5}
cursor={'pointer'}
_hover={{ bg: 'myWhite.600' }}
borderRadius={'md'}
draggable
onDragEnd={(e) => {
if (e.clientX < 360) return;
onAddNode({
template: item,
position: { x: e.clientX, y: e.clientY }
});
}}
onClick={(e) => {
if (isPc) return;
onClose();
onAddNode({
template: item,
position: { x: e.clientX, y: e.clientY }
});
}}
>
<Avatar src={item.logo} w={'34px'} objectFit={'contain'} borderRadius={'0'} />
<Box ml={5} flex={'1 0 0'}>
<Box color={'black'}>{item.name}</Box>
<Box className="textEllipsis3" color={'myGray.500'} fontSize={'sm'}>
{item.intro}
{formatTemplates.map((item, i) => (
<Box key={item.type}>
<Flex>
<Box fontWeight={'bold'} flex={1}>
{item.label}
</Box>
</Box>
</Flex>
{isPlugin && item.type === ModuleTemplateTypeEnum.personalPlugin && (
<Flex
alignItems={'center'}
_hover={{ textDecoration: 'underline' }}
cursor={'pointer'}
onClick={() => router.push('/plugin/list')}
>
<Box fontSize={'sm'} transform={'translateY(-1px)'}>
{t('plugin.To Edit Plugin')}
</Box>
<MyIcon name={'common/rightArrowLight'} w={'12px'} />
</Flex>
)}
</Flex>
<>
{item.list.map((template) => (
<Flex
key={template.id}
alignItems={'center'}
p={5}
cursor={'pointer'}
_hover={{ bg: 'myWhite.600' }}
borderRadius={'sm'}
draggable
onDragEnd={(e) => {
if (e.clientX < 360) return;
onAddNode({
template: template,
position: { x: e.clientX, y: e.clientY }
});
}}
onClick={(e) => {
if (isPc) return;
onClose();
onAddNode({
template: template,
position: { x: e.clientX, y: e.clientY }
});
}}
>
<Avatar
src={template.avatar}
w={'34px'}
objectFit={'contain'}
borderRadius={'0'}
/>
<Box ml={5} flex={'1 0 0'}>
<Box color={'black'}>{template.name}</Box>
<Box className="textEllipsis3" color={'myGray.500'} fontSize={'sm'}>
{template.intro}
</Box>
</Box>
</Flex>
))}
</>
</Box>
))}
</Box>
</Box>

View File

@@ -39,6 +39,7 @@ const SelectAppModal = ({
<MyModal
isOpen
title={`选择应用${max > 1 ? `(${selectedApps.length}/${max})` : ''}`}
iconSrc="/imgs/module/ai.svg"
onClose={onClose}
minW={'700px'}
position={'relative'}

View File

@@ -35,11 +35,12 @@ const ExtractFieldModal = ({
});
return (
<MyModal isOpen={true} onClose={onClose}>
<ModalHeader display={'flex'} alignItems={'center'}>
<Avatar src={'/imgs/module/extract.png'} mr={2} w={'20px'} objectFit={'cover'} />
</ModalHeader>
<MyModal
isOpen={true}
iconSrc="/imgs/module/extract.png"
title={'提取字段配置'}
onClose={onClose}
>
<ModalBody>
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}></Box>

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import {
Box,
Button,
@@ -11,65 +11,69 @@ import {
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import MyModal from '@/components/MyModal';
import Avatar from '@/components/Avatar';
import { FlowNodeValTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
import { useTranslation } from 'next-i18next';
import MySelect from '@/components/Select';
const typeSelectList = [
{
label: '字符串',
value: FlowNodeValTypeEnum.string
},
{
label: '数字',
value: FlowNodeValTypeEnum.number
},
{
label: '布尔',
value: FlowNodeValTypeEnum.boolean
},
{
label: '历史记录',
value: FlowNodeValTypeEnum.chatHistory
},
{
label: '引用内容',
value: FlowNodeValTypeEnum.datasetQuote
},
{
label: '任意',
value: FlowNodeValTypeEnum.any
}
];
import { FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/module/node/constant';
export type EditFieldModeType = 'input' | 'output' | 'pluginInput';
export type EditFieldType = {
type?: `${FlowNodeInputTypeEnum}`; // input type
key: string;
label?: string;
valueType?: `${FlowNodeValTypeEnum}`;
valueType?: `${ModuleDataTypeEnum}`;
description?: string;
required?: boolean;
createSign?: boolean;
};
const FieldEditModal = ({
mode,
defaultField = {
label: '',
key: '',
description: '',
valueType: FlowNodeValTypeEnum.string,
required: false
},
defaultField,
onClose,
onSubmit
}: {
mode: EditFieldModeType;
defaultField?: EditFieldType;
defaultField: EditFieldType;
onClose: () => void;
onSubmit: (data: EditFieldType) => void;
}) => {
const { t } = useTranslation();
const inputTypeList = [
{
label: t('core.module.inputType.target'),
value: FlowNodeInputTypeEnum.target,
valueType: ModuleDataTypeEnum.string
},
{
label: t('core.module.inputType.input'),
value: FlowNodeInputTypeEnum.input,
valueType: ModuleDataTypeEnum.string
},
{
label: t('core.module.inputType.textarea'),
value: FlowNodeInputTypeEnum.textarea,
valueType: ModuleDataTypeEnum.string
},
{
label: t('core.module.inputType.switch'),
value: FlowNodeInputTypeEnum.switch,
valueType: ModuleDataTypeEnum.boolean
},
{
label: t('core.module.inputType.selectDataset'),
value: FlowNodeInputTypeEnum.selectDataset,
valueType: ModuleDataTypeEnum.selectDataset
}
];
const dataTypeSelectList = Object.values(FlowValueTypeMap)
.slice(0, -2)
.map((item) => ({
label: t(item.label),
value: item.value
}));
const { register, getValues, setValue, handleSubmit } = useForm<EditFieldType>({
defaultValues: defaultField
});
@@ -79,75 +83,113 @@ const FieldEditModal = ({
? t('app.Input Field Settings')
: t('app.Output Field Settings');
const showValueTypeSelect = useMemo(() => {
return getValues('type') === FlowNodeInputTypeEnum.target || mode === 'output';
}, [getValues, mode, refresh]);
return (
<MyModal
isOpen={true}
title={
<Flex alignItems={'center'}>
<Avatar src={'/imgs/module/extract.png'} mr={2} w={'20px'} objectFit={'cover'} />
{title}
</Flex>
}
onClose={onClose}
>
<MyModal isOpen={true} iconSrc="/imgs/module/extract.png" title={title} onClose={onClose}>
<ModalBody minH={'260px'} overflow={'visible'}>
{mode === 'input' && (
{/* input type select: target, input, textarea.... */}
{mode === 'pluginInput' && (
<Flex alignItems={'center'} mb={5}>
<Box flex={'0 0 70px'}></Box>
<Box flex={'0 0 70px'}>{t('core.module.Input Type')}</Box>
<MySelect
w={'288px'}
list={inputTypeList}
value={getValues('type')}
onchange={(e: string) => {
const type = e as `${FlowNodeInputTypeEnum}`;
const selectedItem = inputTypeList.find((item) => item.value === type);
setValue('type', type);
setValue('valueType', selectedItem?.valueType);
if (type === FlowNodeInputTypeEnum.selectDataset) {
setValue('label', selectedItem?.label);
}
setRefresh(!refresh);
}}
/>
</Flex>
)}
{['input', 'pluginInput'].includes(mode) && (
<Flex alignItems={'center'} mb={5}>
<Box flex={'0 0 70px'}>{t('common.Require Input')}</Box>
<Switch {...register('required')} />
</Flex>
)}
<Flex mb={5} alignItems={'center'}>
<Box flex={'0 0 70px'}></Box>
<MySelect
w={'288px'}
list={typeSelectList}
value={getValues('valueType')}
onchange={(e: string) => {
const type = e as `${FlowNodeValTypeEnum}`;
setValue('valueType', type);
{showValueTypeSelect && (
<Flex mb={5} alignItems={'center'}>
<Box flex={'0 0 70px'}>{t('core.module.Data Type')}</Box>
<MySelect
w={'288px'}
list={dataTypeSelectList}
value={getValues('valueType')}
onchange={(e: string) => {
const type = e as `${ModuleDataTypeEnum}`;
setValue('valueType', type);
if (
type === FlowNodeValTypeEnum.chatHistory ||
type === FlowNodeValTypeEnum.datasetQuote
) {
const label = typeSelectList.find((item) => item.value === type)?.label;
setValue('label', label);
}
if (
type === ModuleDataTypeEnum.chatHistory ||
type === ModuleDataTypeEnum.datasetQuote
) {
const label = dataTypeSelectList.find((item) => item.value === type)?.label;
setValue('label', label);
}
setRefresh(!refresh);
}}
/>
</Flex>
)}
setRefresh(!refresh);
}}
/>
</Flex>
<Flex mb={5} alignItems={'center'}>
<Box flex={'0 0 70px'}></Box>
<Box flex={'0 0 70px'}>{t('core.module.Field Name')}</Box>
<Input
placeholder="预约字段/sql语句……"
{...register('label', { required: '字段名不能为空' })}
/>
</Flex>
<Flex mb={5} alignItems={'center'}>
<Box flex={'0 0 70px'}> key</Box>
<Box flex={'0 0 70px'}>{t('core.module.Field key')}</Box>
<Input
placeholder="appointment/sql"
{...register('key', { required: '字段 key 不能为空' })}
/>
</Flex>
<Flex mb={5} alignItems={'flex-start'}>
<Box flex={'0 0 70px'}></Box>
<Box flex={'0 0 70px'}>{t('core.module.Field Description')}</Box>
<Textarea placeholder="可选" rows={3} {...register('description')} />
</Flex>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
{t('common.Close')}
</Button>
<Button onClick={handleSubmit(onSubmit)}></Button>
<Button onClick={handleSubmit(onSubmit)}>{t('common.Confirm')}</Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(FieldEditModal);
export const defaultInputField: EditFieldType = {
label: '',
key: '',
description: '',
type: FlowNodeInputTypeEnum.target,
valueType: ModuleDataTypeEnum.string,
required: true,
createSign: true
};
export const defaultOutputField: EditFieldType = {
label: '',
key: '',
description: '',
valueType: ModuleDataTypeEnum.string,
required: true,
createSign: true
};

View File

@@ -9,14 +9,13 @@ import { useTranslation } from 'next-i18next';
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
import { useToast } from '@/web/common/hooks/useToast';
import { useFlowProviderStore, onChangeNode } from '../../FlowProvider';
import {
FlowNodeSpecialInputKeyEnum,
FlowNodeTypeEnum
} from '@fastgpt/global/core/module/node/constant';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getPluginModuleDetail } from '@/web/core/plugin/api';
import { getPreviewPluginModule } from '@/web/core/plugin/api';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { LOGO_ICON } from '@fastgpt/global/core/chat/constants';
type Props = FlowModuleItemType & {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -27,9 +26,9 @@ type Props = FlowModuleItemType & {
const NodeCard = (props: Props) => {
const {
children,
logo = '/icon/logo.svg',
avatar = LOGO_ICON,
name = '未知模块',
description,
intro,
minW = '300px',
moduleId,
flowType,
@@ -59,14 +58,13 @@ const NodeCard = (props: Props) => {
icon: 'common/refreshLight',
label: t('plugin.Synchronous version'),
onClick: () => {
const pluginId = inputs.find(
(item) => item.key === FlowNodeSpecialInputKeyEnum.pluginId
)?.value;
const pluginId = inputs.find((item) => item.key === ModuleInputKeyEnum.pluginId)
?.value;
if (!pluginId) return;
openConfirm(async () => {
try {
setLoading(true);
const pluginModule = await getPluginModuleDetail(pluginId);
const pluginModule = await getPreviewPluginModule(pluginId);
onResetNode(moduleId, pluginModule);
} catch (e) {
return toast({
@@ -147,18 +145,13 @@ const NodeCard = (props: Props) => {
className={isPreview ? 'nodrag' : ''}
>
<Flex className="custom-drag-handle" px={4} py={3} alignItems={'center'}>
<Avatar src={logo} borderRadius={'md'} objectFit={'contain'} w={'30px'} h={'30px'} />
<Avatar src={avatar} borderRadius={'md'} objectFit={'contain'} w={'30px'} h={'30px'} />
<Box ml={3} fontSize={'lg'} color={'myGray.600'}>
{name}
</Box>
{description && (
<MyTooltip label={description} forceShow>
<QuestionOutlineIcon
display={['none', 'inline']}
transform={'translateY(1px)'}
mb={'1px'}
ml={1}
/>
{intro && (
<MyTooltip label={intro} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} mb={'1px'} ml={1} />
</MyTooltip>
)}
<Box flex={1} />

View File

@@ -0,0 +1,23 @@
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Box, Flex, Switch, type SwitchProps } from '@chakra-ui/react';
import React from 'react';
import { useTranslation } from 'next-i18next';
const QGSwitch = (props: SwitchProps) => {
const { t } = useTranslation();
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/app/questionGuide'} mr={2} w={'16px'} />
<Box>{t('core.app.Next Step Guide')}</Box>
<MyTooltip label={t('core.app.Question Guide Tip')} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<Switch {...props} />
</Flex>
);
};
export default QGSwitch;

View File

@@ -0,0 +1,167 @@
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Box, Button, Flex, ModalBody, useDisclosure, Image } from '@chakra-ui/react';
import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'next-i18next';
import MySelect from '@/components/Select';
import { TTSTypeEnum } from '@/constants/app';
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
import { useAudioPlay } from '@/web/common/utils/voice';
import { audioSpeechModels } from '@/web/common/system/staticData';
import MyModal from '@/components/MyModal';
import MySlider from '@/components/Slider';
const TTSSelect = ({
value,
onChange
}: {
value: AppTTSConfigType;
onChange: (e: AppTTSConfigType) => void;
}) => {
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
const list = useMemo(
() => [
{ label: t('core.app.tts.Close'), value: TTSTypeEnum.none },
{ label: t('core.app.tts.Web'), value: TTSTypeEnum.web },
...audioSpeechModels.map((item) => item?.voices || []).flat()
],
[t]
);
const formatValue = useMemo(() => {
if (!value || !value.type) {
return TTSTypeEnum.none;
}
if (value.type === TTSTypeEnum.none || value.type === TTSTypeEnum.web) {
return value.type;
}
return value.voice;
}, [value]);
const formLabel = useMemo(
() => list.find((item) => item.value === formatValue)?.label || t('common.UnKnow'),
[formatValue, list, t]
);
const { playAudio, cancelAudio, audioLoading, audioPlaying } = useAudioPlay({ ttsConfig: value });
const onclickChange = useCallback(
(e: string) => {
if (e === TTSTypeEnum.none || e === TTSTypeEnum.web) {
onChange({ type: e as `${TTSTypeEnum}` });
} else {
const audioModel = audioSpeechModels.find(
(item) => item.voices?.find((voice) => voice.value === e)
);
if (!audioModel) {
return;
}
onChange({
...value,
type: TTSTypeEnum.model,
model: audioModel.model,
voice: e
});
}
},
[onChange, value]
);
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/app/tts'} mr={2} w={'16px'} />
<Box>{t('core.app.TTS')}</Box>
<MyTooltip label={t('core.app.TTS Tip')} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<MyTooltip label={t('core.app.Select TTS')}>
<Box
cursor={'pointer'}
_hover={{ bg: 'myGray.100' }}
py={2}
px={3}
borderRadius={'md'}
onClick={onOpen}
color={'myGray.600'}
>
{formLabel}
</Box>
</MyTooltip>
<MyModal
title={
<>
<MyIcon name={'core/app/tts'} mr={2} w={'20px'} />
{t('core.app.TTS')}
</>
}
isOpen={isOpen}
onClose={onClose}
w={'500px'}
>
<ModalBody px={[5, 16]} py={[4, 8]}>
<Flex justifyContent={'space-between'} alignItems={'center'}>
{t('core.app.tts.Speech model')}
<MySelect w={'220px'} value={formatValue} list={list} onchange={onclickChange} />
</Flex>
<Flex mt={8} justifyContent={'space-between'} alignItems={'center'}>
{t('core.app.tts.Speech speed')}
<MySlider
markList={[
{ label: '0.3', value: 0.3 },
{ label: '2', value: 2 }
]}
width={'220px'}
min={0.3}
max={2}
step={0.1}
value={value.speed || 1}
onChange={(e) => {
onChange({
...value,
speed: e
});
}}
/>
</Flex>
{formatValue !== TTSTypeEnum.none && (
<Flex mt={10} justifyContent={'end'}>
{audioPlaying ? (
<Flex>
<Image src="/icon/speaking.gif" w={'24px'} alt={''} />
<Button
ml={2}
variant={'gray'}
isLoading={audioLoading}
leftIcon={<MyIcon name={'core/chat/stopSpeech'} w={'16px'} />}
onClick={() => {
cancelAudio();
}}
>
{t('core.chat.tts.Stop Speech')}
</Button>
</Flex>
) : (
<Button
isLoading={audioLoading}
leftIcon={<MyIcon name={'core/app/headphones'} w={'16px'} />}
onClick={() => {
playAudio({
text: t('core.app.tts.Test Listen Text')
});
}}
>
{t('core.app.tts.Test Listen')}
</Button>
)}
</Flex>
)}
</ModalBody>
</MyModal>
</Flex>
);
};
export default TTSSelect;

View File

@@ -0,0 +1,342 @@
import React, { useEffect, useState } from 'react';
import {
Box,
Button,
ModalFooter,
ModalBody,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
Flex,
Switch,
Input,
Grid,
FormControl,
useTheme,
Image,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableContainer,
BoxProps,
useDisclosure
} from '@chakra-ui/react';
import { QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons';
import { VariableInputEnum } from '@fastgpt/global/core/module/constants';
import type { VariableItemType } from '@fastgpt/global/core/module/type.d';
import MyIcon from '@/components/Icon';
import { useForm } from 'react-hook-form';
import { useFieldArray } from 'react-hook-form';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
import MyModal from '@/components/MyModal';
import MyTooltip from '@/components/MyTooltip';
import { variableTip } from '@fastgpt/global/core/module/template/tip';
import { useTranslation } from 'next-i18next';
import { useToast } from '@/web/common/hooks/useToast';
const VariableEdit = ({
variables,
onChange
}: {
variables: VariableItemType[];
onChange: (data: VariableItemType[]) => void;
}) => {
const { t } = useTranslation();
const { toast } = useToast();
const theme = useTheme();
const [refresh, setRefresh] = useState(false);
const VariableTypeList = [
{
label: t('core.module.variable.text type'),
icon: 'settingLight',
key: VariableInputEnum.input
},
{
label: t('core.module.variable.select type'),
icon: 'settingLight',
key: VariableInputEnum.select
}
];
const { isOpen: isOpenEdit, onOpen: onOpenEdit, onClose: onCloseEdit } = useDisclosure();
const {
reset: resetEdit,
register: registerEdit,
getValues: getValuesEdit,
setValue: setValuesEdit,
control: editVariableController,
handleSubmit: handleSubmitEdit
} = useForm<{ variable: VariableItemType }>();
const {
fields: selectEnums,
append: appendEnums,
remove: removeEnums
} = useFieldArray({
control: editVariableController,
name: 'variable.enums'
});
const BoxBtnStyles: BoxProps = {
cursor: 'pointer',
px: 3,
py: '2px',
borderRadius: 'md',
_hover: {
bg: 'myGray.200'
}
};
return (
<Box>
<Flex alignItems={'center'}>
<Image alt={''} src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
<Box ml={2} flex={1}>
{t('core.module.Variable')}
<MyTooltip label={variableTip} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
</Box>
<Flex
{...BoxBtnStyles}
onClick={() => {
resetEdit({ variable: addVariable() });
onOpenEdit();
}}
>
+&ensp;{t('common.Add New')}
</Flex>
</Flex>
{variables.length > 0 && (
<Box mt={2} borderRadius={'lg'} overflow={'hidden'} borderWidth={'1px'} borderBottom="none">
<TableContainer>
<Table bg={'white'}>
<Thead>
<Tr>
<Th>{t('core.module.variable.variable name')}</Th>
<Th>{t('core.module.variable.key')}</Th>
<Th>{t('common.Require Input')}</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{variables.map((item, index) => (
<Tr key={item.id}>
<Td>{item.label} </Td>
<Td>{item.key}</Td>
<Td>{item.required ? '✔' : ''}</Td>
<Td>
<MyIcon
mr={3}
name={'settingLight'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
resetEdit({ variable: item });
onOpenEdit();
}}
/>
<MyIcon
name={'delete'}
w={'16px'}
cursor={'pointer'}
onClick={() =>
onChange(variables.filter((variable) => variable.id !== item.id))
}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Box>
)}
<MyModal
iconSrc="/imgs/module/variable.png"
title={t('core.module.Variable Setting')}
isOpen={isOpenEdit}
onClose={onCloseEdit}
>
<ModalBody>
<Flex alignItems={'center'}>
<Box w={'70px'}>{t('common.Require Input')}</Box>
<Switch {...registerEdit('variable.required')} />
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box w={'80px'}>{t('core.module.variable.variable name')}</Box>
<Input
{...registerEdit('variable.label', {
required: t('core.module.variable.variable name is required')
})}
/>
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box w={'80px'}>{t('core.module.variable.key')}</Box>
<Input
{...registerEdit('variable.key', {
required: t('core.module.variable.key is required')
})}
/>
</Flex>
<Box mt={5} mb={2}>
{t('core.module.Field Type')}
</Box>
<Grid gridTemplateColumns={'repeat(2,130px)'} gridGap={4}>
{VariableTypeList.map((item) => (
<Flex
key={item.key}
px={4}
py={1}
border={theme.borders.base}
borderRadius={'md'}
cursor={'pointer'}
{...(item.key === getValuesEdit('variable.type')
? {
bg: 'myWhite.600'
}
: {
_hover: {
boxShadow: 'md'
},
onClick: () => {
setValuesEdit('variable.type', item.key);
setRefresh(!refresh);
}
})}
>
<MyIcon name={item.icon as any} w={'16px'} />
<Box ml={3}>{item.label}</Box>
</Flex>
))}
</Grid>
{getValuesEdit('variable.type') === VariableInputEnum.input && (
<>
<Box mt={5} mb={2}>
{t('core.module.variable.text max length')}
</Box>
<Box>
<NumberInput max={100} min={1} step={1} position={'relative'}>
<NumberInputField
{...registerEdit('variable.maxLen', {
min: 1,
max: 100,
valueAsNumber: true
})}
max={100}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Box>
</>
)}
{getValuesEdit('variable.type') === VariableInputEnum.select && (
<>
<Box mt={5} mb={2}>
{t('core.module.variable.variable options')}
</Box>
<Box>
{selectEnums.map((item, i) => (
<Flex key={item.id} mb={2} alignItems={'center'}>
<FormControl>
<Input
{...registerEdit(`variable.enums.${i}.value`, {
required: t('core.module.variable.variable option is value is required')
})}
/>
</FormControl>
<MyIcon
ml={3}
name={'delete'}
w={'16px'}
cursor={'pointer'}
p={2}
borderRadius={'lg'}
_hover={{ bg: 'red.100' }}
onClick={() => removeEnums(i)}
/>
</Flex>
))}
</Box>
<Button
variant={'solid'}
w={'100%'}
textAlign={'left'}
leftIcon={<SmallAddIcon />}
bg={'myGray.100 !important'}
onClick={() => appendEnums({ value: '' })}
>
{t('core.module.variable add option')}
</Button>
</>
)}
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onCloseEdit}>
{t('common.Close')}
</Button>
<Button
onClick={handleSubmitEdit(({ variable }) => {
// check select
if (variable.type === VariableInputEnum.select) {
const enums = variable.enums.filter((item) => item.value);
if (enums.length === 0) {
toast({
status: 'warning',
title: t('core.module.variable.variable option is required')
});
return;
}
}
const onChangeVariable = [...variables];
// update
if (variable.id) {
const index = variables.findIndex((item) => item.id === variable.id);
onChangeVariable[index] = variable;
} else {
onChangeVariable.push({
...variable,
id: nanoid()
});
}
onChange(onChangeVariable);
onCloseEdit();
})}
>
{getValuesEdit('variable.id') ? t('common.Confirm Update') : t('common.Add New')}
</Button>
</ModalFooter>
</MyModal>
</Box>
);
};
export default React.memo(VariableEdit);
export const defaultVariable: VariableItemType = {
id: nanoid(),
key: 'key',
label: 'label',
type: VariableInputEnum.input,
required: true,
maxLen: 50,
enums: [{ value: '' }]
};
export const addVariable = () => {
const newVariable = { ...defaultVariable, key: nanoid(), id: '' };
return newVariable;
};

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { NodeProps } from 'reactflow';
import { Box, Input, Button, Flex, Textarea } from '@chakra-ui/react';
import { Box, Button, Flex, Textarea } from '@chakra-ui/react';
import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
import Divider from '../modules/Divider';
@@ -10,11 +10,8 @@ import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/module/
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 4);
import MyIcon from '@/components/Icon';
import {
FlowNodeOutputTypeEnum,
FlowNodeValTypeEnum,
FlowNodeSpecialInputKeyEnum
} from '@fastgpt/global/core/module/node/constant';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { useTranslation } from 'next-i18next';
import SourceHandle from '../render/SourceHandle';
import MyTooltip from '@/components/MyTooltip';
@@ -32,7 +29,7 @@ const NodeCQNode = ({ data }: NodeProps<FlowModuleItemType>) => {
moduleId={moduleId}
flowInputList={inputs}
CustomComponent={{
[FlowNodeSpecialInputKeyEnum.agents]: ({
[ModuleInputKeyEnum.agents]: ({
key: agentKey,
value: agents = [],
...props
@@ -100,7 +97,7 @@ const NodeCQNode = ({ data }: NodeProps<FlowModuleItemType>) => {
});
}}
/>
<SourceHandle handleKey={item.key} valueType={FlowNodeValTypeEnum.boolean} />
<SourceHandle handleKey={item.key} valueType={ModuleDataTypeEnum.boolean} />
</Box>
</Box>
))}

View File

@@ -12,11 +12,9 @@ import type { ContextExtractAgentItemType } from '@fastgpt/global/core/module/ty
import RenderOutput from '../render/RenderOutput';
import MyIcon from '@/components/Icon';
import ExtractFieldModal from '../modules/ExtractFieldModal';
import { ContextExtractEnum } from '@/constants/flow/flowField';
import {
FlowNodeOutputTypeEnum,
FlowNodeValTypeEnum
} from '@fastgpt/global/core/module/node/constant';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
import { useFlowProviderStore, onChangeNode } from '../../FlowProvider';
const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
@@ -33,7 +31,7 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
moduleId={moduleId}
flowInputList={inputs}
CustomComponent={{
[ContextExtractEnum.extractKeys]: ({
[ModuleInputKeyEnum.extractKeys]: ({
value: extractKeys = [],
...props
}: {
@@ -94,7 +92,7 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: ContextExtractEnum.extractKeys,
key: ModuleInputKeyEnum.extractKeys,
value: {
...props,
value: extractKeys.filter((extract) => item.key !== extract.key)
@@ -130,7 +128,7 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
onClose={() => setEditExtractField(undefined)}
onSubmit={(data) => {
const extracts: ContextExtractAgentItemType[] =
inputs.find((item) => item.key === ContextExtractEnum.extractKeys)?.value || [];
inputs.find((item) => item.key === ModuleInputKeyEnum.extractKeys)?.value || [];
const exists = extracts.find((item) => item.key === editExtractFiled.key);
@@ -141,9 +139,9 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: ContextExtractEnum.extractKeys,
key: ModuleInputKeyEnum.extractKeys,
value: {
...inputs.find((input) => input.key === ContextExtractEnum.extractKeys),
...inputs.find((input) => input.key === ModuleInputKeyEnum.extractKeys),
value: newInputs
}
});
@@ -152,7 +150,7 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
key: data.key,
label: `提取结果-${data.desc}`,
description: '无法提取时不会返回',
valueType: FlowNodeValTypeEnum.string,
valueType: ModuleDataTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
targets: []
};

View File

@@ -11,9 +11,9 @@ import RenderOutput from '../render/RenderOutput';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeValTypeEnum
FlowNodeOutputTypeEnum
} from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
import { onChangeNode } from '../../FlowProvider';
@@ -37,7 +37,7 @@ const NodeHttp = ({ data }: NodeProps<FlowModuleItemType>) => {
key,
value: {
key,
valueType: FlowNodeValTypeEnum.string,
valueType: ModuleDataTypeEnum.string,
type: FlowNodeInputTypeEnum.target,
label: `入参${inputs.length - 1}`,
edit: true
@@ -62,7 +62,7 @@ const NodeHttp = ({ data }: NodeProps<FlowModuleItemType>) => {
value: {
key: nanoid(),
label: `出参${outputs.length}`,
valueType: FlowNodeValTypeEnum.string,
valueType: ModuleDataTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: []

View File

@@ -3,27 +3,22 @@ import { NodeProps } from 'reactflow';
import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
import { onChangeNode } from '../../FlowProvider';
import dynamic from 'next/dynamic';
import { Box, Button, Flex } from '@chakra-ui/react';
import { QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons';
import { customAlphabet } from 'nanoid';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeValTypeEnum
} from '@fastgpt/global/core/module/node/constant';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/module/node/constant';
import Container from '../modules/Container';
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import SourceHandle from '../render/SourceHandle';
import { EditFieldType } from '../modules/FieldEditModal';
import { defaultInputField, type EditFieldType } from '../modules/FieldEditModal';
import { useToast } from '@/web/common/hooks/useToast';
const FieldEditModal = dynamic(() => import('../modules/FieldEditModal'));
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
const NodeInput = ({ data }: NodeProps<FlowModuleItemType>) => {
const NodePluginInput = ({ data }: NodeProps<FlowModuleItemType>) => {
const { moduleId, inputs, outputs } = data;
const { toast } = useToast();
const [editField, setEditField] = useState<EditFieldType>();
return (
@@ -37,7 +32,7 @@ const NodeInput = ({ data }: NodeProps<FlowModuleItemType>) => {
justifyContent={'right'}
alignItems={'center'}
position={'relative'}
mb={4}
mb={7}
>
<MyIcon
name={'settingLight'}
@@ -47,6 +42,7 @@ const NodeInput = ({ data }: NodeProps<FlowModuleItemType>) => {
_hover={{ color: 'myBlue.600' }}
onClick={() =>
setEditField({
type: item.type,
key: item.key,
label: item.label,
valueType: item.valueType,
@@ -103,31 +99,7 @@ const NodeInput = ({ data }: NodeProps<FlowModuleItemType>) => {
variant={'base'}
leftIcon={<SmallAddIcon />}
onClick={() => {
const key = nanoid();
onChangeNode({
moduleId,
type: 'addInput',
value: {
key,
valueType: FlowNodeValTypeEnum.string,
type: FlowNodeInputTypeEnum.target,
label: `入参${inputs.length + 1}`,
edit: true,
required: true
}
});
onChangeNode({
moduleId,
type: 'addOutput',
value: {
key,
label: `入参${inputs.length + 1}`,
valueType: FlowNodeValTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: []
}
});
setEditField(defaultInputField);
}}
>
@@ -140,6 +112,44 @@ const NodeInput = ({ data }: NodeProps<FlowModuleItemType>) => {
defaultField={editField}
onClose={() => setEditField(undefined)}
onSubmit={(e) => {
// create field
if (e.createSign) {
// check key repeat
const memInput = inputs.find((item) => item.key === e.key);
if (memInput) {
return toast({
status: 'warning',
title: '字段key已存在'
});
}
onChangeNode({
moduleId,
type: 'addInput',
value: {
key: e.key,
valueType: e.valueType,
type: e.type,
label: e.label,
required: e.required,
edit: true
}
});
onChangeNode({
moduleId,
type: 'addOutput',
value: {
key: e.key,
valueType: e.valueType,
label: e.label,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: []
}
});
return setEditField(undefined);
}
// check key valid
const memInput = inputs.find((item) => item.key === editField.key);
const memOutput = outputs.find((item) => item.key === editField.key);
@@ -188,4 +198,4 @@ const NodeInput = ({ data }: NodeProps<FlowModuleItemType>) => {
</NodeCard>
);
};
export default React.memo(NodeInput);
export default React.memo(NodePluginInput);

View File

@@ -3,27 +3,22 @@ import { NodeProps } from 'reactflow';
import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
import { onChangeNode } from '../../FlowProvider';
import dynamic from 'next/dynamic';
import { Box, Button, Flex } from '@chakra-ui/react';
import { QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons';
import { customAlphabet } from 'nanoid';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeValTypeEnum
} from '@fastgpt/global/core/module/node/constant';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/module/node/constant';
import Container from '../modules/Container';
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import { EditFieldType } from '../modules/FieldEditModal';
import { EditFieldType, defaultOutputField } from '../modules/FieldEditModal';
import TargetHandle from '../render/TargetHandle';
import { useToast } from '@/web/common/hooks/useToast';
const FieldEditModal = dynamic(() => import('../modules/FieldEditModal'));
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
const NodeOutput = ({ data }: NodeProps<FlowModuleItemType>) => {
const NodePluginOutput = ({ data }: NodeProps<FlowModuleItemType>) => {
const { moduleId, inputs, outputs } = data;
const { toast } = useToast();
const [editField, setEditField] = useState<EditFieldType>();
return (
@@ -37,22 +32,20 @@ const NodeOutput = ({ data }: NodeProps<FlowModuleItemType>) => {
justifyContent={'left'}
alignItems={'center'}
position={'relative'}
mb={4}
mb={7}
>
<TargetHandle handleKey={item.key} valueType={item.valueType} />
<Box position={'relative'}>
{item.label}
{item.required && (
<Box
position={'absolute'}
right={'-6px'}
top={'-3px'}
color={'red.500'}
fontWeight={'bold'}
>
*
</Box>
)}
<Box
position={'absolute'}
right={'-6px'}
top={'-3px'}
color={'red.500'}
fontWeight={'bold'}
>
*
</Box>
</Box>
<MyIcon
@@ -104,34 +97,10 @@ const NodeOutput = ({ data }: NodeProps<FlowModuleItemType>) => {
variant={'base'}
leftIcon={<SmallAddIcon />}
onClick={() => {
const key = nanoid();
onChangeNode({
moduleId,
type: 'addInput',
value: {
key,
valueType: FlowNodeValTypeEnum.string,
type: FlowNodeInputTypeEnum.target,
label: `入参${inputs.length + 1}`,
edit: true,
required: true
}
});
onChangeNode({
moduleId,
type: 'addOutput',
value: {
key,
label: `入参${inputs.length + 1}`,
valueType: FlowNodeValTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: []
}
});
setEditField(defaultOutputField);
}}
>
</Button>
</Box>
</Container>
@@ -141,6 +110,42 @@ const NodeOutput = ({ data }: NodeProps<FlowModuleItemType>) => {
defaultField={editField}
onClose={() => setEditField(undefined)}
onSubmit={(e) => {
if (e.createSign) {
// check key repeat
const memInput = inputs.find((item) => item.key === e.key);
if (memInput) {
return toast({
status: 'warning',
title: '字段key已存在'
});
}
onChangeNode({
moduleId,
type: 'addInput',
value: {
key: e.key,
valueType: e.valueType,
type: e.type,
label: e.label,
required: e.required,
edit: true
}
});
onChangeNode({
moduleId,
type: 'addOutput',
value: {
key: e.key,
valueType: e.valueType,
label: e.label,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: []
}
});
return setEditField(undefined);
}
const memInput = inputs.find((item) => item.key === editField.key);
const memOutput = outputs.find((item) => item.key === editField.key);
if (!memInput || !memOutput) return;
@@ -188,4 +193,4 @@ const NodeOutput = ({ data }: NodeProps<FlowModuleItemType>) => {
</NodeCard>
);
};
export default React.memo(NodeOutput);
export default React.memo(NodePluginOutput);

View File

@@ -1,26 +0,0 @@
import React from 'react';
import { NodeProps } from 'reactflow';
import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
import Divider from '../modules/Divider';
import Container from '../modules/Container';
import RenderInput from '../render/RenderInput';
import RenderOutput from '../render/RenderOutput';
const NodeSimple = ({ data }: NodeProps<FlowModuleItemType>) => {
const { moduleId, inputs, outputs } = data;
return (
<NodeCard minW={'300px'} isPreview {...data}>
<Divider text="Input" />
<Container>
<RenderInput moduleId={moduleId} flowInputList={inputs} />
</Container>
<Divider text="Output" />
<Container>
<RenderOutput moduleId={moduleId} flowOutputList={outputs} />
</Container>
</NodeCard>
);
};
export default React.memo(NodeSimple);

View File

@@ -11,15 +11,23 @@ const NodeSimple = ({ data }: NodeProps<FlowModuleItemType>) => {
const { moduleId, inputs, outputs } = data;
return (
<NodeCard minW={'300px'} {...data}>
<Divider text="Input" />
<Container>
<RenderInput moduleId={moduleId} flowInputList={inputs} />
</Container>
<Divider text="Output" />
<Container>
<RenderOutput moduleId={moduleId} flowOutputList={outputs} />
</Container>
<NodeCard minW={'350px'} {...data}>
{inputs.length > 0 && (
<>
<Divider text="Input" />
<Container>
<RenderInput moduleId={moduleId} flowInputList={inputs} />
</Container>
</>
)}
{outputs.length > 0 && (
<>
<Divider text="Output" />
<Container>
<RenderOutput moduleId={moduleId} flowOutputList={outputs} />
</Container>
</>
)}
</NodeCard>
);
};

View File

@@ -16,19 +16,19 @@ import {
} from '@chakra-ui/react';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { FlowModuleItemType, ModuleItemType } from '@fastgpt/global/core/module/type.d';
import { SystemInputEnum } from '@/constants/app';
import { welcomeTextTip, variableTip } from '@/constants/flow/ModuleTemplate';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { welcomeTextTip, variableTip } from '@fastgpt/global/core/module/template/tip';
import { onChangeNode } from '../../FlowProvider';
import VariableEditModal, { addVariable } from '../../../VariableEditModal';
import VariableEdit from '../modules/VariableEdit';
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import Container from '../modules/Container';
import NodeCard from '../modules/NodeCard';
import { VariableItemType } from '@/types/app';
import QGSwitch from '@/pages/app/detail/components/QGSwitch';
import TTSSelect from '@/pages/app/detail/components/TTSSelect';
import { splitGuideModule } from '@/global/core/app/modules/utils';
import type { VariableItemType } from '@fastgpt/global/core/module/type.d';
import QGSwitch from '@/components/core/module/Flow/components/modules/QGSwitch';
import TTSSelect from '@/components/core/module/Flow/components/modules/TTSSelect';
import { splitGuideModule } from '@fastgpt/global/core/module/utils';
const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
const theme = useTheme();
@@ -57,7 +57,7 @@ export function WelcomeText({ data }: { data: FlowModuleItemType }) {
const { inputs, moduleId } = data;
const welcomeText = useMemo(
() => inputs.find((item) => item.key === SystemInputEnum.welcomeText),
() => inputs.find((item) => item.key === ModuleInputKeyEnum.welcomeText),
[inputs]
);
@@ -81,7 +81,7 @@ export function WelcomeText({ data }: { data: FlowModuleItemType }) {
onChange={(e) => {
onChangeNode({
moduleId,
key: SystemInputEnum.welcomeText,
key: ModuleInputKeyEnum.welcomeText,
type: 'updateInput',
value: {
...welcomeText,
@@ -100,21 +100,19 @@ function ChatStartVariable({ data }: { data: FlowModuleItemType }) {
const variables = useMemo(
() =>
(inputs.find((item) => item.key === SystemInputEnum.variables)
(inputs.find((item) => item.key === ModuleInputKeyEnum.variables)
?.value as VariableItemType[]) || [],
[inputs]
);
const [editVariable, setEditVariable] = useState<VariableItemType>();
const updateVariables = useCallback(
(value: VariableItemType[]) => {
onChangeNode({
moduleId,
key: SystemInputEnum.variables,
key: ModuleInputKeyEnum.variables,
type: 'updateInput',
value: {
...inputs.find((item) => item.key === SystemInputEnum.variables),
...inputs.find((item) => item.key === ModuleInputKeyEnum.variables),
value
}
});
@@ -122,92 +120,7 @@ function ChatStartVariable({ data }: { data: FlowModuleItemType }) {
[inputs, moduleId]
);
const onclickSubmit = useCallback(
({ variable }: { variable: VariableItemType }) => {
updateVariables(variables.map((item) => (item.id === variable.id ? variable : item)));
setEditVariable(undefined);
},
[updateVariables, variables]
);
return (
<>
<Flex mb={1} alignItems={'center'}>
<MyIcon name={'variable'} mr={2} w={'16px'} color={'#fb7c3d'} />
<Box></Box>
<MyTooltip label={variableTip} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<Flex
ml={2}
textAlign={'right'}
cursor={'pointer'}
px={3}
py={'2px'}
borderRadius={'md'}
_hover={{ bg: 'myGray.200' }}
onClick={() => {
const newVariable = addVariable();
updateVariables(variables.concat(newVariable));
setEditVariable(newVariable);
}}
>
+&ensp;
</Flex>
</Flex>
{variables.length > 0 && (
<TableContainer borderWidth={'1px'} borderBottom="none" borderRadius={'lg'}>
<Table>
<Thead>
<Tr>
<Th></Th>
<Th> key</Th>
<Th></Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{variables.map((item, index) => (
<Tr key={index}>
<Td>{item.label} </Td>
<Td>{item.key}</Td>
<Td>{item.required ? '✔' : ''}</Td>
<Td>
<MyIcon
mr={3}
name={'settingLight'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
setEditVariable(item);
}}
/>
<MyIcon
name={'delete'}
w={'16px'}
cursor={'pointer'}
onClick={() =>
updateVariables(variables.filter((variable) => variable.id !== item.id))
}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
)}
{!!editVariable && (
<VariableEditModal
defaultVariable={editVariable}
onClose={() => setEditVariable(undefined)}
onSubmit={onclickSubmit}
/>
)}
</>
);
return <VariableEdit variables={variables} onChange={(e) => updateVariables(e)} />;
}
function QuestionGuide({ data }: { data: FlowModuleItemType }) {
@@ -215,7 +128,7 @@ function QuestionGuide({ data }: { data: FlowModuleItemType }) {
const questionGuide = useMemo(
() =>
(inputs.find((item) => item.key === SystemInputEnum.questionGuide)?.value as boolean) ||
(inputs.find((item) => item.key === ModuleInputKeyEnum.questionGuide)?.value as boolean) ||
false,
[inputs]
);
@@ -228,10 +141,10 @@ function QuestionGuide({ data }: { data: FlowModuleItemType }) {
const value = e.target.checked;
onChangeNode({
moduleId,
key: SystemInputEnum.questionGuide,
key: ModuleInputKeyEnum.questionGuide,
type: 'updateInput',
value: {
...inputs.find((item) => item.key === SystemInputEnum.questionGuide),
...inputs.find((item) => item.key === ModuleInputKeyEnum.questionGuide),
value
}
});
@@ -250,10 +163,10 @@ function TTSGuide({ data }: { data: FlowModuleItemType }) {
onChange={(e) => {
onChangeNode({
moduleId,
key: SystemInputEnum.tts,
key: ModuleInputKeyEnum.tts,
type: 'updateInput',
value: {
...inputs.find((item) => item.key === SystemInputEnum.tts),
...inputs.find((item) => item.key === ModuleInputKeyEnum.tts),
value: e
}
});

View File

@@ -6,12 +6,12 @@ import { AddIcon } from '@chakra-ui/icons';
import NodeCard from '../../modules/NodeCard';
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
import Container from '../../modules/Container';
import { SystemInputEnum, VariableInputEnum } from '@/constants/app';
import type { VariableItemType } from '@/types/app';
import { VariableInputEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import type { VariableItemType } from '@fastgpt/global/core/module/type.d';
import MyIcon from '@/components/Icon';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
import VariableEditModal, { addVariable } from '../../../../VariableEditModal';
import VariableEditModal, { addVariable } from '../../modules/VariableEdit';
import { onChangeNode } from '../../../FlowProvider';
export const defaultVariable: VariableItemType = {
@@ -29,102 +29,31 @@ const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
const variables = useMemo(
() =>
(inputs.find((item) => item.key === SystemInputEnum.variables)
(inputs.find((item) => item.key === ModuleInputKeyEnum.variables)
?.value as VariableItemType[]) || [],
[inputs]
);
const [editVariable, setEditVariable] = useState<VariableItemType>();
const updateVariables = useCallback(
(value: VariableItemType[]) => {
onChangeNode({
moduleId,
key: SystemInputEnum.variables,
type: 'updateInput',
value: {
...inputs.find((item) => item.key === SystemInputEnum.variables),
value
}
});
},
[inputs, moduleId]
);
const onclickSubmit = useCallback(
({ variable }: { variable: VariableItemType }) => {
updateVariables(variables.map((item) => (item.id === variable.id ? variable : item)));
setEditVariable(undefined);
},
[updateVariables, variables]
);
return (
<>
<NodeCard minW={'300px'} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th></Th>
<Th> key</Th>
<Th></Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{variables.map((item, index) => (
<Tr key={index}>
<Td>{item.label} </Td>
<Td>{item.key}</Td>
<Td>{item.required ? '✔' : ''}</Td>
<Td>
<MyIcon
mr={3}
name={'settingLight'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
setEditVariable(item);
}}
/>
<MyIcon
name={'delete'}
w={'16px'}
cursor={'pointer'}
onClick={() =>
updateVariables(variables.filter((variable) => variable.id !== item.id))
}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<Box mt={2} textAlign={'right'}>
<Button
variant={'base'}
leftIcon={<AddIcon fontSize={'10px'} />}
onClick={() => {
const newVariable = addVariable();
updateVariables(variables.concat(newVariable));
setEditVariable(newVariable);
}}
>
</Button>
</Box>
<VariableEditModal
variables={variables}
onChange={(e) =>
onChangeNode({
moduleId,
key: ModuleInputKeyEnum.variables,
type: 'updateInput',
value: {
...inputs.find((item) => item.key === ModuleInputKeyEnum.variables),
value: e
}
})
}
/>
</Container>
</NodeCard>
{!!editVariable && (
<VariableEditModal
defaultVariable={editVariable}
onClose={() => setEditVariable(undefined)}
onSubmit={onclickSubmit}
/>
)}
</>
);
};

View File

@@ -28,13 +28,12 @@ import MyTooltip from '@/components/MyTooltip';
import TargetHandle from './TargetHandle';
import MyIcon from '@/components/Icon';
import { useTranslation } from 'next-i18next';
import { AIChatProps } from '@/types/core/aiChat';
import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d';
import { chatModelList } from '@/web/common/system/staticData';
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
import { useQuery } from '@tanstack/react-query';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import type { EditFieldModeType, EditFieldType } from '../modules/FieldEditModal';
import { feConfigs } from '@/web/common/system/staticData';
@@ -53,13 +52,31 @@ export const Label = React.memo(function Label({
inputKey: string;
editFiledType?: EditFieldModeType;
}) {
const { required = false, description, edit, label, type, valueType } = item;
const { t } = useTranslation();
const { mode } = useFlowProviderStore();
const {
required = false,
description,
edit,
label,
type,
valueType,
showTargetInApp,
showTargetInPlugin
} = item;
const [editField, setEditField] = useState<EditFieldType>();
const targetHandle = useMemo(() => {
if (type === FlowNodeInputTypeEnum.target) return true;
if (mode === 'app' && showTargetInApp) return true;
if (mode === 'plugin' && showTargetInPlugin) return true;
return false;
}, [mode, showTargetInApp, showTargetInPlugin, type]);
return (
<Flex className="nodrag" cursor={'default'} alignItems={'center'} position={'relative'}>
<Box position={'relative'}>
{label}
{t(label)}
{description && (
<MyTooltip label={description} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
@@ -78,9 +95,7 @@ export const Label = React.memo(function Label({
)}
</Box>
{(type === FlowNodeInputTypeEnum.target || valueType) && (
<TargetHandle handleKey={inputKey} valueType={valueType} />
)}
{targetHandle && <TargetHandle handleKey={inputKey} valueType={valueType} />}
{edit && (
<>
@@ -93,6 +108,7 @@ export const Label = React.memo(function Label({
onClick={() =>
setEditField({
label: item.label,
type: item.type,
valueType: item.valueType,
required: item.required,
key: inputKey,
@@ -210,9 +226,6 @@ const RenderInput = ({
{item.type === FlowNodeInputTypeEnum.aiSettings && (
<AISetting inputs={sortInputs} item={item} moduleId={moduleId} />
)}
{item.type === FlowNodeInputTypeEnum.maxToken && (
<MaxTokenRender inputs={sortInputs} item={item} moduleId={moduleId} />
)}
{item.type === FlowNodeInputTypeEnum.selectChatModel && (
<SelectChatModelRender inputs={sortInputs} item={item} moduleId={moduleId} />
)}
@@ -381,7 +394,7 @@ var AISetting = React.memo(function AISetting({ inputs = [], moduleId }: RenderP
inputs.forEach((item) => {
obj[item.key] = item.value;
});
return obj as AIChatProps;
return obj as AIChatModuleProps;
}, [inputs]);
const {
@@ -427,50 +440,12 @@ var AISetting = React.memo(function AISetting({ inputs = [], moduleId }: RenderP
);
});
var MaxTokenRender = React.memo(function MaxTokenRender({
inputs = [],
item,
moduleId
}: RenderProps) {
const model = inputs.find((item) => item.key === 'model')?.value;
const modelData = chatModelList.find((item) => item.model === model);
const maxToken = modelData ? modelData.maxResponse : 4000;
const markList = [
{ label: '100', value: 100 },
{ label: `${maxToken}`, value: maxToken }
];
return (
<Box pt={5} pb={4} px={2}>
<MySlider
markList={markList}
width={'100%'}
min={item.min || 100}
max={maxToken}
step={item.step || 1}
value={item.value}
onChange={(e) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: e
}
});
}}
/>
</Box>
);
});
var SelectChatModelRender = React.memo(function SelectChatModelRender({
inputs = [],
item,
moduleId
}: RenderProps) {
const modelList = (item.customData?.() as LLMModelItemType[]) || chatModelList || [];
const modelList = chatModelList || [];
function onChangeModel(e: string) {
{
@@ -531,6 +506,7 @@ var SelectChatModelRender = React.memo(function SelectChatModelRender({
var SelectDatasetRender = React.memo(function SelectDatasetRender({ item, moduleId }: RenderProps) {
const theme = useTheme();
const { mode } = useFlowProviderStore();
const { allDatasets, loadAllDatasets } = useDatasetStore();
const {
isOpen: isOpenKbSelect,
@@ -538,9 +514,9 @@ var SelectDatasetRender = React.memo(function SelectDatasetRender({ item, module
onClose: onCloseKbSelect
} = useDisclosure();
const showKbList = useMemo(() => {
const selectedDatasets = useMemo(() => {
const value = item.value as SelectedDatasetType;
return allDatasets.filter((dataset) => value.find((kb) => kb.datasetId === dataset._id));
return allDatasets.filter((dataset) => value?.find((item) => item.datasetId === dataset._id));
}, [allDatasets, item.value]);
useQuery(['loadAllDatasets'], loadAllDatasets);
@@ -551,7 +527,7 @@ var SelectDatasetRender = React.memo(function SelectDatasetRender({ item, module
<Button h={'36px'} onClick={onOpenKbSelect}>
</Button>
{showKbList.map((item) => (
{selectedDatasets.map((item) => (
<Flex
key={item._id}
alignItems={'center'}
@@ -574,22 +550,24 @@ var SelectDatasetRender = React.memo(function SelectDatasetRender({ item, module
</Flex>
))}
</Grid>
<DatasetSelectModal
isOpen={isOpenKbSelect}
activeDatasets={item.value}
onChange={(e) => {
onChangeNode({
moduleId,
key: item.key,
type: 'updateInput',
value: {
...item,
value: e
}
});
}}
onClose={onCloseKbSelect}
/>
{isOpenKbSelect && (
<DatasetSelectModal
isOpen={isOpenKbSelect}
defaultSelectedDatasets={item.value}
onChange={(e) => {
onChangeNode({
moduleId,
key: item.key,
type: 'updateInput',
value: {
...item,
value: e
}
});
}}
onClose={onCloseKbSelect}
/>
)}
</>
);
});

View File

@@ -8,7 +8,8 @@ import SourceHandle from './SourceHandle';
import MyIcon from '@/components/Icon';
import dynamic from 'next/dynamic';
import { onChangeNode } from '../../FlowProvider';
import { SystemOutputEnum } from '@/constants/app';
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import { useTranslation } from 'next-i18next';
import type { EditFieldType, EditFieldModeType } from '../modules/FieldEditModal';
const FieldEditModal = dynamic(() => import('../modules/FieldEditModal'));
@@ -25,7 +26,8 @@ export const Label = ({
outputs: FlowNodeOutputItemType[];
editFiledType?: EditFieldModeType;
}) => {
const { label, description, edit } = item;
const { t } = useTranslation();
const { label = '', description, edit } = item;
const [editField, setEditField] = useState<EditFieldType>();
return (
@@ -71,11 +73,11 @@ export const Label = ({
</>
)}
{description && (
<MyTooltip label={description} forceShow>
<MyTooltip label={t(description)} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} mr={1} />
</MyTooltip>
)}
<Box>{label}</Box>
<Box>{t(label)}</Box>
{!!editField && (
<FieldEditModal
@@ -123,8 +125,8 @@ const RenderOutput = ({
const sortOutput = useMemo(
() =>
[...flowOutputList].sort((a, b) => {
if (a.key === SystemOutputEnum.finish) return -1;
if (b.key === SystemOutputEnum.finish) return 1;
if (a.key === ModuleOutputKeyEnum.finish) return -1;
if (b.key === ModuleOutputKeyEnum.finish) return 1;
return 0;
}),
[flowOutputList]

View File

@@ -1,26 +1,26 @@
import React, { useMemo, useTransition } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import { Handle, Position } from 'reactflow';
import { FlowValueTypeStyle, FlowValueTypeTip } from '@/constants/flow';
import { FlowValueTypeStyle, FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
import MyTooltip from '@/components/MyTooltip';
import { useTranslation } from 'next-i18next';
import { FlowNodeValTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
interface Props extends BoxProps {
handleKey: string;
valueType?: `${FlowNodeValTypeEnum}`;
valueType?: `${ModuleDataTypeEnum}`;
}
const SourceHandle = ({ handleKey, valueType, ...props }: Props) => {
const { t } = useTranslation();
const valType = valueType ?? FlowNodeValTypeEnum.any;
const valType = valueType ?? ModuleDataTypeEnum.any;
const valueStyle = useMemo(
() =>
valueType
? FlowValueTypeStyle[valueType]
: (FlowValueTypeStyle[FlowNodeValTypeEnum.any] as any),
: (FlowValueTypeStyle[ModuleDataTypeEnum.any] as any),
[valueType]
);
@@ -34,8 +34,8 @@ const SourceHandle = ({ handleKey, valueType, ...props }: Props) => {
>
<MyTooltip
label={t('app.module.type', {
type: t(FlowValueTypeTip[valType].label),
example: FlowValueTypeTip[valType].example
type: t(FlowValueTypeMap[valType].label),
example: FlowValueTypeMap[valType].example
})}
>
<Handle

View File

@@ -1,26 +1,26 @@
import React, { useMemo } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import { Handle, OnConnect, Position } from 'reactflow';
import { FlowValueTypeStyle, FlowValueTypeTip } from '@/constants/flow';
import { FlowValueTypeStyle, FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
import MyTooltip from '@/components/MyTooltip';
import { useTranslation } from 'next-i18next';
import { FlowNodeValTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
interface Props extends BoxProps {
handleKey: string;
valueType?: `${FlowNodeValTypeEnum}`;
valueType?: `${ModuleDataTypeEnum}`;
onConnect?: OnConnect;
}
const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
const { t } = useTranslation();
const valType = valueType ?? FlowNodeValTypeEnum.any;
const valType = valueType ?? ModuleDataTypeEnum.any;
const valueStyle = useMemo(
() =>
valueType
? FlowValueTypeStyle[valueType]
: (FlowValueTypeStyle[FlowNodeValTypeEnum.any] as any),
: (FlowValueTypeStyle[ModuleDataTypeEnum.any] as any),
[valueType]
);
@@ -35,8 +35,8 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
>
<MyTooltip
label={t('app.module.type', {
type: t(FlowValueTypeTip[valType].label),
example: FlowValueTypeTip[valType].example
type: t(FlowValueTypeMap[valType].label),
example: FlowValueTypeMap[valType].example
})}
>
<Handle

View File

@@ -2,13 +2,13 @@ import React, { useEffect } from 'react';
import ReactFlow, { Background, Controls, ReactFlowProvider } from 'reactflow';
import { Box, Flex, IconButton, useDisclosure } from '@chakra-ui/react';
import { SmallCloseIcon } from '@chakra-ui/icons';
import { edgeOptions, connectionLineStyle } from '@/constants/flow';
import { edgeOptions, connectionLineStyle } from '@/web/core/modules/constants/flowUi';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import dynamic from 'next/dynamic';
import ButtonEdge from './components/modules/ButtonEdge';
import TemplateList, { type ModuleTemplateProps } from './TemplateList';
import ModuleTemplateList, { type ModuleTemplateProps } from './ModuleTemplateList';
import { useFlowProviderStore } from './FlowProvider';
import 'reactflow/dist/style.css';
@@ -27,8 +27,8 @@ const nodeTypes = {
[FlowNodeTypeEnum.contentExtract]: dynamic(() => import('./components/nodes/NodeExtract')),
[FlowNodeTypeEnum.httpRequest]: dynamic(() => import('./components/nodes/NodeHttp')),
[FlowNodeTypeEnum.runApp]: NodeSimple,
[FlowNodeTypeEnum.pluginInput]: dynamic(() => import('./components/nodes/NodeInput')),
[FlowNodeTypeEnum.pluginOutput]: dynamic(() => import('./components/nodes/NodeOutput')),
[FlowNodeTypeEnum.pluginInput]: dynamic(() => import('./components/nodes/NodePluginInput')),
[FlowNodeTypeEnum.pluginOutput]: dynamic(() => import('./components/nodes/NodePluginOutput')),
[FlowNodeTypeEnum.pluginModule]: NodeSimple
};
const edgeTypes = {
@@ -40,7 +40,7 @@ type Props = {
} & ModuleTemplateProps;
const Container = React.memo(function Container(props: Props) {
const { modules = [], Header, systemTemplates, pluginTemplates, show2Plugin } = props;
const { modules = [], Header, systemTemplates, pluginTemplates } = props;
const {
isOpen: isOpenTemplate,
@@ -114,10 +114,9 @@ const Container = React.memo(function Container(props: Props) {
<Controls position={'bottom-right'} style={{ display: 'flex' }} showInteractive={false} />
</ReactFlow>
<TemplateList
<ModuleTemplateList
systemTemplates={systemTemplates}
pluginTemplates={pluginTemplates}
show2Plugin={show2Plugin}
isOpen={isOpenTemplate}
onClose={onCloseTemplate}
/>

View File

@@ -1,209 +0,0 @@
import React, { useState } from 'react';
import {
Box,
Button,
ModalHeader,
ModalFooter,
ModalBody,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
Flex,
Switch,
Input,
Grid,
FormControl,
useTheme
} from '@chakra-ui/react';
import { SmallAddIcon } from '@chakra-ui/icons';
import { VariableInputEnum } from '@/constants/app';
import type { VariableItemType } from '@/types/app';
import MyIcon from '@/components/Icon';
import { useForm } from 'react-hook-form';
import { useFieldArray } from 'react-hook-form';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
import MyModal from '@/components/MyModal';
const VariableTypeList = [
{ label: '文本', icon: 'settingLight', key: VariableInputEnum.input },
{ label: '下拉单选', icon: 'settingLight', key: VariableInputEnum.select }
];
export type VariableFormType = {
variable: VariableItemType;
};
const VariableEditModal = ({
defaultVariable,
onClose,
onSubmit
}: {
defaultVariable: VariableItemType;
onClose: () => void;
onSubmit: (data: VariableFormType) => void;
}) => {
const theme = useTheme();
const [refresh, setRefresh] = useState(false);
const { reset, getValues, setValue, register, control, handleSubmit } = useForm<VariableFormType>(
{
defaultValues: {
variable: defaultVariable
}
}
);
const {
fields: selectEnums,
append: appendEnums,
remove: removeEnums
} = useFieldArray({
control,
name: 'variable.enums'
});
return (
<MyModal isOpen={true} onClose={onClose}>
<ModalHeader display={'flex'}>
<MyIcon name={'variable'} mr={2} w={'24px'} color={'#FF8A4C'} />
</ModalHeader>
<ModalBody>
<Flex alignItems={'center'}>
<Box w={'70px'}></Box>
<Switch {...register('variable.required')} />
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box w={'80px'}></Box>
<Input {...register('variable.label', { required: '变量名不能为空' })} />
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box w={'80px'}> key</Box>
<Input {...register('variable.key', { required: '变量 key 不能为空' })} />
</Flex>
<Box mt={5} mb={2}>
</Box>
<Grid gridTemplateColumns={'repeat(2,130px)'} gridGap={4}>
{VariableTypeList.map((item) => (
<Flex
key={item.key}
px={4}
py={1}
border={theme.borders.base}
borderRadius={'md'}
cursor={'pointer'}
{...(item.key === getValues('variable.type')
? {
bg: 'myWhite.600'
}
: {
_hover: {
boxShadow: 'md'
},
onClick: () => {
setValue('variable.type', item.key);
setRefresh(!refresh);
}
})}
>
<MyIcon name={item.icon as any} w={'16px'} />
<Box ml={3}>{item.label}</Box>
</Flex>
))}
</Grid>
{getValues('variable.type') === VariableInputEnum.input && (
<>
<Box mt={5} mb={2}>
</Box>
<Box>
<NumberInput max={100} min={1} step={1} position={'relative'}>
<NumberInputField
{...register('variable.maxLen', {
min: 1,
max: 100,
valueAsNumber: true
})}
max={100}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Box>
</>
)}
{getValues('variable.type') === VariableInputEnum.select && (
<>
<Box mt={5} mb={2}>
</Box>
<Box>
{selectEnums.map((item, i) => (
<Flex key={item.id} mb={2} alignItems={'center'}>
<FormControl>
<Input
{...register(`variable.enums.${i}.value`, {
required: '选项内容不能为空'
})}
/>
</FormControl>
<MyIcon
ml={3}
name={'delete'}
w={'16px'}
cursor={'pointer'}
p={2}
borderRadius={'lg'}
_hover={{ bg: 'red.100' }}
onClick={() => removeEnums(i)}
/>
</Flex>
))}
</Box>
<Button
variant={'solid'}
w={'100%'}
textAlign={'left'}
leftIcon={<SmallAddIcon />}
bg={'myGray.100 !important'}
onClick={() => appendEnums({ value: '' })}
>
</Button>
</>
)}
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
</Button>
<Button onClick={handleSubmit(onSubmit)}></Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(VariableEditModal);
export const defaultVariable: VariableItemType = {
id: nanoid(),
key: 'key',
label: 'label',
type: VariableInputEnum.input,
required: true,
maxLen: 50,
enums: [{ value: '' }]
};
export const addVariable = () => {
const newVariable = { ...defaultVariable, key: nanoid(), id: nanoid() };
return newVariable;
};

View File

@@ -219,16 +219,23 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
}}
/>
)}
<MyModal isOpen={!!apiKey} w={['400px', '600px']} onClose={() => setApiKey('')}>
<Box py={3} px={5}>
<Box fontWeight={'bold'} fontSize={'2xl'}>
API
<MyModal
isOpen={!!apiKey}
w={['400px', '600px']}
iconSrc="/imgs/modal/key.svg"
title={
<Box>
<Box fontWeight={'bold'} fontSize={'xl'}>
API
</Box>
<Box fontSize={'sm'} color={'myGray.600'}>
~
</Box>
</Box>
<Box fontSize={'sm'} color={'myGray.600'}>
~
</Box>
</Box>
<ModalBody>
}
onClose={() => setApiKey('')}
>
<ModalBody pt={5}>
<Flex
bg={'myGray.100'}
px={3}
@@ -236,6 +243,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
whiteSpace={'pre-wrap'}
wordBreak={'break-all'}
cursor={'pointer'}
borderRadius={'md'}
onClick={() => copyData(apiKey)}
>
<Box flex={1}>{apiKey}</Box>
@@ -292,7 +300,11 @@ function EditKeyModal({
});
return (
<MyModal isOpen={true} title={isEdit ? t('outlink.Edit API Key') : t('outlink.Create API Key')}>
<MyModal
isOpen={true}
iconSrc="/imgs/modal/key.svg"
title={isEdit ? t('outlink.Edit API Key') : t('outlink.Create API Key')}
>
<ModalBody>
<Flex alignItems={'center'}>
<Box flex={'0 0 90px'}>{t('Name')}:</Box>

View File

@@ -98,6 +98,7 @@ function EditModal({
<MyModal
isOpen
onClose={onClose}
iconSrc="/imgs/modal/team.svg"
title={defaultData.id ? t('user.team.Update Team') : t('user.team.Create Team')}
>
<ModalBody>

View File

@@ -68,13 +68,14 @@ const InviteModal = ({
return (
<MyModal
isOpen
iconSrc="/imgs/modal/team.svg"
title={
<>
<Box>
<Box>{t('user.team.Invite Member')}</Box>
<Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}>
{t('user.team.Invite Member Tips')}
</Box>
</>
</Box>
}
maxW={['90vw', '400px']}
overflow={'unset'}

View File

@@ -64,7 +64,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
const {
data: myTeams = [],
isLoading: isLoadingTeams,
isFetching: isLoadingTeams,
refetch: refetchTeam
} = useQuery(['getTeams', userInfo?._id], () => getTeamList(TeamMemberStatusEnum.active));
const defaultTeam = useMemo(

View File

@@ -53,13 +53,14 @@ const UpdateInviteModal = () => {
return (
<MyModal
isOpen={inviteList && inviteList.length > 0}
iconSrc="/imgs/modal/team.svg"
title={
<>
<Box>
<Box>{t('user.team.Processing invitations')}</Box>
<Box fontWeight={'normal'} fontSize={'sm'} color={'myGray.500'}>
{t('user.team.Processing invitations Tips', { amount: inviteList?.length })}
</Box>
</>
</Box>
}
maxW={['90vw', '500px']}
>