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:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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%'}>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
|
||||
@@ -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')} />
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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';
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user