loop node dynamic height (#3092)

* loop node dynamic height

* fix

* fix
This commit is contained in:
heheer
2024-11-08 12:10:15 +08:00
committed by archer
parent 91645cc420
commit 8ede7add01
11 changed files with 176 additions and 118 deletions

View File

@@ -5,7 +5,7 @@ import { StoreNodeItemType } from '../type/node';
import { StoreEdgeItemType } from '../type/edge'; import { StoreEdgeItemType } from '../type/edge';
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type'; import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
import { VARIABLE_NODE_ID } from '../constants'; import { VARIABLE_NODE_ID } from '../constants';
import { isReferenceValue, isReferenceValueArray } from '../utils'; import { isReferenceValueFormat } from '../utils';
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io'; import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type'; import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
@@ -244,7 +244,7 @@ export const getReferenceVariableValue = ({
const nodeIds = nodes.map((node) => node.nodeId); const nodeIds = nodes.map((node) => node.nodeId);
// handle single reference value // handle single reference value
if (isReferenceValue(value, nodeIds)) { if (isReferenceValueFormat(value)) {
const sourceNodeId = value[0]; const sourceNodeId = value[0];
const outputId = value[1]; const outputId = value[1];
@@ -261,7 +261,11 @@ export const getReferenceVariableValue = ({
} }
// handle reference array // handle reference array
if (isReferenceValueArray(value, nodeIds)) { if (
Array.isArray(value) &&
value.length > 0 &&
value.every((item) => isReferenceValueFormat(item))
) {
const result = value.map<any>((val) => { const result = value.map<any>((val) => {
return getReferenceVariableValue({ return getReferenceVariableValue({
value: val, value: val,
@@ -270,7 +274,7 @@ export const getReferenceVariableValue = ({
}); });
}); });
return result.flat(); return result.flat().filter((item) => item !== undefined);
} }
return value; return value;

View File

@@ -111,7 +111,7 @@ export const Input_Template_Node_Height: FlowNodeInputItemType = {
renderTypeList: [FlowNodeInputTypeEnum.hidden], renderTypeList: [FlowNodeInputTypeEnum.hidden],
valueType: WorkflowIOValueTypeEnum.number, valueType: WorkflowIOValueTypeEnum.number,
label: '', label: '',
value: 960 value: 600
}; };
export const Input_Template_Stream_MODE: FlowNodeInputItemType = { export const Input_Template_Stream_MODE: FlowNodeInputItemType = {

View File

@@ -306,6 +306,15 @@ export const isReferenceValue = (value: any, nodeIds: string[]): value is [strin
const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]); const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]);
return validIdSet.has(value[0]); return validIdSet.has(value[0]);
}; };
export const isReferenceValueFormat = (value: any): value is [string, string] => {
return (
Array.isArray(value) &&
value.length === 2 &&
typeof value[0] === 'string' &&
typeof value[1] === 'string'
);
};
export const isReferenceValueArray = ( export const isReferenceValueArray = (
value: any, value: any,
nodeIds: string[] nodeIds: string[]

View File

@@ -501,7 +501,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
nodes: runtimeNodes, nodes: runtimeNodes,
variables variables
}); });
console.log(value, '=-=-');
// Dynamic input is stored in the dynamic key // Dynamic input is stored in the dynamic key
if (input.canEdit && dynamicInput && params[dynamicInput.key]) { if (input.canEdit && dynamicInput && params[dynamicInput.key]) {
params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType); params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType);

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useEffect, useMemo } from 'react';
import { import {
Background, Background,
ControlButton, ControlButton,
@@ -46,17 +46,24 @@ const FlowController = React.memo(function FlowController() {
const isMac = !window ? false : window.navigator.userAgent.toLocaleLowerCase().includes('mac'); const isMac = !window ? false : window.navigator.userAgent.toLocaleLowerCase().includes('mac');
// Controller shortcut key useKeyPress(['ctrl.z', 'meta.z', 'ctrl.shift.z', 'meta.shift.z', 'ctrl.y', 'meta.y'], (e) => {
useKeyPress(['ctrl.z', 'meta.z'], (e) => { e.preventDefault();
e.stopPropagation();
if (!mouseInCanvas) return; if (!mouseInCanvas) return;
undo();
}); const isUndo = e.key.toLowerCase() === 'z' && !e.shiftKey;
useKeyPress(['ctrl.shift.z', 'meta.shift.z', 'ctrl.y', 'meta.y'], (e) => { const isRedo = (e.key.toLowerCase() === 'z' && e.shiftKey) || e.key.toLowerCase() === 'y';
if (!mouseInCanvas) return;
redo(); if (isUndo) {
undo();
} else if (isRedo) {
redo();
}
}); });
useKeyPress(['ctrl.add', 'meta.add', 'ctrl.equalsign', 'meta.equalsign'], (e) => { useKeyPress(['ctrl.add', 'meta.add', 'ctrl.equalsign', 'meta.equalsign'], (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation();
if (!mouseInCanvas) return; if (!mouseInCanvas) return;
zoomIn(); zoomIn();
}); });

View File

@@ -293,9 +293,24 @@ export const useWorkflow = () => {
const { isDowningCtrl } = useKeyboard(); const { isDowningCtrl } = useKeyboard();
// Loop node size and position // Loop node size and position
const resetParentNodeSizeAndPosition = useMemoizedFn((rect: Rect, parentId: string) => { const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
const width = rect.width + 110 > 900 ? rect.width + 110 : 900; const { childNodes, loopNode } = nodes.reduce(
const height = rect.height + 420 > 900 ? rect.height + 420 : 900; (acc, node) => {
if (node.data.parentNodeId === parentId) {
acc.childNodes.push(node);
}
if (node.id === parentId) {
acc.loopNode = node;
}
return acc;
},
{ childNodes: [] as Node[], loopNode: undefined as Node | undefined }
);
const rect = getNodesBounds(childNodes);
// Calculate parent node size with minimum width/height constraints
const width = Math.max(rect.width + 80, 840);
const height = Math.max(rect.height + 80, 600);
// Update parentNode size and position // Update parentNode size and position
onChangeNode({ onChangeNode({
@@ -317,14 +332,17 @@ export const useWorkflow = () => {
} }
}); });
// Calculate position offset
const offsetHeight = loopNode?.height ? loopNode.height - height - 380 : 0;
// Update parentNode position // Update parentNode position
onNodesChange([ onNodesChange([
{ {
id: parentId, id: parentId,
type: 'position', type: 'position',
position: { position: {
x: rect.x - 50, x: rect.x - 70,
y: rect.y - 300 y: rect.y - (320 + offsetHeight)
} }
} }
]); ]);
@@ -335,43 +353,41 @@ export const useWorkflow = () => {
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>(); const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>();
const checkNodeHelpLine = useMemoizedFn((change: NodeChange, nodes: Node[]) => { const checkNodeHelpLine = useMemoizedFn((change: NodeChange, nodes: Node[]) => {
requestAnimationFrame(() => { const positionChange = change.type === 'position' && change.dragging ? change : undefined;
const positionChange = change.type === 'position' && change.dragging ? change : undefined;
if (positionChange?.position) { if (positionChange?.position) {
// 只判断3000px 内的 nodes并按从近到远的顺序排序 // 只判断3000px 内的 nodes并按从近到远的顺序排序
const filterNodes = nodes const filterNodes = nodes
.filter((node) => { .filter((node) => {
if (!positionChange.position) return false; if (!positionChange.position) return false;
return ( return (
Math.abs(node.position.x - positionChange.position.x) <= 3000 && Math.abs(node.position.x - positionChange.position.x) <= 3000 &&
Math.abs(node.position.y - positionChange.position.y) <= 3000 Math.abs(node.position.y - positionChange.position.y) <= 3000
); );
}) })
.sort((a, b) => { .sort((a, b) => {
if (!positionChange.position) return 0; if (!positionChange.position) return 0;
return ( return (
Math.abs(a.position.x - positionChange.position.x) + Math.abs(a.position.x - positionChange.position.x) +
Math.abs(a.position.y - positionChange.position.y) - Math.abs(a.position.y - positionChange.position.y) -
Math.abs(b.position.x - positionChange.position.x) - Math.abs(b.position.x - positionChange.position.x) -
Math.abs(b.position.y - positionChange.position.y) Math.abs(b.position.y - positionChange.position.y)
); );
}) })
.slice(0, 15); .slice(0, 15);
const helperLines = computeHelperLines(positionChange, filterNodes); const helperLines = computeHelperLines(positionChange, filterNodes);
positionChange.position.x = helperLines.snapPosition.x ?? positionChange.position.x; positionChange.position.x = helperLines.snapPosition.x ?? positionChange.position.x;
positionChange.position.y = helperLines.snapPosition.y ?? positionChange.position.y; positionChange.position.y = helperLines.snapPosition.y ?? positionChange.position.y;
setHelperLineHorizontal(helperLines.horizontal); setHelperLineHorizontal(helperLines.horizontal);
setHelperLineVertical(helperLines.vertical); setHelperLineVertical(helperLines.vertical);
} else { } else {
setHelperLineHorizontal(undefined); setHelperLineHorizontal(undefined);
setHelperLineVertical(undefined); setHelperLineVertical(undefined);
} }
});
}); });
// Check if a node is placed on top of a loop node // Check if a node is placed on top of a loop node
@@ -412,9 +428,7 @@ export const useWorkflow = () => {
state.filter((edge) => edge.source !== node.id && edge.target !== node.id) state.filter((edge) => edge.source !== node.id && edge.target !== node.id)
); );
const childNodes = [...nodes.filter((n) => n.data.parentNodeId === parentNode.id), node]; resetParentNodeSizeAndPosition(parentNode.id);
const rect = getNodesBounds(childNodes);
resetParentNodeSizeAndPosition(rect, parentNode.id);
} }
}); });
@@ -469,7 +483,7 @@ export const useWorkflow = () => {
const childNodes = nodes.filter((n) => n.data.parentNodeId === parentId); const childNodes = nodes.filter((n) => n.data.parentNodeId === parentId);
checkNodeHelpLine(change, childNodes); checkNodeHelpLine(change, childNodes);
resetParentNodeSizeAndPosition(getNodesBounds(childNodes), parentId); resetParentNodeSizeAndPosition(parentId);
} }
// If node is parent node, move parent node and child nodes // If node is parent node, move parent node and child nodes
else if (parentNode[node.data.flowNodeType]) { else if (parentNode[node.data.flowNodeType]) {
@@ -679,7 +693,8 @@ export const useWorkflow = () => {
helperLineVertical, helperLineVertical,
onNodeDragStop, onNodeDragStop,
onPaneContextMenu, onPaneContextMenu,
onPaneClick onPaneClick,
resetParentNodeSizeAndPosition
}; };
}; };

View File

@@ -5,7 +5,7 @@
*/ */
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo, useRef, useCallback } from 'react';
import { Background, NodeProps } from 'reactflow'; import { Background, NodeProps } from 'reactflow';
import NodeCard from '../render/NodeCard'; import NodeCard from '../render/NodeCard';
import Container from '../../components/Container'; import Container from '../../components/Container';
@@ -24,17 +24,18 @@ import {
import { Input_Template_Children_Node_List } from '@fastgpt/global/core/workflow/template/input'; import { Input_Template_Children_Node_List } from '@fastgpt/global/core/workflow/template/input';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../context'; import { WorkflowContext } from '../../../context';
import { cloneDeep } from 'lodash';
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils'; import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
import { AppContext } from '../../../../context'; import { AppContext } from '../../../../context';
import { isReferenceValue, isReferenceValueArray } from '@fastgpt/global/core/workflow/utils'; import { isReferenceValue, isReferenceValueArray } from '@fastgpt/global/core/workflow/utils';
import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io'; import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
import { useWorkflow } from '../../hooks/useWorkflow';
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { nodeId, inputs, outputs, isFolded } = data; const { nodeId, inputs, outputs, isFolded } = data;
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v); const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
const { resetParentNodeSizeAndPosition } = useWorkflow();
const loopInputArray = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray); const loopInputArray = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray);
@@ -44,6 +45,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
nodeHeight: inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value nodeHeight: inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value
}; };
}, [inputs]); }, [inputs]);
const childrenNodeIdList = useMemo(() => { const childrenNodeIdList = useMemo(() => {
return JSON.stringify( return JSON.stringify(
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId) nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
@@ -84,6 +86,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
return type ?? WorkflowIOValueTypeEnum.arrayAny; return type ?? WorkflowIOValueTypeEnum.arrayAny;
}, [appDetail.chatConfig, loopInputArray, nodeList]); }, [appDetail.chatConfig, loopInputArray, nodeList]);
useEffect(() => { useEffect(() => {
if (!loopInputArray) return; if (!loopInputArray) return;
onChangeNode({ onChangeNode({
@@ -110,20 +113,15 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
}); });
}, [childrenNodeIdList, nodeId, onChangeNode]); }, [childrenNodeIdList, nodeId, onChangeNode]);
useEffect(() => {
setTimeout(() => {
resetParentNodeSizeAndPosition(nodeId);
}, 0);
}, [loopInputArray, nodeId, resetParentNodeSizeAndPosition]);
const Render = useMemo(() => { const Render = useMemo(() => {
return ( return (
<NodeCard <NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}>
selected={selected}
maxW="full"
{...(!isFolded && {
minW: '900px',
minH: '900px',
w: nodeWidth,
h: nodeHeight
})}
menuForbid={{ copy: true }}
{...data}
>
<Container position={'relative'} flex={1}> <Container position={'relative'} flex={1}>
<IOTitle text={t('common:common.Input')} /> <IOTitle text={t('common:common.Input')} />
<Box mb={6} maxW={'500px'}> <Box mb={6} maxW={'500px'}>
@@ -132,7 +130,17 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}> <FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
{t('workflow:loop_body')} {t('workflow:loop_body')}
</FormLabel> </FormLabel>
<Box flex={1} position={'relative'} border={'base'} bg={'myGray.100'} rounded={'8px'}> <Box
flex={1}
position={'relative'}
border={'base'}
bg={'myGray.100'}
rounded={'8px'}
{...(!isFolded && {
minW: nodeWidth,
minH: nodeHeight
})}
>
<Background /> <Background />
</Box> </Box>
</Container> </Container>

View File

@@ -145,14 +145,25 @@ function Reference({
const onUpdateField = useCallback( const onUpdateField = useCallback(
({ data }: { data: FlowNodeInputItemType }) => { ({ data }: { data: FlowNodeInputItemType }) => {
if (!data.key) return; if (!data.key) return;
const oldType = inputChildren.valueType;
const newType = data.valueType;
let newValue = data.value;
if (oldType?.includes('array') && !newType?.includes('array')) {
newValue = data.value[0];
} else if (!oldType?.includes('array') && newType?.includes('array')) {
newValue = [data.value];
}
onChangeNode({ onChangeNode({
nodeId, nodeId,
type: 'replaceInput', type: 'replaceInput',
key: inputChildren.key, key: inputChildren.key,
value: { value: {
...data, ...inputChildren,
value: data value: newValue,
key: data.key,
label: data.label,
valueType: data.valueType
} }
}); });
}, },

View File

@@ -2,7 +2,10 @@ import React, { useCallback, useMemo } from 'react';
import type { RenderInputProps } from '../type'; import type { RenderInputProps } from '../type';
import { Flex, Box, ButtonProps, Grid } from '@chakra-ui/react'; import { Flex, Box, ButtonProps, Grid } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { computedNodeInputReference } from '@/web/core/workflow/utils'; import {
computedNodeInputReference,
filterWorkflowNodeOutputsByType
} from '@/web/core/workflow/utils';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { import {
NodeOutputKeyEnum, NodeOutputKeyEnum,
@@ -77,7 +80,6 @@ export const useReference = ({
if (!sourceNodes) return []; if (!sourceNodes) return [];
const isArray = valueType?.includes('array'); const isArray = valueType?.includes('array');
const arrayItemType = isArray ? valueType.replace('array', '').toLowerCase() : valueType;
// 转换为 select 的数据结构 // 转换为 select 的数据结构
const list: CommonSelectProps['list'] = sourceNodes const list: CommonSelectProps['list'] = sourceNodes
@@ -90,16 +92,7 @@ export const useReference = ({
</Flex> </Flex>
), ),
value: node.nodeId, value: node.nodeId,
children: node.outputs children: filterWorkflowNodeOutputsByType(node.outputs, valueType)
.filter(
(output) =>
valueType === WorkflowIOValueTypeEnum.any ||
valueType === WorkflowIOValueTypeEnum.arrayAny ||
output.valueType === WorkflowIOValueTypeEnum.any ||
output.valueType === valueType ||
// Array<String> can select string
arrayItemType === output.valueType
)
.filter((output) => output.id !== NodeOutputKeyEnum.addOutputParam) .filter((output) => output.id !== NodeOutputKeyEnum.addOutputParam)
.map((output) => { .map((output) => {
return { return {
@@ -199,7 +192,7 @@ const SingleReferenceSelector = ({
label={ label={
isValidSelect ? ( isValidSelect ? (
<Flex gap={2} alignItems={'center'} fontSize={'sm'}> <Flex gap={2} alignItems={'center'} fontSize={'sm'}>
<Flex py={1} pl={1}> <Flex py={1} pl={1} alignItems={'center'}>
{nodeName} {nodeName}
<MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} /> <MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} />
{outputName} {outputName}
@@ -249,20 +242,14 @@ const MultipleReferenceSelector = ({
const ArraySelector = useMemo(() => { const ArraySelector = useMemo(() => {
const selectorVal = value as ReferenceItemValueType[]; const selectorVal = value as ReferenceItemValueType[];
const notValidItem = const validSelectValue = selectorVal && selectorVal.length > 0;
!selectorVal ||
selectorVal.length === 0 ||
selectorVal.every((item) => {
const [nodeName, outputName] = getSelectValue(item);
return !nodeName || !outputName;
});
return ( return (
<MultipleRowArraySelect <MultipleRowArraySelect
label={ label={
!notValidItem ? ( validSelectValue ? (
<Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}> <Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}>
{selectorVal.map((item, index) => { {selectorVal?.map((item, index) => {
const [nodeName, outputName] = getSelectValue(item); const [nodeName, outputName] = getSelectValue(item);
const isInvalidItem = !nodeName || !outputName; const isInvalidItem = !nodeName || !outputName;
@@ -270,8 +257,8 @@ const MultipleReferenceSelector = ({
<Flex <Flex
alignItems={'center'} alignItems={'center'}
key={index} key={index}
bg={'primary.50'} bg={isInvalidItem ? 'red.50' : 'primary.50'}
color={'myGray.900'} color={isInvalidItem ? 'red.500' : 'myGray.900'}
py={1} py={1}
px={1.5} px={1.5}
rounded={'sm'} rounded={'sm'}
@@ -282,21 +269,27 @@ const MultipleReferenceSelector = ({
maxW={'200px'} maxW={'200px'}
className="textEllipsis" className="textEllipsis"
> >
{nodeName} {isInvalidItem ? (
<MyIcon <>{t('common:invalid_variable')}</>
name={'common/rightArrowLight'} ) : (
mx={1} <>
w={'12px'} {nodeName}
color={'myGray.500'} <MyIcon
/> name={'common/rightArrowLight'}
{outputName} mx={1}
w={'12px'}
color={'myGray.500'}
/>
{outputName}
</>
)}
</Flex> </Flex>
<MyIcon <MyIcon
name={'common/closeLight'} name={'common/closeLight'}
w={'1rem'} w={'1rem'}
ml={1} ml={1}
cursor={'pointer'} cursor={'pointer'}
color={'myGray.500'} color={isInvalidItem ? 'red.500' : 'myGray.500'}
_hover={{ _hover={{
color: 'red.600' color: 'red.600'
}} }}
@@ -321,7 +314,7 @@ const MultipleReferenceSelector = ({
popDirection={popDirection} popDirection={popDirection}
/> />
); );
}, [getSelectValue, list, onSelect, placeholder, popDirection, value]); }, [getSelectValue, list, onSelect, placeholder, popDirection, t, value]);
return ArraySelector; return ArraySelector;
}; };

View File

@@ -355,7 +355,6 @@ const WorkflowContextProvider = ({
const getNodes = useContextSelector(WorkflowActionContext, (state) => state.getNodes); const getNodes = useContextSelector(WorkflowActionContext, (state) => state.getNodes);
const nodeListString = useContextSelector(WorkflowActionContext, (state) => state.nodeListString); const nodeListString = useContextSelector(WorkflowActionContext, (state) => state.nodeListString);
console.log(121211111111);
const nodeList = useMemo( const nodeList = useMemo(
() => JSON.parse(nodeListString) as FlowNodeItemType[], () => JSON.parse(nodeListString) as FlowNodeItemType[],
[nodeListString] [nodeListString]

View File

@@ -24,7 +24,7 @@ import {
getAppChatConfig, getAppChatConfig,
getGuideModule, getGuideModule,
isReferenceValue, isReferenceValue,
isReferenceValueArray isReferenceValueFormat
} from '@fastgpt/global/core/workflow/utils'; } from '@fastgpt/global/core/workflow/utils';
import { TFunction } from 'next-i18next'; import { TFunction } from 'next-i18next';
import { import {
@@ -263,6 +263,20 @@ export const getRefData = ({
}; };
}; };
export const filterWorkflowNodeOutputsByType = (
outputs: FlowNodeOutputItemType[],
valueType: WorkflowIOValueTypeEnum
): FlowNodeOutputItemType[] => {
return outputs.filter(
(output) =>
valueType === WorkflowIOValueTypeEnum.any ||
valueType === WorkflowIOValueTypeEnum.arrayAny ||
output.valueType === WorkflowIOValueTypeEnum.any ||
output.valueType === valueType ||
valueType?.replace('array', '').toLowerCase() === output.valueType
);
};
/* Connection rules */ /* Connection rules */
export const checkWorkflowNodeAndConnection = ({ export const checkWorkflowNodeAndConnection = ({
nodes, nodes,
@@ -337,7 +351,7 @@ export const checkWorkflowNodeAndConnection = ({
// check reference invalid // check reference invalid
const renderType = input.renderTypeList[input.selectedTypeIndex || 0]; const renderType = input.renderTypeList[input.selectedTypeIndex || 0];
if (renderType === FlowNodeInputTypeEnum.reference && input.required) { if (renderType === FlowNodeInputTypeEnum.reference) {
const checkReference = (value: [string, string]) => { const checkReference = (value: [string, string]) => {
if (value[0] === VARIABLE_NODE_ID) { if (value[0] === VARIABLE_NODE_ID) {
return false; return false;
@@ -348,18 +362,16 @@ export const checkWorkflowNodeAndConnection = ({
return true; return true;
} }
const sourceOutput = sourceNode.data.outputs.find((item) => item.id === value[1]); const sourceOutput = filterWorkflowNodeOutputsByType(
sourceNode.data.outputs,
input.valueType as WorkflowIOValueTypeEnum
).find((item) => item.id === value[1]);
return !sourceOutput; return !sourceOutput;
}; };
// Old format // Old format
if ( if (isReferenceValueFormat(input.value)) {
Array.isArray(input.value) && return input.required && checkReference(input.value);
input.value.length === 2 &&
typeof input.value[0] === 'string' &&
typeof input.value[1] === 'string'
) {
return checkReference(input.value as [string, string]);
} }
// New format // New format