4.7-production (#1053)

* 4.7-alpha3 (#62)

* doc

* Optimize possible null Pointers and parts of Ux

* fix: mulity index training error

* feat: doc and rename question guide

* fix ios speech input (#59)

* fix: prompt editor variables nowrap (#61)

* change openapi import in http module with curl import (#60)

* chore(ui): dataset import modal ui (#58)

* chore(ui): dataset import modal ui

* use component

* fix height

* 4.7 (#63)

* fix: claude3 image type verification failed (#1038) (#1040)

* perf: curl import modal

* doc img

* perf: adapt cohere rerank

* perf: code

* perf: input style

* doc

---------

Co-authored-by: xiaotian <dimsky@163.com>

* fix: ts

* docker deploy

* perf: prompt call

* doc

* ts

* finish ui

* perf: outlink detail ux

* perf: user schema

* fix: plugin update

* feat: get current time plugin

* fix: ts

* perf: fetch anamation

* perf: mark ux

* doc

* perf: select app ux

* fix: split text custom string conflict

* peref: inform readed

* doc

* memo flow component

* perf: version

* faq

* feat: flow max runtimes

* feat: similarity tip

* feat: auto detect file encoding

* Supports asymmetric vector model

* fix: ts

* perf: max w

* move code

* perf: hide whisper

* fix: ts

* feat: system msg modal

* perf: catch error

* perf: inform tip

* fix: inform

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
Co-authored-by: xiaotian <dimsky@163.com>
This commit is contained in:
Archer
2024-03-26 12:09:31 +08:00
committed by GitHub
parent ef15ca894e
commit 911512b36d
180 changed files with 2179 additions and 1361 deletions

View File

@@ -1,7 +1,7 @@
import React, { useRef } from 'react';
import { ModalBody, Textarea, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { updateChatUserFeedback } from '@/web/core/chat/api';

View File

@@ -10,7 +10,7 @@ import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { customAlphabet } from 'nanoid';
import { ChatFileTypeEnum } from '@fastgpt/global/core/chat/constants';
import { addDays } from 'date-fns';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { ChatBoxInputFormType, ChatBoxInputType, UserInputFileItemType } from './type';
@@ -61,7 +61,7 @@ const MessageInput = ({
renderAudioGraph,
stream
} = useSpeech({ shareId, outLinkUid, teamId, teamToken });
const { isPc } = useSystemStore();
const { isPc, whisperModel } = useSystemStore();
const canvasRef = useRef<HTMLCanvasElement>(null);
const { t } = useTranslation();
@@ -369,7 +369,7 @@ const MessageInput = ({
bottom={['10px', '12px']}
>
{/* voice-input */}
{!shareId && !havInput && !isChatting && (
{!shareId && !havInput && !isChatting && !!whisperModel && (
<>
<canvas
ref={canvasRef}
@@ -402,7 +402,7 @@ const MessageInput = ({
name={isSpeaking ? 'core/chat/stopSpeechFill' : 'core/chat/recordFill'}
width={['20px', '22px']}
height={['20px', '22px']}
color={'primary.500'}
color={isSpeaking ? 'primary.500' : 'myGray.600'}
/>
</MyTooltip>
</Flex>
@@ -410,7 +410,7 @@ const MessageInput = ({
)}
{/* send and stop icon */}
{isSpeaking ? (
<Box color={'#5A646E'} w={'36px'} textAlign={'right'}>
<Box color={'#5A646E'} w={'36px'} textAlign={'right'} whiteSpace={'nowrap'}>
{speakingTimeString}
</Box>
) : (

View File

@@ -32,8 +32,6 @@ const SelectMarkCollection = ({
}) => {
const { t } = useTranslation();
const theme = useTheme();
const [selectedDatasetId, setSelectedDatasetId] = useState<string>();
const [selectedDatasetCollectionIds, setSelectedDatasetCollectionIds] = useState<string[]>([]);
const { paths, setParentId, datasets, isFetching } = useDatasetSelect();
return (
@@ -45,17 +43,18 @@ const SelectMarkCollection = ({
paths={paths}
onClose={onClose}
setParentId={setParentId}
isLoading={isFetching}
tips={t('core.chat.Select dataset Desc')}
>
<ModalBody flex={'1 0 0'} overflowY={'auto'}>
<Grid
display={'grid'}
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3}
userSelect={'none'}
>
{datasets.map((item) =>
(() => {
const selected = selectedDatasetId === item._id;
return (
<Card
key={item._id}
@@ -67,16 +66,11 @@ const SelectMarkCollection = ({
_hover={{
boxShadow: 'md'
}}
{...(selected
? {
bg: 'primary.200'
}
: {})}
onClick={() => {
if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id);
} else {
setSelectedDatasetId(item._id);
setAdminMarkData({ ...adminMarkData, datasetId: item._id });
}
}}
>
@@ -104,29 +98,21 @@ const SelectMarkCollection = ({
</Flex>
)}
</ModalBody>
<ModalFooter>
<Button
isLoading={isFetching}
isDisabled={!selectedDatasetId}
onClick={() => {
setAdminMarkData({ ...adminMarkData, datasetId: selectedDatasetId });
}}
>
{t('common.Next Step')}
</Button>
</ModalFooter>
</DatasetSelectModal>
)}
{/* select collection */}
{adminMarkData.datasetId && !adminMarkData.collectionId && (
{adminMarkData.datasetId && (
<SelectCollections
datasetId={adminMarkData.datasetId}
type={'collection'}
title={t('dataset.collections.Select One Collection To Store')}
onClose={onClose}
onChange={({ collectionIds }) => {
setSelectedDatasetCollectionIds(collectionIds);
setAdminMarkData({
...adminMarkData,
collectionId: collectionIds[0]
});
}}
CustomFooter={
<ModalFooter>
@@ -142,17 +128,6 @@ const SelectMarkCollection = ({
>
{t('common.Last Step')}
</Button>
<Button
isDisabled={selectedDatasetCollectionIds.length === 0}
onClick={() => {
setAdminMarkData({
...adminMarkData,
collectionId: selectedDatasetCollectionIds[0]
});
}}
>
{t('common.Next Step')}
</Button>
</ModalFooter>
}
/>
@@ -161,7 +136,12 @@ const SelectMarkCollection = ({
{/* input data */}
{adminMarkData.datasetId && adminMarkData.collectionId && (
<InputDataModal
onClose={onClose}
onClose={() => {
setAdminMarkData({
...adminMarkData,
collectionId: undefined
});
}}
collectionId={adminMarkData.collectionId}
dataId={adminMarkData.dataId}
defaultValue={{

View File

@@ -90,13 +90,14 @@ const ChatItem = ({
</>
);
}
/* AI */
return (
<Flex flexDirection={'column'} gap={2}>
{chat.value.map((value, i) => {
const key = `${chat.dataId}-ai-${i}`;
if (value.text) {
let source = value.text?.content || '';
let source = (value.text?.content || '').trim();
if (!source && chat.value.length > 1) return <></>;
@@ -137,6 +138,7 @@ ${JSON.stringify(questionGuides)}`;
return tool.response;
}
})();
return (
<Box key={tool.id}>
<Accordion allowToggle>
@@ -169,7 +171,7 @@ ${JSON.stringify(questionGuides)}`;
maxH={'500px'}
overflowY={'auto'}
>
{toolParams && (
{toolParams && toolParams !== '{}' && (
<Markdown
source={`~~~json#Input
${toolParams}`}

View File

@@ -652,7 +652,7 @@ const ChatBox = (
} catch (error) {}
};
},
[appId, chatId, feedbackType, outLinkUid, shareId]
[appId, chatId, feedbackType, outLinkUid, shareId, teamId, teamToken]
);
const onCloseUserLike = useCallback(
(chat: ChatSiteItemType) => {
@@ -676,7 +676,7 @@ const ChatBox = (
});
};
},
[appId, chatId, feedbackType]
[appId, chatId, feedbackType, teamId, teamToken]
);
const onADdUserDislike = useCallback(
(chat: ChatSiteItemType) => {
@@ -713,7 +713,7 @@ const ChatBox = (
return () => setFeedbackId(chat.dataId);
}
},
[appId, chatId, feedbackType, outLinkUid, shareId]
[appId, chatId, feedbackType, outLinkUid, shareId, teamId, teamToken]
);
const onReadUserDislike = useCallback(
(chat: ChatSiteItemType) => {
@@ -938,7 +938,7 @@ const ChatBox = (
icon="core/app/markLight"
text={t('core.chat.Admin Mark Content')}
/>
<Box whiteSpace={'pre'}>
<Box whiteSpace={'pre-wrap'}>
<Box color={'black'}>{item.adminFeedback.q}</Box>
<Box color={'myGray.600'}>{item.adminFeedback.a}</Box>
</Box>

View File

@@ -14,6 +14,8 @@ import Navbar from './navbar';
import NavbarPhone from './navbarPhone';
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal'));
const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal'));
const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform'));
const pcUnShowLayoutRoute: Record<string, boolean> = {
'/': true,
@@ -70,10 +72,12 @@ const Layout = ({ children }: { children: JSX.Element }) => {
};
}, [setScreenWidth]);
const { data: unread = 0 } = useQuery(['getUnreadCount'], getUnreadCount, {
const { data, refetch: refetchUnRead } = useQuery(['getUnreadCount'], getUnreadCount, {
enabled: !!userInfo && !!feConfigs.isPlus,
refetchInterval: 10000
});
const unread = data?.unReadCount || 0;
const importantInforms = data?.importantInforms || [];
const isHideNavbar = !!pcUnShowLayoutRoute[router.pathname];
@@ -117,6 +121,10 @@ const Layout = ({ children }: { children: JSX.Element }) => {
{!!userInfo && <UpdateInviteModal />}
{isNotSufficientModal && !isHideNavbar && <NotSufficientModal />}
{!!userInfo && <SystemMsgModal />}
{!!userInfo && importantInforms.length > 0 && (
<ImportantInform informs={importantInforms} refetch={refetchUnRead} />
)}
</Box>
<Loading loading={loading} zIndex={999999} />
</>

View File

@@ -24,7 +24,7 @@ const QuestionGuide = ({ text }: { text: string }) => {
return questionGuides.length > 0 ? (
<Box mt={2}>
<ChatBoxDivider icon="core/chat/QGFill" text={t('core.chat.Question Guide Tips')} />
<ChatBoxDivider icon="core/chat/QGFill" text={t('core.chat.Question Guide')} />
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
{questionGuides.map((text) => (
<Flex

View File

@@ -1,7 +1,17 @@
import React, { useRef, useState } from 'react';
import { Menu, MenuList, MenuItem, Box, useOutsideClick, MenuButton } from '@chakra-ui/react';
import {
Menu,
MenuList,
MenuItem,
Box,
useOutsideClick,
MenuButton,
MenuItemProps
} from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
type MenuItemType = 'primary' | 'danger';
interface Props {
width?: number | string;
offset?: [number, number];
@@ -11,6 +21,7 @@ interface Props {
isActive?: boolean;
label: string | React.ReactNode;
icon?: string;
type?: MenuItemType;
onClick: () => any;
}[];
}
@@ -22,15 +33,25 @@ const MyMenu = ({
Button,
menuList
}: Props) => {
const menuItemStyles = {
const typeMapStyle: Record<MenuItemType, MenuItemProps> = {
primary: {
_hover: {
backgroundColor: 'primary.50',
color: 'primary.600'
}
},
danger: {
_hover: {
color: 'red.600',
background: 'red.1'
}
}
};
const menuItemStyles: MenuItemProps = {
borderRadius: 'sm',
py: 3,
display: 'flex',
alignItems: 'center',
_hover: {
backgroundColor: 'myGray.05',
color: 'primary.600'
}
alignItems: 'center'
};
const ref = useRef<HTMLDivElement>(null);
const closeTimer = useRef<any>();
@@ -92,6 +113,7 @@ const MyMenu = ({
<MenuItem
key={i}
{...menuItemStyles}
{...typeMapStyle[item.type || 'primary']}
onClick={(e) => {
e.stopPropagation();
setIsOpen(false);

View File

@@ -42,16 +42,17 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =>
borderRadius={'md'}
px={3}
mb={2}
fontWeight={'medium'}
alignItems={'center'}
{...(activeId === item.id
? {
bg: ' primary.100 !important',
fontWeight: 'bold',
color: 'primary.600 ',
cursor: 'default'
}
: {
cursor: 'pointer'
cursor: 'pointer',
color: 'myGray.600'
})}
_hover={{
bg: 'myGray.05'
@@ -61,7 +62,7 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =>
onChange(item.id);
}}
>
<MyIcon mr={2} name={item.icon as IconNameType} w={'16px'} />
<MyIcon mr={2} name={item.icon as IconNameType} w={'20px'} />
{item.label}
</Flex>
))}

View File

@@ -2,14 +2,22 @@ import React from 'react';
import { Box, Flex, useTheme, Grid, type GridProps, theme, Image } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast';
// @ts-ignore
interface Props extends GridProps {
list: { icon?: string; title: string | React.ReactNode; desc?: string; value: any }[];
list: {
icon?: string;
title: string | React.ReactNode;
desc?: string;
value: any;
forbidTip?: string; // If this value is exists, it will be prompted to disable when clicked
}[];
iconSize?: string;
align?: 'top' | 'center';
value: any;
hiddenCircle?: boolean;
onChange: (e: any) => void;
}
@@ -25,6 +33,8 @@ const MyRadio = ({
}: Props) => {
const { t } = useTranslation();
const theme = useTheme();
const { toast } = useToast();
return (
<Grid gridGap={[3, 5]} fontSize={['sm', 'md']} {...props}>
{list.map((item) => (
@@ -73,7 +83,16 @@ const MyRadio = ({
borderColor: 'myGray.200'
})
}}
onClick={() => onChange(item.value)}
onClick={() => {
if (item.forbidTip) {
toast({
status: 'warning',
title: item.forbidTip
});
} else {
onChange(item.value);
}
}}
>
{!!item.icon && (
<>

View File

@@ -30,7 +30,8 @@ const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: P
shouldWrapChildren={false}
>
<Box
color={'myGray.600'}
color={'myGray.900'}
fontWeight={'medium'}
display={'inline-flex'}
whiteSpace={'nowrap'}
{...(canPreview

View File

@@ -5,6 +5,7 @@ import React, { Dispatch, useMemo, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Box } from '@chakra-ui/react';
import ParentPaths from '@/components/common/ParentPaths';
import MyBox from '@/components/common/MyBox';
type PathItemType = {
parentId: string;
@@ -17,6 +18,7 @@ const DatasetSelectContainer = ({
paths,
onClose,
tips,
isLoading,
children
}: {
isOpen: boolean;
@@ -24,6 +26,7 @@ const DatasetSelectContainer = ({
paths: PathItemType[];
onClose: () => void;
tips?: string | null;
isLoading?: boolean;
children: React.ReactNode;
}) => {
const { t } = useTranslation();
@@ -57,7 +60,9 @@ const DatasetSelectContainer = ({
maxW={['90vw', '900px']}
isCentered
>
{children}
<MyBox isLoading={isLoading} h={'100%'}>
{children}
</MyBox>
</MyModal>
);
};

View File

@@ -81,6 +81,8 @@ const DatasetParamsModal = ({
const datasetSearchUsingCfrForm = watch('datasetSearchUsingExtensionQuery');
const queryExtensionModel = watch('datasetSearchExtensionModel');
const cfbBgDesc = watch('datasetSearchExtensionBg');
const usingReRankWatch = watch('usingReRank');
const searchModeWatch = watch('searchMode');
const chatModelSelectList = (() =>
llmModelList
@@ -97,16 +99,10 @@ const DatasetParamsModal = ({
const showSimilarity = useMemo(() => {
if (similarity === undefined) return false;
if (
getValues('searchMode') === DatasetSearchModeEnum.fullTextRecall &&
!getValues('usingReRank')
)
return false;
if (getValues('searchMode') === DatasetSearchModeEnum.mixedRecall && !getValues('usingReRank'))
return false;
return true;
}, [getValues, similarity]);
if (usingReRankWatch) return true;
if (searchModeWatch === DatasetSearchModeEnum.embedding) return true;
return false;
}, [searchModeWatch, similarity, usingReRankWatch]);
const showReRank = useMemo(() => {
return usingReRank !== undefined && reRankModelList.length > 0;
@@ -155,64 +151,57 @@ const DatasetParamsModal = ({
setRefresh(!refresh);
}}
/>
{showReRank && (
<>
<Divider my={4} />
<Flex
alignItems={'center'}
cursor={'pointer'}
userSelect={'none'}
py={3}
pl={'14px'}
pr={'16px'}
border={theme.borders.sm}
borderWidth={'1.5px'}
borderRadius={'md'}
position={'relative'}
{...(getValues('usingReRank')
? {
borderColor: 'primary.400'
}
: {})}
onClick={(e) => {
if (
teamPlanStatus?.standardConstants &&
!teamPlanStatus?.standardConstants?.permissionReRank
) {
return toast({
status: 'warning',
title: t('support.team.limit.No permission rerank')
});
<>
<Divider my={4} />
<Flex
alignItems={'center'}
cursor={'pointer'}
userSelect={'none'}
py={3}
pl={'14px'}
pr={'16px'}
border={theme.borders.sm}
borderWidth={'1.5px'}
borderRadius={'md'}
position={'relative'}
{...(getValues('usingReRank')
? {
borderColor: 'primary.400'
}
setValue('usingReRank', !getValues('usingReRank'));
setRefresh((state) => !state);
}}
>
<MyIcon name="core/dataset/rerank" w={'18px'} mr={'14px'} />
<Box pr={2} color={'myGray.800'} flex={'1 0 0'}>
<Box>{t('core.dataset.search.ReRank')}</Box>
<Box fontSize={['xs', 'sm']} color={'myGray.500'}>
{t('core.dataset.search.ReRank desc')}
</Box>
: {})}
onClick={(e) => {
if (!showReRank) {
return toast({
status: 'warning',
title: t('core.ai.Not deploy rerank model')
});
}
if (
teamPlanStatus?.standardConstants &&
!teamPlanStatus?.standardConstants?.permissionReRank
) {
return toast({
status: 'warning',
title: t('support.team.limit.No permission rerank')
});
}
setValue('usingReRank', !getValues('usingReRank'));
setRefresh((state) => !state);
}}
>
<MyIcon name="core/dataset/rerank" w={'18px'} mr={'14px'} />
<Box pr={2} color={'myGray.800'} flex={'1 0 0'}>
<Box>{t('core.dataset.search.ReRank')}</Box>
<Box fontSize={['xs', 'sm']} color={'myGray.500'}>
{t('core.dataset.search.ReRank desc')}
</Box>
<Box position={'relative'} w={'18px'} h={'18px'}>
<Checkbox
colorScheme="primary"
isChecked={getValues('usingReRank')}
size="lg"
/>
<Box
position={'absolute'}
top={0}
right={0}
bottom={0}
left={0}
zIndex={1}
></Box>
</Box>
</Flex>
</>
)}
</Box>
<Box position={'relative'} w={'18px'} h={'18px'}>
<Checkbox colorScheme="primary" isChecked={getValues('usingReRank')} size="lg" />
<Box position={'absolute'} top={0} right={0} bottom={0} left={0} zIndex={1}></Box>
</Box>
</Flex>
</>
</>
)}
{currentTabType === SearchSettingTabEnum.limit && (
@@ -243,15 +232,15 @@ const DatasetParamsModal = ({
</Box>
</Box>
)}
{showSimilarity && (
<Box display={['block', 'flex']} mt={10}>
<Box flex={'0 0 120px'} mb={[8, 0]}>
{t('core.dataset.search.Min Similarity')}
<MyTooltip label={t('core.dataset.search.Min Similarity Tips')} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Box flex={1} mx={4}>
<Box display={['block', 'flex']} mt={10}>
<Box flex={'0 0 120px'} mb={[8, 0]}>
{t('core.dataset.search.Min Similarity')}
<MyTooltip label={t('core.dataset.search.Min Similarity Tips')} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Box flex={1} mx={4}>
{showSimilarity ? (
<MySlider
markList={[
{ label: '0', value: 0 },
@@ -266,9 +255,11 @@ const DatasetParamsModal = ({
setRefresh(!refresh);
}}
/>
</Box>
) : (
<Box color={'myGray.500'}>{t('core.dataset.search.No support similarity')}</Box>
)}
</Box>
)}
</Box>
</Box>
)}
{currentTabType === SearchSettingTabEnum.queryExtension && (

View File

@@ -20,7 +20,7 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import { moduleTemplatesList } from '@fastgpt/global/core/module/template/constants';
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
import { useWorkflowStore } from '@/web/core/workflow/store/workflow';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import ParentPaths from '@/components/common/ParentPaths';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
@@ -70,7 +70,7 @@ const ModuleTemplateList = ({ isOpen, onClose }: ModuleTemplateListProps) => {
searchKey ? item.pluginType !== PluginTypeEnum.folder : true
)
};
return map[templateType];
return JSON.stringify(map[templateType]);
}, [basicNodeTemplates, searchKey, systemNodeTemplates, teamPluginNodeTemplates, templateType]);
const { mutate: onChangeTab } = useRequest({
@@ -96,120 +96,125 @@ const ModuleTemplateList = ({ isOpen, onClose }: ModuleTemplateListProps) => {
})
);
return (
<>
<Box
zIndex={2}
display={isOpen ? 'block' : 'none'}
position={'absolute'}
top={0}
left={0}
bottom={0}
w={`${sliderWidth}px`}
onClick={onClose}
/>
<Flex
zIndex={3}
flexDirection={'column'}
position={'absolute'}
top={'10px'}
left={0}
pt={'20px'}
pb={4}
h={isOpen ? 'calc(100% - 20px)' : '0'}
w={isOpen ? ['100%', `${sliderWidth}px`] : '0'}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'0 20px 20px 0'}
transition={'.2s ease'}
userSelect={'none'}
overflow={isOpen ? 'none' : 'hidden'}
>
<Box mb={2} pl={'20px'} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
<RowTabs
list={[
{
icon: 'core/modules/basicNode',
label: t('core.module.template.Basic Node'),
value: TemplateTypeEnum.basic
},
{
icon: 'core/modules/systemPlugin',
label: t('core.module.template.System Plugin'),
value: TemplateTypeEnum.systemPlugin
},
{
icon: 'core/modules/teamPlugin',
label: t('core.module.template.Team Plugin'),
value: TemplateTypeEnum.teamPlugin
}
]}
py={'5px'}
value={templateType}
onChange={onChangeTab}
/>
{/* close icon */}
<IconButton
size={'sm'}
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.700'} />}
w={'26px'}
h={'26px'}
borderColor={'myGray.300'}
variant={'grayBase'}
aria-label={''}
onClick={onClose}
/>
</Flex>
{templateType === TemplateTypeEnum.teamPlugin && (
<Flex mt={2} alignItems={'center'} h={10}>
<InputGroup mr={4} h={'full'}>
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
<MyIcon name={'common/searchLight'} w={'16px'} color={'myGray.500'} ml={3} />
</InputLeftElement>
<Input
h={'full'}
bg={'myGray.50'}
placeholder={t('plugin.Search plugin')}
onChange={debounce((e) => setSearchKey(e.target.value), 200)}
/>
</InputGroup>
<Box flex={1} />
<Flex
alignItems={'center'}
cursor={'pointer'}
_hover={{
color: 'primary.600'
}}
onClick={() => router.push('/plugin/list')}
>
<Box></Box>
<MyIcon name={'common/rightArrowLight'} w={'14px'} />
</Flex>
</Flex>
)}
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && (
<Flex alignItems={'center'} mt={2}>
<ParentPaths
paths={[currentParent]}
FirstPathDom={null}
onClick={() => {
setCurrentParent(undefined);
}}
fontSize="md"
const Render = useMemo(() => {
const parseTemplates = JSON.parse(templates) as FlowNodeTemplateType[];
return (
<>
<Box
zIndex={2}
display={isOpen ? 'block' : 'none'}
position={'absolute'}
top={0}
left={0}
bottom={0}
w={`${sliderWidth}px`}
onClick={onClose}
/>
<Flex
zIndex={3}
flexDirection={'column'}
position={'absolute'}
top={'10px'}
left={0}
pt={'20px'}
pb={4}
h={isOpen ? 'calc(100% - 20px)' : '0'}
w={isOpen ? ['100%', `${sliderWidth}px`] : '0'}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'0 20px 20px 0'}
transition={'.2s ease'}
userSelect={'none'}
overflow={isOpen ? 'none' : 'hidden'}
>
<Box mb={2} pl={'20px'} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
<RowTabs
list={[
{
icon: 'core/modules/basicNode',
label: t('core.module.template.Basic Node'),
value: TemplateTypeEnum.basic
},
{
icon: 'core/modules/systemPlugin',
label: t('core.module.template.System Plugin'),
value: TemplateTypeEnum.systemPlugin
},
{
icon: 'core/modules/teamPlugin',
label: t('core.module.template.Team Plugin'),
value: TemplateTypeEnum.teamPlugin
}
]}
py={'5px'}
value={templateType}
onChange={onChangeTab}
/>
{/* close icon */}
<IconButton
size={'sm'}
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.700'} />}
w={'26px'}
h={'26px'}
borderColor={'myGray.300'}
variant={'grayBase'}
aria-label={''}
onClick={onClose}
/>
</Flex>
)}
</Box>
<RenderList
templates={templates}
onClose={onClose}
currentParent={currentParent}
setCurrentParent={setCurrentParent}
/>
</Flex>
</>
);
{templateType === TemplateTypeEnum.teamPlugin && (
<Flex mt={2} alignItems={'center'} h={10}>
<InputGroup mr={4} h={'full'}>
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
<MyIcon name={'common/searchLight'} w={'16px'} color={'myGray.500'} ml={3} />
</InputLeftElement>
<Input
h={'full'}
bg={'myGray.50'}
placeholder={t('plugin.Search plugin')}
onChange={debounce((e) => setSearchKey(e.target.value), 200)}
/>
</InputGroup>
<Box flex={1} />
<Flex
alignItems={'center'}
cursor={'pointer'}
_hover={{
color: 'primary.600'
}}
onClick={() => router.push('/plugin/list')}
>
<Box></Box>
<MyIcon name={'common/rightArrowLight'} w={'14px'} />
</Flex>
</Flex>
)}
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && (
<Flex alignItems={'center'} mt={2}>
<ParentPaths
paths={[currentParent]}
FirstPathDom={null}
onClick={() => {
setCurrentParent(undefined);
}}
fontSize="md"
/>
</Flex>
)}
</Box>
<RenderList
templates={parseTemplates}
onClose={onClose}
currentParent={currentParent}
setCurrentParent={setCurrentParent}
/>
</Flex>
</>
);
}, [currentParent, isOpen, onChangeTab, onClose, router, searchKey, t, templateType, templates]);
return Render;
};
export default React.memo(ModuleTemplateList);

View File

@@ -40,11 +40,10 @@ const SelectAppModal = ({
title={`选择应用${max > 1 ? `(${selectedApps.length}/${max})` : ''}`}
iconSrc="/imgs/module/ai.svg"
onClose={onClose}
minW={'700px'}
position={'relative'}
w={'600px'}
>
<ModalBody
minH={'300px'}
display={'grid'}
gridTemplateColumns={['1fr', 'repeat(3, minmax(0, 1fr))']}
gridGap={4}

View File

@@ -5,12 +5,13 @@ import { Box, Flex, Switch, type SwitchProps } from '@chakra-ui/react';
import React from 'react';
import { useTranslation } from 'next-i18next';
// question generator switch
const QGSwitch = (props: SwitchProps) => {
const { t } = useTranslation();
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/app/questionGuide'} mr={2} w={'20px'} />
<Box>{t('core.app.Next Step Guide')}</Box>
<MyIcon name={'core/chat/QGFill'} mr={2} w={'20px'} />
<Box>{t('core.app.Question Guide')}</Box>
<MyTooltip label={t('core.app.Question Guide Tip')} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>

View File

@@ -124,12 +124,12 @@ const VariableEdit = ({
<TableContainer>
<Table bg={'white'}>
<Thead>
<Tr>
<Th w={'18px !important'} p={0} bg={'myGray.50'} />
<Th bg={'myGray.50'}>{t('core.module.variable.variable name')}</Th>
<Th bg={'myGray.50'}>{t('core.module.variable.key')}</Th>
<Th bg={'myGray.50'}>{t('common.Require Input')}</Th>
<Th bg={'myGray.50'}></Th>
<Tr bg={'myGray.50'}>
<Th w={'18px !important'} p={0} />
<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>

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { NodeProps } from 'reactflow';
import { Box, Button, Flex, Textarea } from '@chakra-ui/react';
import NodeCard from '../render/NodeCard';
@@ -16,93 +16,69 @@ import { useTranslation } from 'next-i18next';
import SourceHandle from '../render/SourceHandle';
import MyTooltip from '@/components/MyTooltip';
import { onChangeNode } from '../../FlowProvider';
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
const NodeCQNode = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation();
const { moduleId, inputs } = data;
return (
<NodeCard minW={'400px'} selected={selected} {...data}>
<Divider text={t('common.Input')} />
<Container>
<RenderInput
moduleId={moduleId}
flowInputList={inputs}
CustomComponent={{
[ModuleInputKeyEnum.agents]: ({ key: agentKey, value = [], ...props }) => {
const agents = value as ClassifyQuestionAgentItemType[];
return (
<Box>
{agents.map((item, i) => (
<Box key={item.key} mb={4}>
<Flex alignItems={'center'}>
<MyTooltip label={t('common.Delete')}>
<MyIcon
mt={1}
mr={2}
name={'minus'}
w={'14px'}
cursor={'pointer'}
color={'myGray.600'}
_hover={{ color: 'red.600' }}
onClick={() => {
onChangeNode({
moduleId,
type: 'updateInput',
key: agentKey,
value: {
...props,
key: agentKey,
value: agents.filter((input) => input.key !== item.key)
}
});
onChangeNode({
moduleId,
type: 'delOutput',
key: item.key
});
}}
/>
</MyTooltip>
<Box flex={1}>{i + 1}</Box>
</Flex>
<Box position={'relative'}>
<Textarea
rows={2}
mt={1}
defaultValue={item.value}
onChange={(e) => {
const newVal = agents.map((val) =>
val.key === item.key
? {
...val,
value: e.target.value
}
: val
);
onChangeNode({
moduleId,
type: 'updateInput',
key: agentKey,
value: {
...props,
key: agentKey,
value: newVal
}
});
}}
/>
<SourceHandle
handleKey={item.key}
valueType={ModuleIOValueTypeEnum.boolean}
/>
</Box>
</Box>
))}
<Button
onClick={() => {
const key = nanoid();
const CustomComponent = useMemo(
() => ({
[ModuleInputKeyEnum.agents]: ({
key: agentKey,
value = [],
...props
}: FlowNodeInputItemType) => {
const agents = value as ClassifyQuestionAgentItemType[];
return (
<Box>
{agents.map((item, i) => (
<Box key={item.key} mb={4}>
<Flex alignItems={'center'}>
<MyTooltip label={t('common.Delete')}>
<MyIcon
mt={1}
mr={2}
name={'minus'}
w={'14px'}
cursor={'pointer'}
color={'myGray.600'}
_hover={{ color: 'red.600' }}
onClick={() => {
onChangeNode({
moduleId,
type: 'updateInput',
key: agentKey,
value: {
...props,
key: agentKey,
value: agents.filter((input) => input.key !== item.key)
}
});
onChangeNode({
moduleId,
type: 'delOutput',
key: item.key
});
}}
/>
</MyTooltip>
<Box flex={1}>{i + 1}</Box>
</Flex>
<Box position={'relative'}>
<Textarea
rows={2}
mt={1}
defaultValue={item.value}
onChange={(e) => {
const newVal = agents.map((val) =>
val.key === item.key
? {
...val,
value: e.target.value
}
: val
);
onChangeNode({
moduleId,
type: 'updateInput',
@@ -110,29 +86,56 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
value: {
...props,
key: agentKey,
value: agents.concat({ value: '', key })
}
});
onChangeNode({
moduleId,
type: 'addOutput',
value: {
key,
label: '',
type: FlowNodeOutputTypeEnum.hidden,
targets: []
value: newVal
}
});
}}
>
{t('core.module.Add question type')}
</Button>
/>
<SourceHandle handleKey={item.key} valueType={ModuleIOValueTypeEnum.boolean} />
</Box>
);
}
}}
/>
</Box>
))}
<Button
onClick={() => {
const key = nanoid();
onChangeNode({
moduleId,
type: 'updateInput',
key: agentKey,
value: {
...props,
key: agentKey,
value: agents.concat({ value: '', key })
}
});
onChangeNode({
moduleId,
type: 'addOutput',
value: {
key,
label: '',
type: FlowNodeOutputTypeEnum.hidden,
targets: []
}
});
}}
>
{t('core.module.Add question type')}
</Button>
</Box>
);
}
}),
[moduleId, t]
);
return (
<NodeCard minW={'400px'} selected={selected} {...data}>
<Divider text={t('common.Input')} />
<Container>
<RenderInput moduleId={moduleId} flowInputList={inputs} CustomComponent={CustomComponent} />
</Container>
</NodeCard>
);

View File

@@ -20,6 +20,7 @@ import SourceHandle from '../render/SourceHandle';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MySlider from '@/components/Slider';
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation();
@@ -27,7 +28,10 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) =>
const { nodes } = useFlowProviderStore();
const { moduleId, inputs, outputs } = data;
const quotes = inputs.filter((item) => item.valueType === ModuleIOValueTypeEnum.datasetQuote);
const quotes = useMemo(
() => inputs.filter((item) => item.valueType === ModuleIOValueTypeEnum.datasetQuote),
[inputs]
);
const tokenLimit = useMemo(() => {
let maxTokens = 3000;
@@ -46,8 +50,8 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) =>
return maxTokens;
}, [llmModelList, nodes]);
const RenderQuoteList = useMemo(
() => (
const RenderQuoteList = useMemo(() => {
return (
<Box>
<Box>
{quotes.map((quote, i) => (
@@ -88,45 +92,45 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) =>
{t('core.module.Dataset quote.Add quote')}
</Button>
</Box>
),
[moduleId, quotes, t]
);
);
}, [moduleId, quotes, t]);
const CustomComponent = useMemo(() => {
console.log(111);
return {
[ModuleInputKeyEnum.datasetMaxTokens]: (item: FlowNodeInputItemType) => (
<Box px={2}>
<MySlider
markList={[
{ label: '100', value: 100 },
{ label: tokenLimit, value: tokenLimit }
]}
width={'100%'}
min={100}
max={tokenLimit}
step={50}
value={item.value}
onChange={(e) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: e
}
});
}}
/>
</Box>
)
};
}, [moduleId, tokenLimit]);
return (
<NodeCard minW={'400px'} selected={selected} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'} position={'relative'}>
<RenderInput
moduleId={moduleId}
flowInputList={inputs}
CustomComponent={{
[ModuleInputKeyEnum.datasetMaxTokens]: (item) => (
<Box px={2}>
<MySlider
markList={[
{ label: '100', value: 100 },
{ label: tokenLimit, value: tokenLimit }
]}
width={'100%'}
min={100}
max={tokenLimit}
step={50}
value={item.value}
onChange={(e) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: e
}
});
}}
/>
</Box>
)
}}
/>
<RenderInput moduleId={moduleId} flowInputList={inputs} CustomComponent={CustomComponent} />
{/* render dataset select */}
{RenderQuoteList}
<Flex position={'absolute'} right={4} top={'60%'}>

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import {
Box,
Button,
@@ -28,6 +28,7 @@ import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/module/node/constan
import { ModuleIOValueTypeEnum } from '@fastgpt/global/core/module/constants';
import { onChangeNode, useFlowProviderStore } from '../../../FlowProvider';
import RenderToolInput from '../../render/RenderToolInput';
import { FlowNodeInputItemType } from '../../../../../../../../../../packages/global/core/module/node/type';
const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
const { inputs, outputs, moduleId } = data;
@@ -36,6 +37,99 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation();
const [editExtractFiled, setEditExtractField] = useState<ContextExtractAgentItemType>();
const CustomComponent = useMemo(
() => ({
[ModuleInputKeyEnum.extractKeys]: ({
value: extractKeys = [],
...props
}: Omit<FlowNodeInputItemType, 'value'> & {
value?: ContextExtractAgentItemType[];
}) => (
<Box>
<Flex alignItems={'center'}>
<Box flex={'1 0 0'}>{t('core.module.extract.Target field')}</Box>
<Button
size={'sm'}
variant={'whitePrimary'}
leftIcon={<AddIcon fontSize={'10px'} />}
onClick={() => setEditExtractField(defaultField)}
>
{t('core.module.extract.Add field')}
</Button>
</Flex>
<Box
mt={2}
borderRadius={'md'}
overflow={'hidden'}
borderWidth={'1px'}
borderBottom="none"
>
<TableContainer>
<Table bg={'white'}>
<Thead>
<Tr>
<Th bg={'myGray.50'}> key</Th>
<Th bg={'myGray.50'}></Th>
<Th bg={'myGray.50'}></Th>
<Th bg={'myGray.50'}></Th>
</Tr>
</Thead>
<Tbody>
{extractKeys.map((item, index) => (
<Tr
key={index}
position={'relative'}
whiteSpace={'pre-wrap'}
wordBreak={'break-all'}
>
<Td>{item.key}</Td>
<Td>{item.desc}</Td>
<Td>{item.required ? '✔' : ''}</Td>
<Td whiteSpace={'nowrap'}>
<MyIcon
mr={3}
name={'common/settingLight'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
setEditExtractField(item);
}}
/>
<MyIcon
name={'delete'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.extractKeys,
value: {
...props,
value: extractKeys.filter((extract) => item.key !== extract.key)
}
});
onChangeNode({
moduleId,
type: 'delOutput',
key: item.key
});
}}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Box>
</Box>
)
}),
[moduleId, t]
);
return (
<NodeCard minW={'400px'} {...data}>
{toolInputs.length > 0 && (
@@ -52,97 +146,7 @@ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
<RenderInput
moduleId={moduleId}
flowInputList={commonInputs}
CustomComponent={{
[ModuleInputKeyEnum.extractKeys]: ({
value: extractKeys = [],
...props
}: {
value?: ContextExtractAgentItemType[];
}) => (
<Box>
<Flex alignItems={'center'}>
<Box flex={'1 0 0'}>{t('core.module.extract.Target field')}</Box>
<Button
size={'sm'}
variant={'whitePrimary'}
leftIcon={<AddIcon fontSize={'10px'} />}
onClick={() => setEditExtractField(defaultField)}
>
{t('core.module.extract.Add field')}
</Button>
</Flex>
<Box
mt={2}
borderRadius={'md'}
overflow={'hidden'}
borderWidth={'1px'}
borderBottom="none"
>
<TableContainer>
<Table bg={'white'}>
<Thead>
<Tr>
<Th bg={'myGray.50'}> key</Th>
<Th bg={'myGray.50'}></Th>
<Th bg={'myGray.50'}></Th>
<Th bg={'myGray.50'}></Th>
</Tr>
</Thead>
<Tbody>
{extractKeys.map((item, index) => (
<Tr
key={index}
position={'relative'}
whiteSpace={'pre-wrap'}
wordBreak={'break-all'}
>
<Td>{item.key}</Td>
<Td>{item.desc}</Td>
<Td>{item.required ? '✔' : ''}</Td>
<Td whiteSpace={'nowrap'}>
<MyIcon
mr={3}
name={'common/settingLight'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
setEditExtractField(item);
}}
/>
<MyIcon
name={'delete'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.extractKeys,
value: {
...props,
value: extractKeys.filter(
(extract) => item.key !== extract.key
)
}
});
onChangeNode({
moduleId,
type: 'delOutput',
key: item.key
});
}}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Box>
</Box>
)
}}
CustomComponent={CustomComponent}
/>
</Container>
</>

View File

@@ -6,8 +6,8 @@ import { onChangeNode } from '../../../FlowProvider';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
import { useToast } from '@fastgpt/web/hooks/useToast';
import yaml from 'js-yaml';
import { useForm } from 'react-hook-form';
import parse from '@bany/curl-to-json';
type RequestMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
const methodMap: { [K in RequestMethod]: string } = {
@@ -18,20 +18,19 @@ const methodMap: { [K in RequestMethod]: string } = {
patch: 'PATCH'
};
const OpenApiImportModal = ({
children,
const CurlImportModal = ({
moduleId,
inputs
inputs,
onClose
}: {
children: React.ReactElement;
moduleId: string;
inputs: FlowNodeInputItemType[];
onClose: () => void;
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { t } = useTranslation();
const { register, handleSubmit } = useForm({
defaultValues: {
openapiContent: ''
curlContent: ''
}
});
@@ -39,60 +38,36 @@ const OpenApiImportModal = ({
const handleFileProcessing = async (content: string) => {
try {
let data;
try {
data = JSON.parse(content);
} catch (jsonError) {
try {
data = yaml.load(content, { schema: yaml.FAILSAFE_SCHEMA });
} catch (yamlError) {
console.error(yamlError);
throw new Error();
}
}
const firstPathName = Object.keys(data.paths)[0];
const firstPathData = data.paths[firstPathName];
const firstRequestMethod = Object.keys(firstPathData)[0];
const firstRequestMethodData = firstPathData[firstRequestMethod];
const firstRequestParameters = firstRequestMethodData.parameters || [];
const pathParams = [];
const headerParams = [];
for (const parameter of firstRequestParameters) {
if (parameter.in === 'path') {
pathParams.push({
key: parameter.name,
type: parameter.schema.type
});
} else {
headerParams.push({
key: parameter.name,
type: parameter.schema.type
});
}
}
const requestBodySchema =
firstRequestMethodData.requestBody?.content?.['application/json']?.schema;
let requestBodyValue = '';
if (requestBodySchema) {
requestBodyValue = JSON.stringify(requestBodySchema, null, 2);
}
const requestUrl = inputs.find((item) => item.key === ModuleInputKeyEnum.httpReqUrl);
const requestMethod = inputs.find((item) => item.key === ModuleInputKeyEnum.httpMethod);
const params = inputs.find((item) => item.key === ModuleInputKeyEnum.httpParams);
const headers = inputs.find((item) => item.key === ModuleInputKeyEnum.httpHeaders);
const jsonBody = inputs.find((item) => item.key === ModuleInputKeyEnum.httpJsonBody);
const parsed = parse(content);
if (!parsed.url) {
throw new Error('url not found');
}
const newParams = Object.keys(parsed.params || {}).map((key) => ({
key,
value: parsed.params?.[key],
type: 'string'
}));
const newHeaders = Object.keys(parsed.header || {}).map((key) => ({
key,
value: parsed.header?.[key],
type: 'string'
}));
const newBody = JSON.stringify(parsed.data, null, 2);
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.httpReqUrl,
value: {
...requestUrl,
value: firstPathName
value: parsed.url
}
});
@@ -102,7 +77,7 @@ const OpenApiImportModal = ({
key: ModuleInputKeyEnum.httpMethod,
value: {
...requestMethod,
value: methodMap[firstRequestMethod.toLowerCase() as RequestMethod] || 'GET'
value: methodMap[parsed.method?.toLowerCase() as RequestMethod] || 'GET'
}
});
@@ -112,7 +87,7 @@ const OpenApiImportModal = ({
key: ModuleInputKeyEnum.httpParams,
value: {
...params,
value: pathParams
value: newParams
}
});
@@ -122,7 +97,7 @@ const OpenApiImportModal = ({
key: ModuleInputKeyEnum.httpHeaders,
value: {
...headers,
value: headerParams
value: newHeaders
}
});
@@ -132,7 +107,7 @@ const OpenApiImportModal = ({
key: ModuleInputKeyEnum.httpJsonBody,
value: {
...jsonBody,
value: requestBodyValue
value: newBody
}
});
@@ -153,33 +128,28 @@ const OpenApiImportModal = ({
};
return (
<>
{children && <Box onClick={onOpen}>{children}</Box>}
<MyModal
isOpen={isOpen}
onClose={onClose}
iconSrc="modal/edit"
title={t('common.Import')}
m={'auto'}
w={500}
>
<ModalBody>
<Textarea
height={400}
maxH={500}
mt={2}
{...register('openapiContent')}
placeholder={t('core.module.http.OpenAPI import placeholder')}
/>
</ModalBody>
<ModalFooter>
<Button onClick={handleSubmit((data) => handleFileProcessing(data.openapiContent))}>
{t('common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
</>
<MyModal
isOpen
onClose={onClose}
iconSrc="modal/edit"
title={t('core.module.http.curl import')}
w={600}
>
<ModalBody>
<Textarea
rows={20}
mt={2}
{...register('curlContent')}
placeholder={t('core.module.http.curl import placeholder')}
/>
</ModalBody>
<ModalFooter>
<Button onClick={handleSubmit((data) => handleFileProcessing(data.curlContent))}>
{t('common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(OpenApiImportModal);
export default React.memo(CurlImportModal);

View File

@@ -17,7 +17,8 @@ import {
Th,
Td,
TableContainer,
Button
Button,
useDisclosure
} from '@chakra-ui/react';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { onChangeNode, useFlowProviderStore } from '../../../FlowProvider';
@@ -39,7 +40,7 @@ import HttpInput from '@fastgpt/web/components/common/Input/HttpInput';
import dynamic from 'next/dynamic';
import MySelect from '@fastgpt/web/components/common/MySelect';
import RenderToolInput from '../../render/RenderToolInput';
const OpenApiImportModal = dynamic(() => import('./OpenApiImportModal'));
const CurlImportModal = dynamic(() => import('./CurlImportModal'));
export const HttpHeaders = [
{ key: 'A-IM', label: 'A-IM' },
@@ -104,6 +105,8 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
const { toast } = useToast();
const [_, startSts] = useTransition();
const { isOpen: isOpenCurl, onOpen: onOpenCurl, onClose: onCloseCurl } = useDisclosure();
const requestMethods = inputs.find((item) => item.key === ModuleInputKeyEnum.httpMethod);
const requestUrl = inputs.find((item) => item.key === ModuleInputKeyEnum.httpReqUrl);
@@ -180,11 +183,9 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
<Box>
<Box mb={2} display={'flex'} justifyContent={'space-between'}>
<Box>{t('core.module.Http request settings')}</Box>
<Box>
<OpenApiImportModal moduleId={moduleId} inputs={inputs}>
<Button variant={'link'}>{t('core.module.http.OpenAPI import')}</Button>
</OpenApiImportModal>
</Box>
<Button variant={'link'} onClick={onOpenCurl}>
{t('core.module.http.curl import')}
</Button>
</Box>
<Flex alignItems={'center'} className="nodrag">
<MySelect
@@ -228,16 +229,18 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
}}
/>
<Input
flex={'1 0 0'}
ml={2}
h={'34px'}
value={requestUrl?.value}
placeholder={t('core.module.input.label.Http Request Url')}
fontSize={'xs'}
w={'350px'}
onChange={onChangeUrl}
onBlur={onBlurUrl}
/>
</Flex>
{isOpenCurl && <CurlImportModal moduleId={moduleId} inputs={inputs} onClose={onCloseCurl} />}
</Box>
);
});

View File

@@ -1,27 +0,0 @@
import React from 'react';
import { NodeProps } from 'reactflow';
import NodeCard from '../render/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';
import { useTranslation } from 'next-i18next';
const NodeRunAPP = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation();
const { moduleId, inputs, outputs } = data;
return (
<NodeCard minW={'350px'} selected={selected} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<RenderInput moduleId={moduleId} flowInputList={inputs} />
</Container>
<Divider text={t('common.Output')} />
<Container>
<RenderOutput moduleId={moduleId} flowOutputList={outputs} />
</Container>
</NodeCard>
);
};
export default React.memo(NodeRunAPP);

View File

@@ -9,6 +9,7 @@ import RenderOutput from '../render/RenderOutput';
import RenderToolInput from '../render/RenderToolInput';
import { useTranslation } from 'next-i18next';
import { useFlowProviderStore } from '../../FlowProvider';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/module/node/constant';
const NodeSimple = ({
data,
@@ -44,7 +45,7 @@ const NodeSimple = ({
</Container>
</>
)}
{outputs.length > 0 && (
{outputs.filter((output) => output.type !== FlowNodeOutputTypeEnum.hidden).length > 0 && (
<>
<Divider text={t('common.Output')} />
<Container>

View File

@@ -21,10 +21,6 @@ const NodeTools = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
<RenderInput moduleId={moduleId} flowInputList={inputs} />
</Container>
<Divider text={t('common.Output')} />
<Container>
<RenderOutput moduleId={moduleId} flowOutputList={outputs} />
</Container>
<Box position={'relative'}>
<Box borderBottomLeftRadius={'md'} borderBottomRadius={'md'} overflow={'hidden'}>
<Divider showBorderBottom={false} text={t('core.module.template.Tool module')} />

View File

@@ -22,7 +22,7 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
const theme = useTheme();
return (
<>
<NodeCard minW={'300px'} selected={selected} {...data}>
<NodeCard minW={'300px'} selected={selected} forbidMenu {...data}>
<Container className="nodrag" borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<WelcomeText data={data} />
<Box pt={4} pb={2}>

View File

@@ -12,7 +12,7 @@ import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getPreviewPluginModule } from '@/web/core/plugin/api';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { ToolTargetHandle } from './ToolHandle';
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';

View File

@@ -13,6 +13,10 @@ const RenderList: {
types: `${FlowNodeInputTypeEnum}`[];
Component: React.ComponentType<RenderInputProps>;
}[] = [
{
types: [FlowNodeInputTypeEnum.triggerAndFinish],
Component: dynamic(() => import('./templates/TriggerAndFinish'))
},
{
types: [FlowNodeInputTypeEnum.input],
Component: dynamic(() => import('./templates/TextInput'))

View File

@@ -1,14 +1,16 @@
import React, { useEffect, useState } from 'react';
import React, { useMemo } from 'react';
import type { RenderInputProps } from '../type';
import { getFlowStore, onChangeNode, useFlowProviderStoreType } from '../../../../FlowProvider';
import { onChangeNode, useFlowProviderStore } from '../../../../FlowProvider';
import { Box, Button, Flex, useDisclosure, useTheme } from '@chakra-ui/react';
import { SelectAppItemType } from '@fastgpt/global/core/module/type';
import Avatar from '@/components/Avatar';
import SelectAppModal from '../../../../SelectAppModal';
import { useTranslation } from 'next-i18next';
const SelectAppRender = ({ item, moduleId }: RenderInputProps) => {
const { t } = useTranslation();
const theme = useTheme();
const [filterAppIds, setFilterAppIds] = useState<useFlowProviderStoreType['filterAppIds']>([]);
const { filterAppIds } = useFlowProviderStore();
const {
isOpen: isOpenSelectApp,
@@ -18,50 +20,65 @@ const SelectAppRender = ({ item, moduleId }: RenderInputProps) => {
const value = item.value as SelectAppItemType | undefined;
useEffect(() => {
async () => {
const { filterAppIds } = await getFlowStore();
setFilterAppIds(filterAppIds);
};
}, []);
const filterAppString = useMemo(() => filterAppIds.join(','), [filterAppIds]);
return (
<>
<Box onClick={onOpenSelectApp}>
{!value ? (
<Button variant={'whitePrimary'} w={'100%'}>
</Button>
) : (
<Flex alignItems={'center'} border={theme.borders.base} borderRadius={'md'} px={3} py={2}>
<Avatar src={value?.logo} />
<Box fontWeight={'bold'} ml={1}>
{value?.name}
</Box>
</Flex>
const Render = useMemo(() => {
return (
<>
<Box onClick={onOpenSelectApp}>
{!value ? (
<Button variant={'whitePrimary'} w={'100%'}>
{t('core.module.Select app')}
</Button>
) : (
<Flex
alignItems={'center'}
border={theme.borders.base}
borderRadius={'md'}
px={3}
py={2}
>
<Avatar src={value?.logo} />
<Box fontWeight={'bold'} ml={1}>
{value?.name}
</Box>
</Flex>
)}
</Box>
{isOpenSelectApp && (
<SelectAppModal
defaultApps={item.value?.id ? [item.value.id] : []}
filterAppIds={filterAppString.split(',')}
onClose={onCloseSelectApp}
onSuccess={(e) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: 'app',
value: {
...item,
value: e[0]
}
});
}}
/>
)}
</Box>
</>
);
}, [
filterAppString,
isOpenSelectApp,
item,
moduleId,
onCloseSelectApp,
onOpenSelectApp,
t,
theme.borders.base,
value
]);
{isOpenSelectApp && (
<SelectAppModal
defaultApps={item.value?.id ? [item.value.id] : []}
filterAppIds={filterAppIds}
onClose={onCloseSelectApp}
onSuccess={(e) => {
onChangeNode({
moduleId,
type: 'updateInput',
key: 'app',
value: {
...item,
value: e[0]
}
});
}}
/>
)}
</>
);
return Render;
};
export default React.memo(SelectAppRender);

View File

@@ -0,0 +1,50 @@
import React, { useMemo } from 'react';
import type { RenderInputProps } from '../type';
import { Box, Flex } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import TargetHandle from '../../TargetHandle';
import SourceHandle from '../../SourceHandle';
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import { useFlowProviderStore } from '../../../../FlowProvider';
const TriggerAndFinish = ({ moduleId }: RenderInputProps) => {
const { t } = useTranslation();
const { nodes } = useFlowProviderStore();
const outputs = useMemo(
() => nodes.find((node) => node.data.moduleId === moduleId)?.data?.outputs || [],
[moduleId, nodes]
);
const hasFinishOutput = useMemo(
() => outputs.some((output) => output.key === ModuleOutputKeyEnum.finish),
[outputs]
);
const Render = useMemo(
() => (
<Flex
className="nodrag"
cursor={'default'}
alignItems={'center'}
justifyContent={'space-between'}
position={'relative'}
>
<Box position={'relative'}>
<TargetHandle handleKey={ModuleInputKeyEnum.switch} valueType={'any'} />
{t('core.module.input.label.switch')}
</Box>
{hasFinishOutput && (
<Box position={'relative'}>
{t('core.module.output.label.running done')}
<SourceHandle handleKey={ModuleOutputKeyEnum.finish} valueType={'boolean'} />
</Box>
)}
</Flex>
),
[hasFinishOutput, t]
);
return Render;
};
export default React.memo(TriggerAndFinish);

View File

@@ -15,7 +15,7 @@ import {
import { useForm } from 'react-hook-form';
import { defaultEditFormData } from './constants';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { onChangeNode } from '../../../FlowProvider';
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';

View File

@@ -14,39 +14,40 @@ import { useCallback } from 'react';
type ToolHandleProps = BoxProps & {
moduleId: string;
};
export const ToolTargetHandle = ({ moduleId, ...props }: ToolHandleProps) => {
export const ToolTargetHandle = ({ moduleId }: ToolHandleProps) => {
const { t } = useTranslation();
const valueTypeMap = FlowValueTypeMap[ModuleIOValueTypeEnum.tools];
return (
<Box position={'absolute'} left={'50%'} transform={'translate(-17px,-10px)'} {...props}>
<MyTooltip
label={t('app.module.type', {
type: t(valueTypeMap?.label),
description: valueTypeMap?.description
})}
<MyTooltip
label={t('app.module.type', {
type: t(valueTypeMap?.label),
description: valueTypeMap?.description
})}
shouldWrapChildren={false}
>
<Handle
style={{
borderRadius: '0',
backgroundColor: 'transparent'
}}
type="target"
id={ModuleOutputKeyEnum.selectedTools}
position={Position.Top}
>
<Handle
style={{
width: '14px',
height: '14px',
border: '4px solid #5E8FFF',
borderRadius: '0',
backgroundColor: 'transparent',
transformOrigin: 'center',
transform: 'rotate(45deg)'
}}
type="target"
id={ModuleOutputKeyEnum.selectedTools}
position={Position.Top}
<Box
w={'14px'}
h={'14px'}
border={'4px solid #5E8FFF'}
transform={'translate(-40%,-30%) rotate(45deg)'}
/>
</MyTooltip>
</Box>
</Handle>
</MyTooltip>
);
};
export const ToolSourceHandle = ({ moduleId, ...props }: ToolHandleProps) => {
export const ToolSourceHandle = ({ moduleId }: ToolHandleProps) => {
const { t } = useTranslation();
const { setEdges, nodes } = useFlowProviderStore();
@@ -75,29 +76,30 @@ export const ToolSourceHandle = ({ moduleId, ...props }: ToolHandleProps) => {
);
return (
<Box position={'absolute'} left={'50%'} transform={'translate(-16px,-14px)'} {...props}>
<MyTooltip
label={t('app.module.type', {
type: t(valueTypeMap?.label),
description: valueTypeMap?.description
})}
<MyTooltip
label={t('app.module.type', {
type: t(valueTypeMap?.label),
description: valueTypeMap?.description
})}
shouldWrapChildren={false}
>
<Handle
style={{
borderRadius: '0',
backgroundColor: 'transparent'
}}
type="source"
id={ModuleOutputKeyEnum.selectedTools}
position={Position.Bottom}
onConnect={onConnect}
>
<Handle
style={{
width: '14px',
height: '14px',
border: '4px solid #5E8FFF',
borderRadius: '0',
backgroundColor: 'transparent',
transformOrigin: 'center',
transform: 'rotate(45deg)'
}}
type="source"
id={ModuleOutputKeyEnum.selectedTools}
position={Position.Bottom}
onConnect={onConnect}
<Box
w={'14px'}
h={'14px'}
border={'4px solid #5E8FFF'}
transform={'translate(-40%,-30%) rotate(45deg)'}
/>
</MyTooltip>
</Box>
</Handle>
</MyTooltip>
);
};

View File

@@ -18,7 +18,8 @@ import {
MenuList,
MenuItem,
MenuButton,
Menu
Menu,
IconButton
} from '@chakra-ui/react';
import {
getOpenApiKeys,
@@ -37,10 +38,11 @@ import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useForm } from 'react-hook-form';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import MyTooltip from '@/components/MyTooltip';
import { getDocPath } from '@/web/common/system/doc';
import MyMenu from '@/components/MyMenu';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
type EditProps = EditApiKeyProps & { _id?: string };
const defaultEditData: EditProps = {
@@ -59,6 +61,10 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
const [baseUrl, setBaseUrl] = useState('https://fastgpt.in/api');
const [editData, setEditData] = useState<EditProps>();
const [apiKey, setApiKey] = useState('');
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: '确认删除该API密钥删除后该密钥立即失效对应的对话日志不会删除请确认'
});
const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({
mutationFn: async (id: string) => delOpenApiById(id),
@@ -181,13 +187,12 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
<MyMenu
offset={[-50, 5]}
Button={
<MyIcon
<IconButton
icon={<MyIcon name={'more'} w={'14px'} />}
name={'more'}
w={'14px'}
p={2}
_hover={{ bg: 'myWhite.600 ' }}
cursor={'pointer'}
borderRadius={'md'}
variant={'whitePrimary'}
size={'sm'}
aria-label={''}
/>
}
menuList={[
@@ -205,7 +210,8 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
{
label: t('common.Delete'),
icon: 'delete',
onClick: () => onclickRemove(_id)
type: 'danger',
onClick: openConfirm(() => onclickRemove(_id))
}
]}
/>
@@ -216,6 +222,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
</Table>
<Loading loading={isGetting || isDeleting} fixed={false} />
</TableContainer>
{!!editData && (
<EditKeyModal
defaultData={editData}
@@ -231,6 +238,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
}}
/>
)}
<ConfirmModal />
<MyModal
isOpen={!!apiKey}
w={['400px', '600px']}

View File

@@ -0,0 +1,63 @@
import { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
import React from 'react';
import { Box, Flex } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { CloseIcon } from '@chakra-ui/icons';
import { readInform } from '@/web/support/user/inform/api';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
const ImportantInform = ({
informs,
refetch
}: {
informs: UserInformSchema[];
refetch: () => void;
}) => {
const { mutate: onClickClose } = useRequest({
mutationFn: async (id: string) => {
await readInform(id);
},
onSuccess: () => {
refetch();
},
errorToast: 'Failed to read the inform'
});
return (
<Box position={'fixed'} top={'3%'} left={'50%'} transform={'translateX(-50%)'} zIndex={99999}>
{informs.map((inform) => (
<Flex
key={inform._id}
bg={'primary.015'}
py={3}
px={5}
fontSize={'md'}
borderRadius={'lg'}
boxShadow={'4'}
borderWidth={'1px'}
borderColor={'borderColor.base'}
minW={['200px', '400px']}
alignItems={'flex-start'}
mb={3}
backdropFilter={'blur(30px)'}
>
<MyIcon name={'support/user/informLight'} w={'16px'} mr={2} />
<Box flex={'1 0 0'}>
<Box fontWeight={'bold'}>{inform.title}</Box>
<Box fontSize={'sm'}>{inform.content}</Box>
</Box>
<CloseIcon
cursor={'pointer'}
_hover={{
color: 'primary.700'
}}
w={'12px'}
onClick={() => onClickClose(inform._id)}
/>
</Flex>
))}
</Box>
);
};
export default ImportantInform;

View File

@@ -0,0 +1,43 @@
import React, { useCallback } from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useQuery } from '@tanstack/react-query';
import { Button, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { getSystemMsgModalData } from '@/web/support/user/inform/api';
import Markdown from '@/components/Markdown';
const SystemMsgModal = ({}: {}) => {
const { t } = useTranslation();
const { systemMsgReadId, setSysMsgReadId } = useUserStore();
const { isOpen, onOpen, onClose } = useDisclosure();
const { data } = useQuery(['initSystemMsgModal', systemMsgReadId], getSystemMsgModalData, {
onSuccess(res) {
if (res?.content && (!systemMsgReadId || res.id !== systemMsgReadId)) {
onOpen();
}
}
});
const onclickRead = useCallback(() => {
if (!data) return;
setSysMsgReadId(data.id);
onClose();
}, [data, onClose, setSysMsgReadId]);
return (
<MyModal isOpen={isOpen} iconSrc={LOGO_ICON} title={t('support.user.inform.System message')}>
<ModalBody overflow={'auto'}>
<Markdown source={data?.content} />
</ModalBody>
<ModalFooter>
<Button onClick={onclickRead}>{t('support.inform.Read')}</Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(SystemMsgModal);

View File

@@ -5,7 +5,7 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyTooltip from '@/components/MyTooltip';

View File

@@ -4,9 +4,9 @@ import { useTranslation } from 'next-i18next';
import { ModalCloseButton, ModalBody, Box, ModalFooter, Button } from '@chakra-ui/react';
import TagTextarea from '@/components/common/Textarea/TagTextarea';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { postInviteTeamMember } from '@/web/support/user/team/api';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { InviteMemberResponse } from '@fastgpt/global/support/user/team/controller.d';
import MySelect from '@fastgpt/web/components/common/MySelect';

View File

@@ -36,12 +36,12 @@ import {
TeamMemberStatusMap
} from '@fastgpt/global/support/user/team/constant';
import dynamic from 'next/dynamic';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { setToken } from '@/web/support/user/auth';
import { useLoading } from '@fastgpt/web/hooks/useLoading';
import { FormDataType, defaultForm } from './EditModal';
import MyMenu from '@/components/MyMenu';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@@ -15,7 +15,7 @@ import { putUpdateTeam } from '@/web/support/user/team/api';
import { useFieldArray, useForm } from 'react-hook-form';
import { useTranslation } from 'next-i18next';
import type { TeamTagItemType } from '@fastgpt/global/support/user/team/type';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { RepeatIcon } from '@chakra-ui/icons';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData';

View File

@@ -14,9 +14,9 @@ import { useQuery } from '@tanstack/react-query';
import { getTeamList, updateInviteResult } from '@/web/support/user/team/api';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
import Avatar from '@/components/Avatar';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useSystemStore } from '@/web/common/system/useSystemStore';
const UpdateInviteModal = () => {

View File

@@ -29,7 +29,11 @@ function Error() {
{`出现未捕获的异常。
1. 私有部署用户90%由于配置文件不正确导致。
2. 部分系统不兼容相关API。大部分是苹果的safari 浏览器导致,可以尝试更换 chrome。
3. 请关闭浏览器翻译功能,部分翻译导致页面崩溃。`}
3. 请关闭浏览器翻译功能,部分翻译导致页面崩溃。
排除3后打开控制台的 console 查看具体报错信息。
如果提示 xxx undefined 的话,就是配置文件有错误。
`}
</Box>
);
}

View File

@@ -27,7 +27,7 @@ import {
} from '@fastgpt/global/support/wallet/bill/constants';
// import { usePagination } from '@/web/common/hooks/usePagination';
import MyBox from '@/components/common/MyBox';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal';

View File

@@ -403,7 +403,7 @@ const PlanUsage = () => {
</Flex>
{isFreeTeam && (
<Box mt="2" color={'#485264'} fontSize="sm">
15使
30使
</Box>
)}
</Box>
@@ -504,9 +504,9 @@ const PlanUsage = () => {
const Other = () => {
const theme = useTheme();
const { toast } = useToast();
const { feConfigs, systemVersion } = useSystemStore();
const { feConfigs } = useSystemStore();
const { t } = useTranslation();
const { userInfo, updateUserInfo, initUserInfo, teamPlanStatus } = useUserStore();
const { userInfo, updateUserInfo } = useUserStore();
const { reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType
});
@@ -552,10 +552,6 @@ const Other = () => {
<Box ml={2} flex={1}>
{t('system.Help Document')}
</Box>
<Box w={'8px'} h={'8px'} borderRadius={'50%'} bg={'#67c13b'} />
<Box fontSize={'md'} ml={2}>
V{systemVersion}
</Box>
</Link>
)}
<Link

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Box, Flex, useTheme } from '@chakra-ui/react';
import { Box, Button, Flex, useTheme } from '@chakra-ui/react';
import { getInforms, readInform } from '@/web/support/user/inform/api';
import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
import { formatTimeToChatTime } from '@/utils/tools';
@@ -7,8 +7,10 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { useLoading } from '@fastgpt/web/hooks/useLoading';
import { useTranslation } from 'next-i18next';
const InformTable = () => {
const { t } = useTranslation();
const theme = useTheme();
const { Loading } = useLoading();
const { isPc } = useSystemStore();
@@ -22,7 +24,7 @@ const InformTable = () => {
pageNum
} = usePagination<UserInformSchema>({
api: getInforms,
pageSize: isPc ? 20 : 10
pageSize: 20
});
return (
@@ -35,35 +37,44 @@ const InformTable = () => {
py={2}
px={4}
borderRadius={'md'}
cursor={item.read ? 'default' : 'pointer'}
position={'relative'}
_notLast={{ mb: 3 }}
onClick={async () => {
if (!item.read) {
await readInform(item._id);
getData(pageNum);
}
}}
>
<Flex alignItems={'center'} justifyContent={'space-between'}>
<Flex alignItems={'center'}>
<Box fontWeight={'bold'}>{item.title}</Box>
<Box ml={2} color={'myGray.500'}>
{formatTimeToChatTime(item.time)}
<Box ml={2} color={'myGray.500'} flex={'1 0 0'}>
({formatTimeToChatTime(item.time)})
</Box>
{!item.read && (
<Button
variant={'whitePrimary'}
size={'xs'}
onClick={async () => {
if (!item.read) {
await readInform(item._id);
getData(pageNum);
}
}}
>
{t('support.inform.Read')}
</Button>
)}
</Flex>
<Box fontSize={'sm'} color={'myGray.600'} whiteSpace={'pre-wrap'}>
<Box mt={2} fontSize={'sm'} color={'myGray.600'} whiteSpace={'pre-wrap'}>
{item.content}
</Box>
{!item.read && (
<Box
w={'5px'}
h={'5px'}
borderRadius={'10px'}
bg={'red.600'}
position={'absolute'}
bottom={'8px'}
right={'8px'}
></Box>
<>
<Box
w={'5px'}
h={'5px'}
borderRadius={'10px'}
bg={'red.600'}
position={'absolute'}
top={'8px'}
left={'8px'}
/>
</>
)}
</Box>
))}

View File

@@ -3,7 +3,7 @@ import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/rea
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import type { UserType } from '@fastgpt/global/support/user/type.d';
const OpenAIAccountModal = ({

View File

@@ -3,7 +3,7 @@ import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/rea
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { updatePasswordByOld } from '@/web/support/user/api';
type FormType = {

View File

@@ -4,7 +4,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRouter } from 'next/router';
import dynamic from 'next/dynamic';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import PageContainer from '@/components/PageContainer';
import SideTabs from '@/components/SideTabs';
import Tabs from '@/components/Tabs';
@@ -34,7 +34,7 @@ enum TabEnum {
const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const { t } = useTranslation();
const { userInfo, setUserInfo } = useUserStore();
const { feConfigs, isPc } = useSystemStore();
const { feConfigs, isPc, systemVersion } = useSystemStore();
const tabList = [
{
@@ -148,6 +148,12 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
activeId={currentTab}
onChange={setCurrentTab}
/>
<Flex alignItems={'center'}>
<Box w={'8px'} h={'8px'} borderRadius={'50%'} bg={'#67c13b'} />
<Box fontSize={'md'} ml={2}>
V{systemVersion}
</Box>
</Flex>
</Flex>
) : (
<Box mb={3}>

View File

@@ -2,9 +2,9 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authFileToken } from '@fastgpt/service/support/permission/controller';
import { detect } from 'jschardet';
import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -37,7 +37,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
}
const encoding = detect(buffers)?.encoding || 'utf-8';
const encoding = detectFileEncoding(buffers);
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
res.setHeader('Cache-Control', 'public, max-age=3600');

View File

@@ -25,8 +25,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
reRankModels:
global.reRankModels?.map((item) => ({
...item,
requestUrl: undefined,
requestAuth: undefined
requestUrl: '',
requestAuth: ''
})) || [],
whisperModel: global.whisperModel,
audioSpeechModels: global.audioSpeechModels,
@@ -42,7 +42,7 @@ const defaultFeConfigs: FastGPTFeConfigsType = {
openAPIDocUrl: 'https://doc.fastgpt.in/docs/development/openapi',
systemTitle: 'FastGPT',
concatMd:
'* 项目开源地址: [FastGPT GitHub](https://github.com/labring/FastGPT)\n* 交流群: ![](https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg)',
'项目开源地址: [FastGPT GitHub](https://github.com/labring/FastGPT)\n交流群: ![](https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg)',
limit: {
exportDatasetLimitMinutes: 0,
websiteSyncLimitMinuted: 0
@@ -150,7 +150,7 @@ function getSystemPlugin() {
const filterFiles = files.filter((item) => item.endsWith('.json'));
// read json file
const fileTemplates: PluginTemplateType[] = filterFiles.map((filename) => {
const fileTemplates: (PluginTemplateType & { weight: number })[] = filterFiles.map((filename) => {
const content = readFileSync(`${basePath}/${filename}`, 'utf-8');
return {
...JSON.parse(content),
@@ -159,5 +159,7 @@ function getSystemPlugin() {
};
});
fileTemplates.sort((a, b) => b.weight - a.weight);
global.communityPlugins = fileTemplates;
}

View File

@@ -72,7 +72,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
userChatInput: text
},
stream: true,
detail: true
detail: true,
maxRunTimes: 200
});
responseWrite({

View File

@@ -1,12 +1,12 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import * as SwaggerParser from '@apidevtools/swagger-parser';
import SwaggerParser from '@apidevtools/swagger-parser';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const apiURL = req.body.url as string;
const api = await (SwaggerParser as any).validate(apiURL);
const api = await SwaggerParser.validate(apiURL);
return jsonRes(res, {
data: api

View File

@@ -17,6 +17,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { teamId, tmbId } = await authPluginCrud({ req, authToken: true, id, per: 'owner' });
const updateData = {
name: props.name,
intro: props.intro,
avatar: props.avatar,
parentId: props.parentId,
...(props.modules &&
props.modules.length > 0 && {
modules: props.modules
}),
metadata: props.metadata
};
if (props.metadata?.apiSchemaStr) {
await mongoSessionRun(async (session) => {
// update children
@@ -26,13 +38,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
parent: body,
session
});
await MongoPlugin.findByIdAndUpdate(id, props, { session });
await MongoPlugin.findByIdAndUpdate(id, updateData, { session });
});
jsonRes(res, {});
} else {
jsonRes(res, {
data: await MongoPlugin.findByIdAndUpdate(id, props)
data: await MongoPlugin.findByIdAndUpdate(id, updateData)
});
}
} catch (err) {

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { readMongoImg } from '@fastgpt/service/common/file/image/controller';
import { guessImageTypeFromBase64 } from '@fastgpt/service/common/file/utils';
import { guessBase64ImageType } from '@fastgpt/service/common/file/utils';
// get the models available to the system
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -11,8 +11,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { id } = req.query as { id: string };
const binary = await readMongoImg({ id });
const imageType = guessImageTypeFromBase64(binary.toString('base64'));
res.setHeader('Content-Type', imageType);
res.setHeader('Content-Type', guessBase64ImageType(binary.toString('base64')));
res.send(binary);
} catch (error) {
jsonRes(res, {

View File

@@ -184,7 +184,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
userChatInput: text
},
stream,
detail
detail,
maxRunTimes: 200
});
// save chat

View File

@@ -9,17 +9,19 @@ import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
import { getVectorModel } from '@fastgpt/service/core/ai/model';
import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
import { EmbeddingTypeEnm } from '@fastgpt/global/core/ai/constants';
type Props = {
input: string | string[];
model: string;
dimensions?: number;
billId?: string;
type: `${EmbeddingTypeEnm}`;
};
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
let { input, model, billId } = req.body as Props;
let { input, model, billId, type } = req.body as Props;
await connectToDatabase();
if (!Array.isArray(input) && typeof input !== 'string') {
@@ -38,7 +40,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
const { tokens, vectors } = await getVectorsByText({
input: query,
model: getVectorModel(model)
model: getVectorModel(model),
type
});
res.json({

View File

@@ -2,7 +2,7 @@ import React, { useCallback, useRef, useState } from 'react';
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
import { SmallCloseIcon } from '@chakra-ui/icons';
import { ModuleItemType } from '@fastgpt/global/core/module/type';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { AppSchema } from '@fastgpt/global/core/app/type.d';
import { useTranslation } from 'next-i18next';
import { useCopyData } from '@/web/common/hooks/useCopyData';
@@ -16,7 +16,7 @@ import { useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider
import { flowNode2Modules, filterExportModules } from '@/components/core/module/utils';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { getErrText } from '@fastgpt/global/common/error/utils';
const ImportSettings = dynamic(() => import('@/components/core/module/Flow/ImportSettings'));

View File

@@ -16,7 +16,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useAppStore } from '@/web/core/app/store/useAppStore';

View File

@@ -102,7 +102,10 @@ const SelectUsingWayModal = ({ share, onClose }: { share: OutLinkSchema; onClose
data-open-icon="${getValues('scriptOpenIcon')}"
data-close-icon="${getValues('scriptCloseIcon')}"
defer
/>`
/>
<script>
console.log("Chat box loaded")
</script>`
}
};

View File

@@ -14,7 +14,8 @@ import {
ModalBody,
Input,
Switch,
Link
Link,
IconButton
} from '@chakra-ui/react';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -31,7 +32,7 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useForm } from 'react-hook-form';
import { defaultOutLinkForm } from '@/constants/app';
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant';
import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast';
@@ -42,6 +43,7 @@ import dayjs from 'dayjs';
import { getDocPath } from '@/web/common/system/doc';
import dynamic from 'next/dynamic';
import MyMenu from '@/components/MyMenu';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
const SelectUsingWayModal = dynamic(() => import('./SelectUsingWayModal'));
@@ -53,6 +55,10 @@ const Share = ({ appId }: { appId: string }) => {
const [editLinkData, setEditLinkData] = useState<OutLinkEditType>();
const [selectedLinkData, setSelectedLinkData] = useState<OutLinkSchema>();
const { toast } = useToast();
const { ConfirmModal, openConfirm } = useConfirm({
content: t('support.outlink.Delete link tip'),
type: 'delete'
});
const {
isFetching,
@@ -130,25 +136,25 @@ const Share = ({ appId }: { appId: string }) => {
)}
<Td>{item.lastTime ? formatTimeToChatTime(item.lastTime) : t('common.Un used')}</Td>
<Td display={'flex'} alignItems={'center'}>
<Button
onClick={() => setSelectedLinkData(item)}
size={'sm'}
mr={3}
variant={'whitePrimary'}
>
{t('core.app.outLink.Select Mode')}
</Button>
<MyMenu
Button={
<MyIcon
<IconButton
icon={<MyIcon name={'more'} w={'14px'} />}
name={'more'}
_hover={{ bg: 'myGray.100 ' }}
cursor={'pointer'}
borderRadius={'md'}
w={'14px'}
p={2}
variant={'whiteBase'}
size={'sm'}
aria-label={''}
/>
}
menuList={[
{
label: t('core.app.outLink.Select Mode'),
icon: 'copy',
onClick: () => {
setSelectedLinkData(item);
}
},
{
label: t('common.Edit'),
icon: 'edit',
@@ -163,7 +169,8 @@ const Share = ({ appId }: { appId: string }) => {
{
label: t('common.Delete'),
icon: 'delete',
onClick: async () => {
type: 'danger',
onClick: openConfirm(async () => {
setIsLoading(true);
try {
await delShareChatById(item._id);
@@ -172,7 +179,7 @@ const Share = ({ appId }: { appId: string }) => {
console.log(error);
}
setIsLoading(false);
}
})
}
]}
/>
@@ -219,6 +226,7 @@ const Share = ({ appId }: { appId: string }) => {
onClose={() => setSelectedLinkData(undefined)}
/>
)}
<ConfirmModal />
<Loading loading={isFetching} fixed={false} />
</Box>
);

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { Box, Flex, Button, IconButton } from '@chakra-ui/react';
import { DragHandleIcon } from '@chakra-ui/icons';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRouter } from 'next/router';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { AppSchema } from '@fastgpt/global/core/app/type.d';

View File

@@ -7,8 +7,8 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { appModules2Form, getDefaultAppForm } from '@fastgpt/global/core/app/utils';
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
import { chatNodeSystemPromptTip, welcomeTextTip } from '@fastgpt/global/core/module/template/tip';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';

View File

@@ -20,7 +20,7 @@ import {
} from '@chakra-ui/react';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { getTeamsTags } from '@/web/support/user/team/api';
import { useQuery } from '@tanstack/react-query';

View File

@@ -13,7 +13,7 @@ import {
} from '@chakra-ui/react';
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
import { useWorkflowStore } from '@/web/core/workflow/store/workflow';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useQuery } from '@tanstack/react-query';
import EmptyTip from '@/components/EmptyTip';
import { FlowNodeTemplateType } from '@fastgpt/global/core/module/type';

View File

@@ -23,7 +23,7 @@ import { postCreateApp } from '@/web/core/app/api';
import { useRouter } from 'next/router';
import { appTemplates } from '@/web/core/app/templates';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';

View File

@@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query';
import { AddIcon } from '@chakra-ui/icons';
import { delModelById } from '@/web/core/app/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';

View File

@@ -17,7 +17,7 @@ import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import Tabs from '@/components/Tabs';
import { useUserStore } from '@/web/support/user/useUserStore';
import { AppListItemType } from '@fastgpt/global/core/app/type';

View File

@@ -2,7 +2,7 @@ import React, { useMemo, useRef, useState } from 'react';
import { ModalFooter, ModalBody, Input, Button } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
const EditFolderModal = ({
onClose,

View File

@@ -26,12 +26,12 @@ import {
} from '@/web/core/dataset/api';
import { useQuery } from '@tanstack/react-query';
import { debounce } from 'lodash';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyInput from '@/components/MyInput';
import dayjs from 'dayjs';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useLoading } from '@fastgpt/web/hooks/useLoading';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
@@ -707,6 +707,7 @@ const CollectionCard = () => {
<Box>{t('common.Delete')}</Box>
</Flex>
),
type: 'danger',
onClick: () =>
openDeleteConfirm(
() => {

View File

@@ -25,7 +25,7 @@ import { useQuery } from '@tanstack/react-query';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { debounce } from 'lodash';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -302,7 +302,10 @@ const DataCard = () => {
>
<Flex zIndex={1} alignItems={'center'} justifyContent={'space-between'}>
<Box
border={theme.borders.base}
borderWidth={'1px'}
borderColor={'primary.200'}
bg={'primary.50'}
color={'primary.600'}
px={2}
fontSize={'sm'}
mr={1}
@@ -310,7 +313,7 @@ const DataCard = () => {
>
# {item.chunkIndex ?? '-'}
</Box>
<Box className={'textEllipsis'} color={'myGray.500'} fontSize={'xs'}>
<Box className={'textEllipsis'} fontSize={'xs'}>
ID:{item._id}
</Box>
</Flex>

View File

@@ -5,7 +5,7 @@ import { Box, Button, Input, Link, ModalBody, ModalFooter } from '@chakra-ui/rea
import { strIsLink } from '@fastgpt/global/common/string/tools';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { getDocPath } from '@/web/common/system/doc';
import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@@ -15,7 +15,7 @@ const PreviewData = ({
const { sources, setSources } = useImportStore();
return (
<Flex flexDirection={'column'} h={'100%'} maxW={'1080px'}>
<Flex flexDirection={'column'} h={'100%'}>
<Box flex={'1 0 0 '}>
<Preview showPreviewChunks={showPreviewChunks} sources={sources} />
</Box>

View File

@@ -15,7 +15,7 @@ import {
import { useImportStore, type FormType } from '../Provider';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { postCreateTrainingUsage } from '@/web/support/wallet/usage/api';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { chunksUpload, fileCollectionCreate } from '@/web/core/dataset/utils';

View File

@@ -8,7 +8,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { formatFileSize } from '@fastgpt/global/common/file/tools';
import { useTranslation } from 'next-i18next';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { readFileRawContent } from '@fastgpt/web/common/file/read';
import { getUploadBase64ImgController } from '@/web/common/file/controller';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';

View File

@@ -8,7 +8,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { formatFileSize } from '@fastgpt/global/common/file/tools';
import { useTranslation } from 'next-i18next';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import MyTooltip from '@/components/MyTooltip';
import { useImportStore } from '../Provider';
import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@@ -5,7 +5,7 @@ import { DeleteIcon } from '@chakra-ui/icons';
import { delDatasetById } from '@/web/core/dataset/api';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useForm } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
@@ -14,7 +14,7 @@ import MyTooltip from '@/components/MyTooltip';
import { useTranslation } from 'next-i18next';
import PermissionRadio from '@/components/support/permission/Radio';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
import MySelect from '@fastgpt/web/components/common/MySelect';
import AIModelSelector from '@/components/Select/AIModelSelector';

View File

@@ -1,5 +1,5 @@
import React, { useMemo, useState } from 'react';
import { Box, Flex, Button, Textarea, useTheme, Grid } from '@chakra-ui/react';
import { Box, Flex, Button, Textarea, useTheme, Grid, HStack } from '@chakra-ui/react';
import { UseFormRegister, useFieldArray, useForm } from 'react-hook-form';
import {
postInsertData2Dataset,
@@ -12,12 +12,11 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyModal from '@fastgpt/web/components/common/MyModal';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'next-i18next';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils';
import { DatasetDataIndexItemType } from '@fastgpt/global/core/dataset/type';
import SideTabs from '@/components/SideTabs';
@@ -27,8 +26,8 @@ import { getDocPath } from '@/web/common/system/doc';
import RawSourceBox from '@/components/core/dataset/RawSourceBox';
import MyBox from '@/components/common/MyBox';
import { getErrText } from '@fastgpt/global/common/error/utils';
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
export type InputDataType = {
q: string;
@@ -232,9 +231,9 @@ const InputDataModal = ({
return (
<MyModal isOpen={true} isCentered w={'90vw'} maxW={'1440px'} h={'90vh'}>
<MyBox isLoading={isLoading} display={'flex'} h={'100%'}>
<Box p={5} borderRight={theme.borders.base}>
<Box p={5} bg={'myGray.50'} borderLeftRadius={'md'} borderRight={theme.borders.base}>
<RawSourceBox
w={'200px'}
w={'210px'}
className="textEllipsis3"
whiteSpace={'pre-wrap'}
sourceName={collection.sourceName}
@@ -256,32 +255,40 @@ const InputDataModal = ({
}}
/>
</Box>
<Flex flexDirection={'column'} py={3} flex={1} h={'100%'}>
<Box fontSize={'lg'} px={5} fontWeight={'bold'} mb={4}>
<Flex flexDirection={'column'} pb={8} flex={1} h={'100%'}>
<Box fontSize={'lg'} px={5} py={3} fontWeight={'medium'}>
{currentTab === TabEnum.content && (
<>{dataId ? t('dataset.data.Update Data') : t('dataset.data.Input Data')}</>
)}
{currentTab === TabEnum.index && <> {t('dataset.data.Index Edit')}</>}
</Box>
<Box flex={1} px={5} overflow={'auto'}>
<Box flex={1} px={9} overflow={'auto'}>
{currentTab === TabEnum.content && <InputTab maxToken={maxToken} register={register} />}
{currentTab === TabEnum.index && (
<Grid gridTemplateColumns={['1fr', '1fr 1fr']} gridGap={4}>
<Grid mt={3} gridTemplateColumns={['1fr', '1fr 1fr']} gridGap={4}>
{indexes?.map((index, i) => (
<Box
key={index.dataId || i}
p={3}
p={4}
borderRadius={'md'}
border={theme.borders.base}
bg={i % 2 !== 0 ? 'myWhite.400' : ''}
border={
index.defaultIndex
? '1.5px solid var(--light-fastgpt-primary-opacity-01, rgba(51, 112, 255, 0.10))'
: '1.5px solid var(--Gray-Modern-200, #E8EBF0)'
}
bg={index.defaultIndex ? 'primary.50' : 'myGray.25'}
_hover={{
'& .delete': {
display: index.defaultIndex ? 'none' : 'block'
}
}}
>
<Flex mb={1}>
<Box flex={1}>
<Flex mb={2}>
<Box
flex={1}
fontWeight={'medium'}
color={index.defaultIndex ? 'primary.700' : 'myGray.900'}
>
{index.defaultIndex
? t('dataset.data.Default Index')
: t('dataset.data.Custom Index Number', { number: i })}
@@ -296,16 +303,23 @@ const InputDataModal = ({
/>
</Flex>
{index.defaultIndex ? (
<Box>{t('core.dataset.data.Default Index Tip')}</Box>
<Box fontSize={'sm'} fontWeight={'medium'} color={'myGray.600'}>
{t('core.dataset.data.Default Index Tip')}
</Box>
) : (
<Textarea
maxLength={maxToken}
fontSize={'sm'}
rows={10}
borderColor={'transparent'}
px={0}
pt={0}
_focus={{
borderColor: 'primary.400',
px: 3
px: 3,
py: 2,
borderColor: 'primary.500',
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)',
bg: 'white'
}}
placeholder={t('dataset.data.Index Placeholder')}
{...register(`indexes.${i}.text`, {
@@ -316,14 +330,19 @@ const InputDataModal = ({
</Box>
))}
<Flex
flexDirection={'column'}
alignItems={'center'}
justifyContent={'center'}
borderRadius={'md'}
border={theme.borders.base}
color={'myGray.600'}
fontWeight={'medium'}
border={'1.5px solid var(--Gray-Modern-200, #E8EBF0)'}
bg={'myGray.25'}
cursor={'pointer'}
_hover={{
bg: 'primary.50'
bg: 'primary.50',
color: 'primary.600',
border:
'1.5px solid var(--light-fastgpt-primary-opacity-01, rgba(51, 112, 255, 0.10))'
}}
minH={'100px'}
onClick={() =>
@@ -334,14 +353,14 @@ const InputDataModal = ({
})
}
>
<MyIcon name={'common/addCircleLight'} w={'16px'} />
<MyIcon name={'common/addLight'} w={'18px'} mr={1.5} />
<Box>{t('dataset.data.Add Index')}</Box>
</Flex>
</Grid>
)}
</Box>
{/* footer */}
<Flex justifyContent={'flex-end'} px={5} mt={4}>
<Flex justifyContent={'flex-end'} px={9} mt={6}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
{t('common.Close')}
</Button>
@@ -364,10 +383,6 @@ const InputDataModal = ({
export default React.memo(InputDataModal);
enum InputTypeEnum {
q = 'q',
a = 'a'
}
const InputTab = ({
maxToken,
register
@@ -376,70 +391,46 @@ const InputTab = ({
register: UseFormRegister<InputDataType>;
}) => {
const { t } = useTranslation();
const { isPc } = useSystemStore();
const [inputType, setInputType] = useState(InputTypeEnum.q);
return (
<Flex flexDirection={'column'} h={'100%'}>
<Box>
<RowTabs
list={[
{
label: (
<Flex alignItems={'center'}>
<Box as="span" color={'red.600'}>
*
</Box>
{t('core.dataset.data.Main Content')}
<MyTooltip label={t('core.dataset.data.Data Content Tip')}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
),
value: InputTypeEnum.q
},
{
label: (
<Flex alignItems={'center'}>
{t('core.dataset.data.Auxiliary Data')}
<MyTooltip label={t('core.dataset.data.Auxiliary Data Tip')}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
),
value: InputTypeEnum.a
}
]}
value={inputType}
onChange={(e) => setInputType(e as InputTypeEnum)}
/>
</Box>
<Box mt={3} flex={'1 0 0'}>
{inputType === InputTypeEnum.q && (
<HStack h={'100%'} spacing={6}>
<Flex flexDirection={'column'} w={'50%'} h={'100%'}>
<Flex pt={3} pb={2} fontWeight={'medium'} fontSize={'md'} alignItems={'center'}>
<Box color={'red.600'}>*</Box>
<Box color={'myGray.900'}>{t('core.dataset.data.Main Content')}</Box>
<QuestionTip label={t('core.dataset.data.Data Content Tip')} ml={1} />
</Flex>
<Box flex={'1 0 0'}>
<Textarea
placeholder={t('core.dataset.data.Data Content Placeholder', { maxToken })}
maxLength={maxToken}
h={'100%'}
bg={'myWhite.400'}
tabIndex={1}
bg={'myGray.50'}
h={'full'}
{...register(`q`, {
required: true
})}
/>
)}
{inputType === InputTypeEnum.a && (
</Box>
</Flex>
<Flex flexDirection={'column'} w={'50%'} h={'100%'}>
<Flex pt={3} pb={2} fontWeight={'medium'} fontSize={'md'} alignItems={'center'}>
<Box color={'myGray.900'}>{t('core.dataset.data.Auxiliary Data')}</Box>
<QuestionTip label={t('core.dataset.data.Auxiliary Data Tip')} ml={1} />
</Flex>
<Box flex={'1 0 0'}>
<Textarea
placeholder={t('core.dataset.data.Auxiliary Data Placeholder', {
maxToken: maxToken * 1.5
})}
h={'100%'}
bg={'myWhite.400'}
rows={isPc ? 24 : 12}
tabIndex={1}
bg={'myGray.50'}
maxLength={maxToken * 1.5}
{...register('a')}
/>
)}
</Box>
</Flex>
</Box>
</Flex>
</HStack>
);
};

View File

@@ -18,7 +18,7 @@ import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useSearchTestStore, SearchTestStoreItemType } from '@/web/core/dataset/store/searchTest';
import { postSearchText } from '@/web/core/dataset/api';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { formatTimeToChatTime } from '@/utils/tools';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useToast } from '@fastgpt/web/hooks/useToast';

View File

@@ -15,7 +15,6 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
import { getTrainingQueueLen } from '@/web/core/dataset/api';
import MyTooltip from '@/components/MyTooltip';
import Script from 'next/script';
import CollectionCard from './components/CollectionCard';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useUserStore } from '@/web/support/user/useUserStore';
@@ -24,8 +23,8 @@ import {
DatasetTypeEnum,
DatasetTypeMap
} from '@fastgpt/global/core/dataset/constants';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag';
import Head from 'next/head';
import MyBox from '@/components/common/MyBox';

View File

@@ -7,7 +7,7 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';

View File

@@ -18,7 +18,7 @@ import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { useTranslation } from 'next-i18next';
import { useQuery } from '@tanstack/react-query';
import { getDatasets, putDatasetById, getDatasetPaths } from '@/web/core/dataset/api';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
const MoveModal = ({
onClose,

View File

@@ -1,19 +1,9 @@
import React, { useMemo, useRef } from 'react';
import {
Box,
Flex,
Grid,
useTheme,
useDisclosure,
Card,
MenuButton,
Image,
Button
} from '@chakra-ui/react';
import { Box, Flex, Grid, useDisclosure, Image, Button } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import PageContainer from '@/components/PageContainer';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { AddIcon } from '@chakra-ui/icons';
import { useQuery } from '@tanstack/react-query';
import {
@@ -31,7 +21,7 @@ import dynamic from 'next/dynamic';
import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
import { FolderImgUrl, FolderIcon } from '@fastgpt/global/common/file/image/constants';
import MyMenu from '@/components/MyMenu';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
import EditFolderModal, { useEditFolder } from '../component/EditFolderModal';
@@ -389,6 +379,7 @@ const Kb = () => {
{t('common.Delete')}
</Flex>
),
type: 'danger',
onClick: () => {
openConfirm(
() => onclickDelDataset(dataset._id),

View File

@@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import dynamic from 'next/dynamic';

View File

@@ -81,7 +81,7 @@ const Render = ({ pluginId }: Props) => {
export default function FlowEdit(props: any) {
return (
<FlowProvider mode={'plugin'} filterAppIds={[]}>
<FlowProvider mode={'plugin'}>
<Render {...props} />
</FlowProvider>
);

View File

@@ -7,13 +7,13 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { delOnePlugin, postCreatePlugin, putUpdatePlugin } from '@/web/core/plugin/api';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { customAlphabet } from 'nanoid';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';

View File

@@ -21,7 +21,7 @@ import { useForm } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import { useTranslation } from 'next-i18next';
@@ -36,7 +36,7 @@ import {
} from '@/web/core/plugin/api';
import { str2OpenApiSchema } from '@fastgpt/global/core/plugin/httpPlugin/utils';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { AddIcon } from '@chakra-ui/icons';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { EditFormType } from './type';
@@ -100,12 +100,6 @@ const HttpPluginEditModal = ({
}
}, [apiSchemaStr, t, toast]);
const {
isOpen: isOpenUrlImport,
onOpen: onOpenUrlImport,
onClose: onCloseUrlImport
} = useDisclosure();
const { mutate: onCreate, isLoading: isCreating } = useRequest({
mutationFn: async (data: CreateOnePluginParams) => {
return postCreatePlugin(data);
@@ -194,8 +188,6 @@ const HttpPluginEditModal = ({
const schema = await getApiSchemaByUrl(schemaUrl);
setValue('metadata.apiSchemaStr', JSON.stringify(schema, null, 2));
onCloseUrlImport();
},
errorToast: t('plugin.Invalid Schema')
});
@@ -251,7 +243,13 @@ const HttpPluginEditModal = ({
<Box color={'myGray.800'} fontWeight={'bold'} mt={3}>
{t('plugin.Intro')}
</Box>
<Textarea {...register('intro')} bg={'myWhite.600'} rows={3} mt={3} />
<Textarea
{...register('intro')}
bg={'myWhite.600'}
rows={3}
mt={3}
placeholder={t('core.plugin.Http plugin intro placeholder')}
/>
</>
<Box mt={4}>
<Box
@@ -263,32 +261,24 @@ const HttpPluginEditModal = ({
<Box my={'auto'}>{'OpenAPI Schema'}</Box>
<Box>
{isOpenUrlImport ? (
<Flex alignItems={'center'}>
<Input
mr={2}
placeholder={'https://...'}
h={'30px'}
onBlur={(e) => setSchemaUrl(e.target.value)}
/>
<Button size={'sm'} isLoading={isLoadingUrlApi} onClick={onClickUrlLoadApi}>
{t('common.Confirm')}
</Button>
<Button ml={2} variant={'whiteBase'} size={'sm'} onClick={onCloseUrlImport}>
{t('common.Cancel')}
</Button>
</Flex>
) : (
<Flex alignItems={'center'}>
<Input
mr={2}
placeholder={t('plugin.Import from URL')}
h={'30px'}
w={['150px', '250px']}
fontSize={'sm'}
onBlur={(e) => setSchemaUrl(e.target.value)}
/>
<Button
variant={'whiteBase'}
size={'sm'}
fontSize={'xs'}
leftIcon={<AddIcon fontSize={'xs'} />}
onClick={onOpenUrlImport}
variant={'whitePrimary'}
isLoading={isLoadingUrlApi}
onClick={onClickUrlLoadApi}
>
{t('plugin.Import from URL')}
{t('common.Import')}
</Button>
)}
</Flex>
</Box>
</Box>
<Textarea
@@ -466,34 +456,42 @@ const HttpPluginEditModal = ({
<Box color={'myGray.800'} fontWeight={'bold'} mt={3}>
{t('plugin.Plugin List')}
</Box>
<TableContainer maxH={400} overflowY={'auto'} mt={3}>
<Table border={'1px solid'} borderColor={'myGray.200'}>
<Thead>
<Th>{t('Name')}</Th>
<Th>{t('plugin.Description')}</Th>
<Th>{t('plugin.Method')}</Th>
<Th>{t('plugin.Path')}</Th>
</Thead>
<Tbody>
{apiData?.pathData?.map((item, index) => (
<Tr key={index}>
<Td>{item.name}</Td>
<Td
fontSize={'sm'}
textColor={'gray.600'}
w={'auto'}
maxW={80}
whiteSpace={'pre-wrap'}
>
{item.description}
</Td>
<Td>{item.method}</Td>
<Td>{item.path}</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<Box
mt={3}
borderRadius={'md'}
overflow={'hidden'}
borderWidth={'1px'}
borderBottom="none"
>
<TableContainer maxH={400} overflowY={'auto'}>
<Table bg={'white'}>
<Thead bg={'myGray.50'}>
<Th>{t('Name')}</Th>
<Th>{t('plugin.Description')}</Th>
<Th>{t('plugin.Method')}</Th>
<Th>{t('plugin.Path')}</Th>
</Thead>
<Tbody>
{apiData?.pathData?.map((item, index) => (
<Tr key={index}>
<Td>{item.name}</Td>
<Td
fontSize={'sm'}
textColor={'gray.600'}
w={'auto'}
maxW={80}
whiteSpace={'pre-wrap'}
>
{item.description}
</Td>
<Td>{item.method}</Td>
<Td>{item.path}</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Box>
</>
</ModalBody>

View File

@@ -35,7 +35,7 @@ const FAQ = () => {
},
{
title: '免费版数据会清除么?',
desc: '免费版用户(免费版且未购买额外套餐)15天无使用记录后,系统会自动清除账号下所有知识库内容。'
desc: '免费版用户(免费版且未购买额外套餐)30天无使用记录后,系统会自动清除账号下所有知识库内容。'
}
];

View File

@@ -7,7 +7,7 @@ import { postCheckStandardSub, postUpdateStandardSub } from '@/web/support/walle
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
import { StandardSubPlanParams } from '@fastgpt/global/support/wallet/sub/api';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { StandardSubPlanUpdateResponse } from '@fastgpt/global/support/wallet/sub/api.d';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';

View File

@@ -2,6 +2,7 @@ import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
import { sendOneInform } from '../support/user/inform/api';
import { lockTrainingDataByTeamId } from '@fastgpt/service/core/dataset/training/controller';
import { InformLevelEnum } from '@fastgpt/global/support/user/inform/constants';
export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) => {
try {
@@ -12,7 +13,7 @@ export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) =>
// send inform and lock data
try {
sendOneInform({
type: 'system',
level: InformLevelEnum.important,
title: '文本训练任务中止',
content:
'该团队账号AI积分不足文本训练任务中止重新充值后将会继续。暂停的任务将在 7 天后被删除。',

View File

@@ -23,7 +23,7 @@ export function connectToDatabase(): Promise<void> {
afterHook: async () => {
// init system config
getInitConfig();
// init vector database, init root user
//init vector database, init root user
await Promise.all([initVectorStore(), initRootUser()]);
startMongoWatch();
@@ -36,7 +36,7 @@ export function connectToDatabase(): Promise<void> {
});
}
async function initRootUser() {
async function initRootUser(retry = 3): Promise<any> {
try {
const rootUser = await MongoUser.findOne({
username: 'root'
@@ -48,12 +48,9 @@ async function initRootUser() {
await mongoSessionRun(async (session) => {
// init root user
if (rootUser) {
await MongoUser.findOneAndUpdate(
{ username: 'root' },
{
password: hashStr(psw)
}
);
await rootUser.updateOne({
password: hashStr(psw)
});
} else {
const [{ _id }] = await MongoUser.create(
[
@@ -75,7 +72,12 @@ async function initRootUser() {
password: psw
});
} catch (error) {
console.log('init root user error', error);
exit(1);
if (retry > 0) {
console.log('retry init root user');
return initRootUser(retry - 1);
} else {
console.error('init root user error', error);
exit(1);
}
}
}

View File

@@ -1,8 +1,8 @@
import { POST } from '@fastgpt/service/common/api/plusRequest';
import { SendInformProps } from '@fastgpt/global/support/user/inform/type';
import { SendInform2UserProps } from '@fastgpt/global/support/user/inform/type';
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
export function sendOneInform(data: SendInformProps) {
export function sendOneInform(data: SendInform2UserProps) {
if (!FastGPTProUrl) return;
return POST('/support/user/inform/create', data);
}

View File

@@ -181,12 +181,12 @@ export const pushQuestionGuideUsage = ({
createUsage({
teamId,
tmbId,
appName: 'core.app.Next Step Guide',
appName: 'core.app.Question Guide',
totalPoints,
source: UsageSourceEnum.fastgpt,
list: [
{
moduleName: 'core.app.Next Step Guide',
moduleName: 'core.app.Question Guide',
amount: totalPoints,
model: modelName,
tokens

View File

@@ -83,8 +83,7 @@ export const streamFetch = ({
}
if (responseQueue.length > 0) {
const fetchCount = Math.max(1, Math.round(responseQueue.length / 10));
const fetchCount = Math.max(1, Math.round(responseQueue.length / 30));
for (let i = 0; i < fetchCount; i++) {
const item = responseQueue[i];
onMessage(item);
@@ -167,7 +166,15 @@ export const streamFetch = ({
}
})();
// console.log(parseJson, event);
if (event === SseResponseEventEnum.answer || event === SseResponseEventEnum.fastAnswer) {
if (event === SseResponseEventEnum.answer) {
const text = parseJson.choices?.[0]?.delta?.content || '';
for (const item of text) {
responseQueue.push({
event,
text: item
});
}
} else if (event === SseResponseEventEnum.fastAnswer) {
const text = parseJson.choices?.[0]?.delta?.content || '';
responseQueue.push({
event,

View File

@@ -1,131 +0,0 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useDisclosure, Button, ModalBody, ModalFooter } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import MyModal from '@fastgpt/web/components/common/MyModal';
export const useConfirm = (props?: {
title?: string;
iconSrc?: string | '';
content?: string;
showCancel?: boolean;
type?: 'common' | 'delete';
hideFooter?: boolean;
}) => {
const { t } = useTranslation();
const map = useMemo(() => {
const map = {
common: {
title: t('common.confirm.Common Tip'),
bg: undefined,
iconSrc: 'common/confirm/commonTip'
},
delete: {
title: t('common.Delete Warning'),
bg: 'red.600',
iconSrc: 'common/confirm/deleteTip'
}
};
if (props?.type && map[props.type]) return map[props.type];
return map.common;
}, [props?.type, t]);
const {
title = map?.title || t('Warning'),
iconSrc = map?.iconSrc,
content,
showCancel = true,
hideFooter = false
} = props || {};
const [customContent, setCustomContent] = useState<string | React.ReactNode>(content);
const { isOpen, onOpen, onClose } = useDisclosure();
const confirmCb = useRef<any>();
const cancelCb = useRef<any>();
return {
openConfirm: useCallback(
(confirm?: any, cancel?: any, customContent?: string | React.ReactNode) => {
confirmCb.current = confirm;
cancelCb.current = cancel;
customContent && setCustomContent(customContent);
return onOpen;
},
[onOpen]
),
onClose,
ConfirmModal: useCallback(
({
closeText = t('common.Close'),
confirmText = t('common.Confirm'),
isLoading,
bg,
countDown = 0
}: {
closeText?: string;
confirmText?: string;
isLoading?: boolean;
bg?: string;
countDown?: number;
}) => {
const timer = useRef<any>();
const [countDownAmount, setCountDownAmount] = useState(countDown);
useEffect(() => {
timer.current = setInterval(() => {
setCountDownAmount((val) => {
if (val <= 0) {
clearInterval(timer.current);
}
return val - 1;
});
}, 1000);
}, []);
return (
<MyModal
isOpen={isOpen}
onClose={onClose}
iconSrc={iconSrc}
title={title}
maxW={['90vw', '500px']}
>
<ModalBody pt={5}>{customContent}</ModalBody>
{!hideFooter && (
<ModalFooter>
{showCancel && (
<Button
variant={'whiteBase'}
onClick={() => {
onClose();
typeof cancelCb.current === 'function' && cancelCb.current();
}}
>
{closeText}
</Button>
)}
<Button
bg={bg ? bg : map.bg}
isDisabled={countDownAmount > 0}
ml={4}
isLoading={isLoading}
onClick={() => {
onClose();
typeof confirmCb.current === 'function' && confirmCb.current();
}}
>
{countDownAmount > 0 ? `${countDownAmount}s` : confirmText}
</Button>
</ModalFooter>
)}
</MyModal>
);
},
[customContent, hideFooter, iconSrc, isOpen, map.bg, onClose, showCancel, t, title]
)
};
};

View File

@@ -8,7 +8,6 @@ import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
export const useSpeech = (props?: OutLinkChatAuthProps) => {
const { t } = useTranslation();
const mediaRecorder = useRef<MediaRecorder>();
// const mediaStream = useRef<MediaStream>();
const [mediaStream, setMediaStream] = useState<MediaStream>();
const { toast } = useToast();
const [isSpeaking, setIsSpeaking] = useState(false);
@@ -54,6 +53,7 @@ export const useSpeech = (props?: OutLinkChatAuthProps) => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
setMediaStream(stream);
mediaRecorder.current = new MediaRecorder(stream);
const chunks: Blob[] = [];
setIsSpeaking(true);
@@ -74,11 +74,18 @@ export const useSpeech = (props?: OutLinkChatAuthProps) => {
mediaRecorder.current.onstop = async () => {
const formData = new FormData();
const blob = new Blob(chunks, { type: 'audio/webm' });
let options = {};
if (MediaRecorder.isTypeSupported('audio/webm')) {
options = { type: 'audio/webm' };
} else if (MediaRecorder.isTypeSupported('video/mp4')) {
options = { type: 'video/mp4' };
} else {
console.error('no suitable mimetype found for this device');
}
const blob = new Blob(chunks, options);
const duration = Math.round((Date.now() - startTimestamp.current) / 1000);
formData.append('file', blob, 'recording.webm');
formData.append('file', blob, 'recording.mp4');
formData.append(
'data',
JSON.stringify({
@@ -112,7 +119,13 @@ export const useSpeech = (props?: OutLinkChatAuthProps) => {
};
mediaRecorder.current.start();
} catch (error) {}
} catch (error) {
toast({
status: 'warning',
title: getErrText(error, 'Whisper error')
});
console.log(error);
}
};
const stopSpeak = () => {

View File

@@ -1,5 +1,6 @@
import { AppItemType } from '@/types/app';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { Dataset_SEARCH_DESC } from '@fastgpt/global/core/module/template/system/datasetSearch';
// template
export const appTemplates: (AppItemType & {
@@ -876,7 +877,7 @@ export const appTemplates: (AppItemType & {
{
moduleId: '0voh5n',
name: '知识库搜索',
intro: '调用知识库搜索能力,查找“有可能”与问题相关的内容',
intro: Dataset_SEARCH_DESC,
avatar: '/imgs/module/db.png',
flowType: 'datasetSearchNode',
showStatus: true,

View File

@@ -4,7 +4,10 @@ import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/mo
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { UserInputModule } from '@fastgpt/global/core/module/template/system/userInput';
import { ToolModule } from '@fastgpt/global/core/module/template/system/tools';
import { DatasetSearchModule } from '@fastgpt/global/core/module/template/system/datasetSearch';
import {
DatasetSearchModule,
Dataset_SEARCH_DESC
} from '@fastgpt/global/core/module/template/system/datasetSearch';
import { getNanoid } from '@fastgpt/global/common/string/tools';
export async function postForm2Modules(data: AppSimpleEditFormType) {
@@ -489,7 +492,7 @@ export async function postForm2Modules(data: AppSimpleEditFormType) {
{
moduleId: '0voh5n',
name: '知识库搜索',
intro: '调用知识库搜索能力,查找“有可能”与问题相关的内容',
intro: Dataset_SEARCH_DESC,
avatar: '/imgs/module/db.png',
flowType: 'datasetSearchNode',
showStatus: true,
@@ -640,14 +643,6 @@ export async function postForm2Modules(data: AppSimpleEditFormType) {
key: 'quoteQA'
}
]
},
{
key: 'finish',
label: 'core.module.output.label.running done',
description: 'core.module.output.description.running done',
valueType: 'boolean',
type: 'source',
targets: []
}
]
}
@@ -800,14 +795,6 @@ export async function postForm2Modules(data: AppSimpleEditFormType) {
type: 'source',
valueType: 'datasetQuote',
targets: []
},
{
key: 'finish',
label: 'core.module.output.label.running done',
description: 'core.module.output.description.running done',
valueType: 'boolean',
type: 'source',
targets: []
}
]
}

View File

@@ -1,7 +1,7 @@
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyModal from '@fastgpt/web/components/common/MyModal';
import ParentPaths from '@/components/common/ParentPaths';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { getDatasetCollectionPathById, getDatasetCollections } from '@/web/core/dataset/api';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { Box, Flex, ModalFooter, Button, useTheme, Grid, Card, ModalBody } from '@chakra-ui/react';

View File

@@ -17,7 +17,13 @@ export const getPluginPaths = (parentId?: string) =>
// http plugin
export const getApiSchemaByUrl = (url: string) =>
POST<Object>('/core/plugin/httpPlugin/getApiSchemaByUrl', { url });
POST<Object>(
'/core/plugin/httpPlugin/getApiSchemaByUrl',
{ url },
{
timeout: 30000
}
);
/* work flow */
export const getPlugTemplates = () => GET<FlowNodeTemplateType[]>('/core/plugin/templates');

View File

@@ -1,9 +1,17 @@
import { GET, POST, PUT } from '@/web/common/api/request';
import type { PagingData, RequestPaging } from '@/types';
import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
import { SystemMsgModalValueType } from '@fastgpt/service/support/user/inform/type';
export const getInforms = (data: RequestPaging) =>
POST<PagingData<UserInformSchema>>(`/proApi/support/user/inform/list`, data);
export const getUnreadCount = () => GET<number>(`/proApi/support/user/inform/countUnread`);
export const getUnreadCount = () =>
GET<{
unReadCount: number;
importantInforms: UserInformSchema[];
}>(`/proApi/support/user/inform/countUnread`);
export const readInform = (id: string) => GET(`/proApi/support/user/inform/read`, { id });
export const getSystemMsgModalData = () =>
GET<SystemMsgModalValueType>(`/proApi/support/user/inform/getSystemMsgModal`);

View File

@@ -6,9 +6,11 @@ import type { UserType } from '@fastgpt/global/support/user/type.d';
import { getTokenLogin, putUserInfo } from '@/web/support/user/api';
import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { getTeamPlanStatus } from './team/api';
import { useSystemStore } from '@/web/common/system/useSystemStore';
type State = {
systemMsgReadId: string;
setSysMsgReadId: (id: string) => void;
userInfo: UserType | null;
initUserInfo: () => Promise<UserType>;
setUserInfo: (user: UserType | null) => void;
@@ -21,6 +23,13 @@ export const useUserStore = create<State>()(
devtools(
persist(
immer((set, get) => ({
systemMsgReadId: '',
setSysMsgReadId(id: string) {
set((state) => {
state.systemMsgReadId = id;
});
},
userInfo: null,
async initUserInfo() {
get().initTeamPlanStatus();
@@ -71,7 +80,9 @@ export const useUserStore = create<State>()(
})),
{
name: 'userStore',
partialize: (state) => ({})
partialize: (state) => ({
systemMsgReadId: state.systemMsgReadId
})
}
)
)