Commit 5054ccd4 by Archer Committed by GitHub

fix: start node check (#5794)

* fix: start node check

* remove log

* fix: variables refresh

* fix: workflow start check

* fix: variables refresh

* perf: auto save

* perf: add log
parent 44e9299d
...@@ -100,7 +100,7 @@ export const getNextTimeByCronStringAndTimezone = ({ ...@@ -100,7 +100,7 @@ export const getNextTimeByCronStringAndTimezone = ({
return new Date(date); return new Date(date);
} catch (error) { } catch (error) {
console.log('getNextTimeByCronStringAndTimezone error', error); console.log(`getNextTimeByCronStringAndTimezone error: ${cronString}`, error);
return new Date(); return new Date();
} }
}; };
...@@ -108,6 +108,7 @@ export type RuntimeNodeItemType = { ...@@ -108,6 +108,7 @@ export type RuntimeNodeItemType = {
flowNodeType: StoreNodeItemType['flowNodeType']; flowNodeType: StoreNodeItemType['flowNodeType'];
showStatus?: StoreNodeItemType['showStatus']; showStatus?: StoreNodeItemType['showStatus'];
isEntry?: boolean; isEntry?: boolean;
isStart?: boolean;
version?: string; version?: string;
inputs: FlowNodeInputItemType[]; inputs: FlowNodeInputItemType[];
......
...@@ -294,11 +294,11 @@ export const checkNodeRunStatus = ({ ...@@ -294,11 +294,11 @@ export const checkNodeRunStatus = ({
runtimeEdges: RuntimeEdgeItemType[]; runtimeEdges: RuntimeEdgeItemType[];
}) => { }) => {
const filterRuntimeEdges = filterWorkflowEdges(runtimeEdges); const filterRuntimeEdges = filterWorkflowEdges(runtimeEdges);
const isStartNode = (nodeType: string) => { const isStartNode = (nodeType: string) => {
const map: Record<any, boolean> = { const map: Record<any, boolean> = {
[FlowNodeTypeEnum.workflowStart]: true, [FlowNodeTypeEnum.workflowStart]: true,
[FlowNodeTypeEnum.pluginInput]: true [FlowNodeTypeEnum.pluginInput]: true,
[FlowNodeTypeEnum.loopStart]: true
}; };
return !!map[nodeType]; return !!map[nodeType];
}; };
...@@ -328,10 +328,12 @@ export const checkNodeRunStatus = ({ ...@@ -328,10 +328,12 @@ export const checkNodeRunStatus = ({
// Start node // Start node
const sourceNode = nodesMap.get(edge.source); const sourceNode = nodesMap.get(edge.source);
if (!sourceNode) continue; if (!sourceNode) continue;
if (isStartNode(sourceNode.flowNodeType)) {
if (isStartNode(sourceNode.flowNodeType) || sourceNode.isStart) {
commonEdges.push(sourceEdge); commonEdges.push(sourceEdge);
continue; continue;
} }
// Circle detected // Circle detected
if (edge.source === targetNode.nodeId) { if (edge.source === targetNode.nodeId) {
recursiveEdgeGroupsMap.set(edge.target, [ recursiveEdgeGroupsMap.set(edge.target, [
...@@ -350,6 +352,7 @@ export const checkNodeRunStatus = ({ ...@@ -350,6 +352,7 @@ export const checkNodeRunStatus = ({
// 查找目标节点的 source edges 并加入栈中 // 查找目标节点的 source edges 并加入栈中
const nextEdges = filterRuntimeEdges.filter((item) => item.target === edge.source); const nextEdges = filterRuntimeEdges.filter((item) => item.target === edge.source);
for (const nextEdge of nextEdges) { for (const nextEdge of nextEdges) {
stack.push({ stack.push({
edge: nextEdge, edge: nextEdge,
...@@ -364,7 +367,7 @@ export const checkNodeRunStatus = ({ ...@@ -364,7 +367,7 @@ export const checkNodeRunStatus = ({
// Classify edges // Classify edges
const { commonEdges, recursiveEdgeGroups } = splitNodeEdges(node); const { commonEdges, recursiveEdgeGroups } = splitNodeEdges(node);
console.log(JSON.stringify({ commonEdges, recursiveEdgeGroups }, null, 2));
// Entry // Entry
if (commonEdges.length === 0 && recursiveEdgeGroups.length === 0) { if (commonEdges.length === 0 && recursiveEdgeGroups.length === 0) {
return 'run'; return 'run';
......
...@@ -62,9 +62,12 @@ export const initToolNodes = ( ...@@ -62,9 +62,12 @@ export const initToolNodes = (
nodes.forEach((node) => { nodes.forEach((node) => {
if (entryNodeIds.includes(node.nodeId)) { if (entryNodeIds.includes(node.nodeId)) {
node.isEntry = true; node.isEntry = true;
node.isStart = true;
if (startParams) { if (startParams) {
node.inputs = updateToolInputValue({ params: startParams, inputs: node.inputs }); node.inputs = updateToolInputValue({ params: startParams, inputs: node.inputs });
} }
} else {
node.isStart = false;
} }
}); });
}; };
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
Textarea, Textarea,
HStack HStack
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useCallback, useEffect, useMemo } from 'react'; import React, { useCallback, useEffect } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
...@@ -61,105 +61,91 @@ const ScheduledTriggerConfig = ({ ...@@ -61,105 +61,91 @@ const ScheduledTriggerConfig = ({
} }
}, []); }, []);
const Render = useMemo(() => { return (
return ( <>
<> <Flex alignItems={'center'}>
<Flex alignItems={'center'}> <MyIcon name={'core/app/schedulePlan'} w={'20px'} />
<MyIcon name={'core/app/schedulePlan'} w={'20px'} /> <HStack ml={2} flex={1} spacing={1}>
<HStack ml={2} flex={1} spacing={1}> <FormLabel color={'myGray.600'}>{t('common:core.app.Interval timer run')}</FormLabel>
<FormLabel color={'myGray.600'}>{t('common:core.app.Interval timer run')}</FormLabel> <QuestionTip label={t('common:core.app.Interval timer tip')} />
<QuestionTip label={t('common:core.app.Interval timer tip')} /> </HStack>
</HStack> <MyTooltip label={t('common:core.app.Config schedule plan')}>
<MyTooltip label={t('common:core.app.Config schedule plan')}> <Button
<Button variant={'transparentBase'}
variant={'transparentBase'} iconSpacing={1}
iconSpacing={1} size={'sm'}
size={'sm'} mr={'-5px'}
mr={'-5px'} color={'myGray.600'}
color={'myGray.600'} onClick={onOpen}
onClick={onOpen} >
> {cronString2Label(value?.cronString ?? '', t)}
{cronString2Label(value?.cronString ?? '', t)} </Button>
</Button> </MyTooltip>
</MyTooltip> </Flex>
</Flex>
<MyModal <MyModal
isOpen={isOpen} isOpen={isOpen}
onClose={onClose} onClose={onClose}
iconSrc={'core/app/schedulePlan'} iconSrc={'core/app/schedulePlan'}
title={t('common:core.app.Interval timer config')} title={t('common:core.app.Interval timer config')}
overflow={'unset'} overflow={'unset'}
> >
<ModalBody> <ModalBody>
<Flex justifyContent={'space-between'} alignItems={'center'}> <Flex justifyContent={'space-between'} alignItems={'center'}>
<FormLabel flex={'0 0 80px'}>{t('common:core.app.schedule.Open schedule')}</FormLabel> <FormLabel flex={'0 0 80px'}>{t('common:core.app.schedule.Open schedule')}</FormLabel>
<Switch <Switch
isChecked={isOpenSchedule} isChecked={isOpenSchedule}
onChange={(e) => { onChange={(e) => {
if (e.target.checked) { if (e.target.checked) {
onUpdate({ cronString: defaultCronString }); onUpdate({ cronString: defaultCronString });
} else { } else {
onUpdate({ cronString: '' }); onUpdate({ cronString: '' });
} }
}} }}
/> />
</Flex> </Flex>
{isOpenSchedule && ( {isOpenSchedule && (
<> <>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>{t('app:execute_time')}</FormLabel> <FormLabel flex={'0 0 80px'}>{t('app:execute_time')}</FormLabel>
<Box flex={'1 0 0'}> <Box flex={'1 0 0'}>
<ScheduleTimeSelect <ScheduleTimeSelect
cronString={value?.cronString} cronString={value?.cronString}
onChange={(e) => {
onUpdate({ cronString: e });
}}
/>
</Box>
</Flex>
<Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>{t('app:time_zone')}</FormLabel>
<Box flex={'1 0 0'}>
<TimezoneSelect
value={timezone}
onChange={(e) => {
onUpdate({ timezone: e });
}}
/>
</Box>
</Flex>
<Box mt={5}>
<FormLabel mb={1}>{t('common:core.app.schedule.Default prompt')}</FormLabel>
<Textarea
value={defaultPrompt}
rows={8}
bg={'myGray.50'}
placeholder={t('common:core.app.schedule.Default prompt placeholder')}
onChange={(e) => { onChange={(e) => {
onUpdate({ defaultPrompt: e.target.value }); onUpdate({ cronString: e });
}} }}
/> />
</Box> </Box>
</> </Flex>
)} <Flex alignItems={'center'} mt={5}>
</ModalBody> <FormLabel flex={'0 0 80px'}>{t('app:time_zone')}</FormLabel>
</MyModal> <Box flex={'1 0 0'}>
</> <TimezoneSelect
); value={timezone}
}, [ onChange={(e) => {
defaultPrompt, onUpdate({ timezone: e });
isOpen, }}
isOpenSchedule, />
onClose, </Box>
onOpen, </Flex>
onUpdate, <Box mt={5}>
t, <FormLabel mb={1}>{t('common:core.app.schedule.Default prompt')}</FormLabel>
timezone, <Textarea
value?.cronString value={defaultPrompt}
]); rows={8}
bg={'myGray.50'}
return Render; placeholder={t('common:core.app.schedule.Default prompt placeholder')}
onChange={(e) => {
onUpdate({ defaultPrompt: e.target.value });
}}
/>
</Box>
</>
)}
</ModalBody>
</MyModal>
</>
);
}; };
export default React.memo(ScheduledTriggerConfig); export default React.memo(ScheduledTriggerConfig);
import React, { type Dispatch, useMemo } from 'react'; import React, { type Dispatch, useCallback, useMemo } from 'react';
import { type NodeProps, useViewport } from 'reactflow'; import { type NodeProps, useViewport } from 'reactflow';
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
...@@ -18,12 +18,10 @@ import { ...@@ -18,12 +18,10 @@ import {
type AppDetailType, type AppDetailType,
type VariableItemType type VariableItemType
} from '@fastgpt/global/core/app/type'; } from '@fastgpt/global/core/app/type';
import { useMemoizedFn } from 'ahooks';
import VariableEdit from '@/components/core/app/VariableEdit'; import VariableEdit from '@/components/core/app/VariableEdit';
import { AppContext } from '@/pageComponents/app/detail/context'; import { AppContext } from '@/pageComponents/app/detail/context';
import WelcomeTextConfig from '@/components/core/app/WelcomeTextConfig'; import WelcomeTextConfig from '@/components/core/app/WelcomeTextConfig';
import FileSelect from '@/components/core/app/FileSelect'; import FileSelect from '@/components/core/app/FileSelect';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { userFilesInput } from '@fastgpt/global/core/workflow/template/system/workflowStart'; import { userFilesInput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
import Container from '../components/Container'; import Container from '../components/Container';
import AutoExecConfig from '@/components/core/app/AutoExecConfig'; import AutoExecConfig from '@/components/core/app/AutoExecConfig';
...@@ -44,7 +42,7 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => { ...@@ -44,7 +42,7 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
systemConfigNode: data, systemConfigNode: data,
isPublicFetch: true isPublicFetch: true
}); });
}, [data, appDetail]); }, [data, appDetail.chatConfig]);
const componentsProps = useMemo( const componentsProps = useMemo(
() => ({ () => ({
...@@ -54,51 +52,47 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => { ...@@ -54,51 +52,47 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
[chatConfig, setAppDetail] [chatConfig, setAppDetail]
); );
const Render = useMemo(() => { return (
return ( <>
<> <NodeCard
<NodeCard selected={selected}
selected={selected} menuForbid={{
menuForbid={{ debug: true,
debug: true, copy: true,
copy: true, delete: true
delete: true }}
}} {...data}
{...data} >
> <Container>
<Container> <WelcomeText {...componentsProps} />
<WelcomeText {...componentsProps} /> <Box mt={2} pt={2}>
<Box mt={2} pt={2}> <ChatStartVariable {...componentsProps} />
<ChatStartVariable {...componentsProps} /> </Box>
</Box> <Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}> <FileSelectConfig {...componentsProps} />
<FileSelectConfig {...componentsProps} /> </Box>
</Box> <Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}> <TTSGuide {...componentsProps} />
<TTSGuide {...componentsProps} /> </Box>
</Box> <Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}> <WhisperGuide {...componentsProps} />
<WhisperGuide {...componentsProps} /> </Box>
</Box> <Box mt={3} pt={4} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={3} pt={4} borderTop={'base'} borderColor={'myGray.200'}> <QuestionGuide {...componentsProps} />
<QuestionGuide {...componentsProps} /> </Box>
</Box> <Box mt={4} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={4} pt={3} borderTop={'base'} borderColor={'myGray.200'}> <ScheduledTrigger {...componentsProps} />
<ScheduledTrigger {...componentsProps} /> </Box>
</Box> <Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}> <AutoExecute {...componentsProps} />
<AutoExecute {...componentsProps} /> </Box>
</Box> <Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}> <QuestionInputGuide {...componentsProps} />
<QuestionInputGuide {...componentsProps} /> </Box>
</Box> </Container>
</Container> </NodeCard>
</NodeCard> </>
</> );
);
}, [componentsProps, data, selected]);
return Render;
}; };
export default React.memo(NodeUserGuide); export default React.memo(NodeUserGuide);
...@@ -124,15 +118,18 @@ function WelcomeText({ chatConfig: { welcomeText }, setAppDetail }: ComponentPro ...@@ -124,15 +118,18 @@ function WelcomeText({ chatConfig: { welcomeText }, setAppDetail }: ComponentPro
} }
function ChatStartVariable({ chatConfig: { variables = [] }, setAppDetail }: ComponentProps) { function ChatStartVariable({ chatConfig: { variables = [] }, setAppDetail }: ComponentProps) {
const updateVariables = useMemoizedFn((value: VariableItemType[]) => { const updateVariables = useCallback(
setAppDetail((state) => ({ (value: VariableItemType[]) => {
...state, setAppDetail((state) => ({
chatConfig: { ...state,
...state.chatConfig, chatConfig: {
variables: value ...state.chatConfig,
} variables: value
})); }
}); }));
},
[setAppDetail]
);
const { zoom } = useViewport(); const { zoom } = useViewport();
return <VariableEdit variables={variables} onChange={(e) => updateVariables(e)} zoom={zoom} />; return <VariableEdit variables={variables} onChange={(e) => updateVariables(e)} zoom={zoom} />;
......
...@@ -8,7 +8,6 @@ import IOTitle from '../components/IOTitle'; ...@@ -8,7 +8,6 @@ import IOTitle from '../components/IOTitle';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowBufferDataContext } from '../../context/workflowInitContext'; import { WorkflowBufferDataContext } from '../../context/workflowInitContext';
import { useCreation } from 'ahooks';
import { type FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io'; import { type FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
...@@ -16,8 +15,7 @@ import { AppContext } from '@/pageComponents/app/detail/context'; ...@@ -16,8 +15,7 @@ import { AppContext } from '@/pageComponents/app/detail/context';
import { workflowSystemVariables } from '@/web/core/app/utils'; import { workflowSystemVariables } from '@/web/core/app/utils';
import { import {
formatEditorVariablePickerIcon, formatEditorVariablePickerIcon,
getAppChatConfig, getAppChatConfig
getGuideModule
} from '@fastgpt/global/core/workflow/utils'; } from '@fastgpt/global/core/workflow/utils';
import MyDivider from '@fastgpt/web/components/common/MyDivider'; import MyDivider from '@fastgpt/web/components/common/MyDivider';
import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance'; import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
...@@ -48,9 +46,9 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => { ...@@ -48,9 +46,9 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
valueDesc: item.valueDesc valueDesc: item.valueDesc
}; };
}); });
}, [systemConfigNode, appDetail.chatConfig, t]); }, [appDetail.chatConfig, systemConfigNode, t]);
const systemVariables = useMemo( const systemVariables = useMemoEnhance(
() => () =>
workflowSystemVariables.map((item) => ({ workflowSystemVariables.map((item) => ({
id: item.key, id: item.key,
......
...@@ -14,7 +14,7 @@ import React, { ...@@ -14,7 +14,7 @@ import React, {
useState useState
} from 'react'; } from 'react';
import { createContext, useContextSelector } from 'use-context-selector'; import { createContext, useContextSelector } from 'use-context-selector';
import { useDebounceEffect } from 'ahooks'; import { useDebounceEffect, useMemoizedFn, useUnmount } from 'ahooks';
import { WorkflowBufferDataContext, WorkflowInitContext } from './workflowInitContext'; import { WorkflowBufferDataContext, WorkflowInitContext } from './workflowInitContext';
import { compareSnapshot } from '@/web/core/workflow/utils'; import { compareSnapshot } from '@/web/core/workflow/utils';
import { AppContext } from '@/pageComponents/app/detail/context'; import { AppContext } from '@/pageComponents/app/detail/context';
...@@ -105,13 +105,10 @@ export const WorkflowPersistenceProvider: React.FC<PropsWithChildren> = ({ child ...@@ -105,13 +105,10 @@ export const WorkflowPersistenceProvider: React.FC<PropsWithChildren> = ({ child
}, [appDetail.chatConfig, flowData2StoreData, isSaved, onSaveApp]); }, [appDetail.chatConfig, flowData2StoreData, isSaved, onSaveApp]);
// 页面关闭前自动保存 // 页面关闭前自动保存
useEffect(() => { useUnmount(() => {
return () => { autoSaveFn();
if (isProduction) { });
autoSaveFn();
}
};
}, [autoSaveFn]);
useBeforeunload({ useBeforeunload({
tip: t('common:core.tip.leave page'), tip: t('common:core.tip.leave page'),
callback: autoSaveFn callback: autoSaveFn
......
...@@ -188,12 +188,7 @@ const SelectAppModal = ({ ...@@ -188,12 +188,7 @@ const SelectAppModal = ({
onClick={handleItemClick} onClick={handleItemClick}
> >
<Flex alignItems={'center'} w={'1.25rem'} onClick={(e) => e.stopPropagation()}> <Flex alignItems={'center'} w={'1.25rem'} onClick={(e) => e.stopPropagation()}>
{!isFolder && ( {!isFolder && <Checkbox isChecked={selected} onChange={handleItemClick} />}
<Checkbox
isChecked={selected}
onChange={handleItemClick}
/>
)}
</Flex> </Flex>
<Avatar src={item.avatar} w="1.5rem" borderRadius={'sm'} /> <Avatar src={item.avatar} w="1.5rem" borderRadius={'sm'} />
<Box>{item.name}</Box> <Box>{item.name}</Box>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment