4.8.13 test (#3098)
* perf: loop node refresh * rename context * comment * fix: ts * perf: push chat log
This commit is contained in:
@@ -335,7 +335,7 @@ export enum ContentTypes {
|
|||||||
raw = 'raw-text'
|
raw = 'raw-text'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ArrayTypeMap = {
|
export const ArrayTypeMap: Record<WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum> = {
|
||||||
[WorkflowIOValueTypeEnum.string]: WorkflowIOValueTypeEnum.arrayString,
|
[WorkflowIOValueTypeEnum.string]: WorkflowIOValueTypeEnum.arrayString,
|
||||||
[WorkflowIOValueTypeEnum.number]: WorkflowIOValueTypeEnum.arrayNumber,
|
[WorkflowIOValueTypeEnum.number]: WorkflowIOValueTypeEnum.arrayNumber,
|
||||||
[WorkflowIOValueTypeEnum.boolean]: WorkflowIOValueTypeEnum.arrayBoolean,
|
[WorkflowIOValueTypeEnum.boolean]: WorkflowIOValueTypeEnum.arrayBoolean,
|
||||||
@@ -343,5 +343,12 @@ export const ArrayTypeMap = {
|
|||||||
[WorkflowIOValueTypeEnum.arrayString]: WorkflowIOValueTypeEnum.arrayString,
|
[WorkflowIOValueTypeEnum.arrayString]: WorkflowIOValueTypeEnum.arrayString,
|
||||||
[WorkflowIOValueTypeEnum.arrayNumber]: WorkflowIOValueTypeEnum.arrayNumber,
|
[WorkflowIOValueTypeEnum.arrayNumber]: WorkflowIOValueTypeEnum.arrayNumber,
|
||||||
[WorkflowIOValueTypeEnum.arrayBoolean]: WorkflowIOValueTypeEnum.arrayBoolean,
|
[WorkflowIOValueTypeEnum.arrayBoolean]: WorkflowIOValueTypeEnum.arrayBoolean,
|
||||||
[WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.arrayObject
|
[WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
[WorkflowIOValueTypeEnum.chatHistory]: WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
[WorkflowIOValueTypeEnum.datasetQuote]: WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
[WorkflowIOValueTypeEnum.dynamic]: WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
[WorkflowIOValueTypeEnum.selectDataset]: WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
[WorkflowIOValueTypeEnum.selectApp]: WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
[WorkflowIOValueTypeEnum.arrayAny]: WorkflowIOValueTypeEnum.arrayAny,
|
||||||
|
[WorkflowIOValueTypeEnum.any]: WorkflowIOValueTypeEnum.arrayAny
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 { isReferenceValueFormat } from '../utils';
|
import { isValidReferenceValueFormat } 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';
|
||||||
@@ -235,20 +235,19 @@ export const getReferenceVariableValue = ({
|
|||||||
nodes,
|
nodes,
|
||||||
variables
|
variables
|
||||||
}: {
|
}: {
|
||||||
value: ReferenceValueType;
|
value?: ReferenceValueType;
|
||||||
nodes: RuntimeNodeItemType[];
|
nodes: RuntimeNodeItemType[];
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
}) => {
|
}) => {
|
||||||
if (!value) return undefined;
|
if (!value) return undefined;
|
||||||
|
|
||||||
const nodeIds = nodes.map((node) => node.nodeId);
|
|
||||||
|
|
||||||
// handle single reference value
|
// handle single reference value
|
||||||
if (isReferenceValueFormat(value)) {
|
if (isValidReferenceValueFormat(value)) {
|
||||||
const sourceNodeId = value[0];
|
const sourceNodeId = value[0];
|
||||||
const outputId = value[1];
|
const outputId = value[1];
|
||||||
|
|
||||||
if (sourceNodeId === VARIABLE_NODE_ID && outputId) {
|
if (sourceNodeId === VARIABLE_NODE_ID) {
|
||||||
|
if (!outputId) return undefined;
|
||||||
return variables[outputId];
|
return variables[outputId];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +263,7 @@ export const getReferenceVariableValue = ({
|
|||||||
if (
|
if (
|
||||||
Array.isArray(value) &&
|
Array.isArray(value) &&
|
||||||
value.length > 0 &&
|
value.length > 0 &&
|
||||||
value.every((item) => isReferenceValueFormat(item))
|
value.every((item) => isValidReferenceValueFormat(item))
|
||||||
) {
|
) {
|
||||||
const result = value.map<any>((val) => {
|
const result = value.map<any>((val) => {
|
||||||
return getReferenceVariableValue({
|
return getReferenceVariableValue({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { WorkflowIOValueTypeEnum } from '../../../constants';
|
|||||||
|
|
||||||
export type TUpdateListItem = {
|
export type TUpdateListItem = {
|
||||||
variable?: ReferenceItemValueType;
|
variable?: ReferenceItemValueType;
|
||||||
value: ReferenceValueType; // input: ['',value], reference: [nodeId,outputId]
|
value?: ReferenceValueType; // input: ['',value], reference: [nodeId,outputId]
|
||||||
valueType?: WorkflowIOValueTypeEnum;
|
valueType?: WorkflowIOValueTypeEnum;
|
||||||
renderType: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference;
|
renderType: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference;
|
||||||
};
|
};
|
||||||
|
|||||||
4
packages/global/core/workflow/type/io.d.ts
vendored
4
packages/global/core/workflow/type/io.d.ts
vendored
@@ -80,6 +80,6 @@ export type FlowNodeOutputItemType = {
|
|||||||
customFieldConfig?: CustomFieldConfigType;
|
customFieldConfig?: CustomFieldConfigType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReferenceItemValueType = undefined | [string, string];
|
export type ReferenceItemValueType = [string, string | undefined];
|
||||||
export type ReferenceArrayValueType = undefined | [string, string][];
|
export type ReferenceArrayValueType = ReferenceItemValueType[];
|
||||||
export type ReferenceValueType = ReferenceItemValueType | ReferenceArrayValueType;
|
export type ReferenceValueType = ReferenceItemValueType | ReferenceArrayValueType;
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ import {
|
|||||||
VARIABLE_NODE_ID,
|
VARIABLE_NODE_ID,
|
||||||
NodeOutputKeyEnum
|
NodeOutputKeyEnum
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './type/io.d';
|
import {
|
||||||
|
FlowNodeInputItemType,
|
||||||
|
FlowNodeOutputItemType,
|
||||||
|
ReferenceArrayValueType,
|
||||||
|
ReferenceItemValueType
|
||||||
|
} from './type/io.d';
|
||||||
import { StoreNodeItemType } from './type/node';
|
import { StoreNodeItemType } from './type/node';
|
||||||
import type {
|
import type {
|
||||||
VariableItemType,
|
VariableItemType,
|
||||||
@@ -300,28 +305,37 @@ export const formatEditorVariablePickerIcon = (
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isReferenceValue = (value: any, nodeIds: string[]): value is [string, string] => {
|
// Check the value is a valid reference value format: [variableId, outputId]
|
||||||
if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== 'string') return false;
|
export const isValidReferenceValueFormat = (value: any): value is ReferenceItemValueType => {
|
||||||
|
return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string';
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
Check whether the value([variableId, outputId]) value is a valid reference value:
|
||||||
|
1. The value must be an array of length 2
|
||||||
|
2. The first item of the array must be one of VARIABLE_NODE_ID or nodeIds
|
||||||
|
*/
|
||||||
|
export const isValidReferenceValue = (
|
||||||
|
value: any,
|
||||||
|
nodeIds: string[]
|
||||||
|
): value is ReferenceItemValueType => {
|
||||||
|
if (!isValidReferenceValueFormat(value)) return false;
|
||||||
|
|
||||||
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] => {
|
Check whether the value([variableId, outputId][]) value is a valid reference value array:
|
||||||
return (
|
1. The value must be an array
|
||||||
Array.isArray(value) &&
|
2. The array must contain at least one element
|
||||||
value.length === 2 &&
|
3. Each element in the array must be a valid reference value
|
||||||
typeof value[0] === 'string' &&
|
*/
|
||||||
typeof value[1] === 'string'
|
export const isValidArrayReferenceValue = (
|
||||||
);
|
|
||||||
};
|
|
||||||
export const isReferenceValueArray = (
|
|
||||||
value: any,
|
value: any,
|
||||||
nodeIds: string[]
|
nodeIds: string[]
|
||||||
): value is [string, string][] => {
|
): value is ReferenceArrayValueType => {
|
||||||
if (!Array.isArray(value) || value.length === 0) return false;
|
if (!Array.isArray(value)) return false;
|
||||||
|
|
||||||
return value.every((item) => isReferenceValue(item, nodeIds));
|
return value.every((item) => isValidReferenceValue(item, nodeIds));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getElseIFLabel = (i: number) => {
|
export const getElseIFLabel = (i: number) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { addLog } from '../../common/system/log';
|
|||||||
import { MongoChatItem } from './chatItemSchema';
|
import { MongoChatItem } from './chatItemSchema';
|
||||||
import { MongoChat } from './chatSchema';
|
import { MongoChat } from './chatSchema';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { ChatItemType } from '@fastgpt/global/core/chat/type';
|
import { AIChatItemType, ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
|
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
@@ -81,17 +81,15 @@ const pushChatLogInternal = async ({
|
|||||||
metadata?: Metadata;
|
metadata?: Metadata;
|
||||||
}) => {
|
}) => {
|
||||||
const [chatItemHuman, chatItemAi] = await Promise.all([
|
const [chatItemHuman, chatItemAi] = await Promise.all([
|
||||||
MongoChatItem.findById(chatItemIdHuman).lean() as Promise<ChatItem>,
|
MongoChatItem.findById(chatItemIdHuman).lean(),
|
||||||
MongoChatItem.findById(chatItemIdAi).lean() as Promise<ChatItem>
|
MongoChatItem.findById(chatItemIdAi).lean() as Promise<AIChatItemType>
|
||||||
]);
|
]);
|
||||||
const [chat] = (await MongoChat.find({ chatId }).lean()) as {
|
|
||||||
title: string;
|
if (!chatItemHuman || !chatItemAi) {
|
||||||
outLinkUid: string | undefined;
|
return;
|
||||||
tmbId: string;
|
}
|
||||||
teamId: string;
|
|
||||||
metadata: Object;
|
const chat = await MongoChat.findOne({ chatId }).lean();
|
||||||
source: string;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
// addLog.warn('ChatLogDebug', chat);
|
// addLog.warn('ChatLogDebug', chat);
|
||||||
// addLog.warn('ChatLogDebug', { chatItemHuman, chatItemAi });
|
// addLog.warn('ChatLogDebug', { chatItemHuman, chatItemAi });
|
||||||
@@ -114,10 +112,7 @@ const pushChatLogInternal = async ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const responseData = chatItemAi.responseData;
|
const responseData = chatItemAi.responseData;
|
||||||
let responseTime = 0;
|
const responseTime = responseData?.reduce((acc, item) => acc + (item?.runningTime ?? 0), 0) || 0;
|
||||||
responseData.forEach((item) => {
|
|
||||||
responseTime += item.runningTime;
|
|
||||||
});
|
|
||||||
|
|
||||||
const chatLog: ChatLog = {
|
const chatLog: ChatLog = {
|
||||||
title: chat.title,
|
title: chat.title,
|
||||||
@@ -138,6 +133,7 @@ const pushChatLogInternal = async ({
|
|||||||
responseTime: responseTime * 1000,
|
responseTime: responseTime * 1000,
|
||||||
metadata: metadataString,
|
metadata: metadataString,
|
||||||
sourceName: chat.source ?? '-',
|
sourceName: chat.source ?? '-',
|
||||||
|
// @ts-ignore
|
||||||
createdAt: new Date(chatItemAi.time).getTime(),
|
createdAt: new Date(chatItemAi.time).getTime(),
|
||||||
sourceId: `crbeer-fastgpt-${appId}`
|
sourceId: `crbeer-fastgpt-${appId}`
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
|
|||||||
user,
|
user,
|
||||||
node: { name }
|
node: { name }
|
||||||
} = props;
|
} = props;
|
||||||
const { loopInputArray = [], childrenNodeIdList } = params;
|
const { loopInputArray = [], childrenNodeIdList = [] } = params;
|
||||||
|
|
||||||
if (!Array.isArray(loopInputArray)) {
|
if (!Array.isArray(loopInputArray)) {
|
||||||
return Promise.reject('Input value is not an array');
|
return Promise.reject('Input value is not an array');
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { removeSystemVariable, valueTypeFormat } from '../utils';
|
import { removeSystemVariable, valueTypeFormat } from '../utils';
|
||||||
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
import { isValidReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
||||||
@@ -27,13 +27,17 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
|
|||||||
const result = updateList.map((item) => {
|
const result = updateList.map((item) => {
|
||||||
const variable = item.variable;
|
const variable = item.variable;
|
||||||
|
|
||||||
if (!isReferenceValue(variable, nodeIds)) {
|
if (!isValidReferenceValue(variable, nodeIds)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const varNodeId = variable[0];
|
const varNodeId = variable[0];
|
||||||
const varKey = variable[1];
|
const varKey = variable[1];
|
||||||
|
|
||||||
|
if (!varKey) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const value = (() => {
|
const value = (() => {
|
||||||
// If first item is empty, it means it is a input value
|
// If first item is empty, it means it is a input value
|
||||||
if (!item.value?.[0]) {
|
if (!item.value?.[0]) {
|
||||||
|
|||||||
@@ -50,5 +50,5 @@ WORKFLOW_MAX_LOOP_TIMES=50
|
|||||||
|
|
||||||
# 对话日志推送服务
|
# 对话日志推送服务
|
||||||
# URL/INTERVAL 为空时不推送
|
# URL/INTERVAL 为空时不推送
|
||||||
CHAT_LOG_URL=http://localhost:8080
|
# CHAT_LOG_URL=http://localhost:8080
|
||||||
CHAT_LOG_INTERVAL=10000
|
# CHAT_LOG_INTERVAL=10000
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|||||||
import SaveButton from '../Workflow/components/SaveButton';
|
import SaveButton from '../Workflow/components/SaveButton';
|
||||||
import PublishHistories from '../PublishHistoriesSlider';
|
import PublishHistories from '../PublishHistoriesSlider';
|
||||||
import {
|
import {
|
||||||
WorkflowActionContext,
|
WorkflowNodeEdgeContext,
|
||||||
WorkflowInitContext
|
WorkflowInitContext
|
||||||
} from '../WorkflowComponents/context/workflowInitContext';
|
} from '../WorkflowComponents/context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
|
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
|
||||||
@@ -50,7 +50,7 @@ const Header = () => {
|
|||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const {
|
const {
|
||||||
flowData2StoreData,
|
flowData2StoreData,
|
||||||
flowData2StoreDataAndCheck,
|
flowData2StoreDataAndCheck,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|||||||
import SaveButton from './components/SaveButton';
|
import SaveButton from './components/SaveButton';
|
||||||
import PublishHistories from '../PublishHistoriesSlider';
|
import PublishHistories from '../PublishHistoriesSlider';
|
||||||
import {
|
import {
|
||||||
WorkflowActionContext,
|
WorkflowNodeEdgeContext,
|
||||||
WorkflowInitContext
|
WorkflowInitContext
|
||||||
} from '../WorkflowComponents/context/workflowInitContext';
|
} from '../WorkflowComponents/context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
|
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
|
||||||
@@ -54,7 +54,7 @@ const Header = () => {
|
|||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const {
|
const {
|
||||||
flowData2StoreData,
|
flowData2StoreData,
|
||||||
flowData2StoreDataAndCheck,
|
flowData2StoreDataAndCheck,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import { useUserStore } from '@/web/support/user/useUserStore';
|
|||||||
import { LoopStartNode } from '@fastgpt/global/core/workflow/template/system/loop/loopStart';
|
import { LoopStartNode } from '@fastgpt/global/core/workflow/template/system/loop/loopStart';
|
||||||
import { LoopEndNode } from '@fastgpt/global/core/workflow/template/system/loop/loopEnd';
|
import { LoopEndNode } from '@fastgpt/global/core/workflow/template/system/loop/loopEnd';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { WorkflowActionContext } from '../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../context/workflowInitContext';
|
||||||
|
|
||||||
type ModuleTemplateListProps = {
|
type ModuleTemplateListProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -389,7 +389,7 @@ const RenderList = React.memo(function RenderList({
|
|||||||
const { computedNewNodeName } = useWorkflowUtils();
|
const { computedNewNodeName } = useWorkflowUtils();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const setNodes = useContextSelector(WorkflowActionContext, (v) => v.setNodes);
|
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
|
|
||||||
const formatTemplates = useMemo<NodeTemplateListType>(() => {
|
const formatTemplates = useMemo<NodeTemplateListType>(() => {
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import { NodeOutputKeyEnum, RuntimeEdgeStatusEnum } from '@fastgpt/global/core/w
|
|||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../context';
|
import { WorkflowContext } from '../../context';
|
||||||
import { useThrottleEffect } from 'ahooks';
|
import { useThrottleEffect } from 'ahooks';
|
||||||
import { WorkflowActionContext, WorkflowInitContext } from '../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext, WorkflowInitContext } from '../../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
||||||
|
|
||||||
const ButtonEdge = (props: EdgeProps) => {
|
const ButtonEdge = (props: EdgeProps) => {
|
||||||
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
||||||
const onEdgesChange = useContextSelector(WorkflowActionContext, (v) => v.onEdgesChange);
|
const onEdgesChange = useContextSelector(WorkflowNodeEdgeContext, (v) => v.onEdgesChange);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
const workflowDebugData = useContextSelector(WorkflowContext, (v) => v.workflowDebugData);
|
const workflowDebugData = useContextSelector(WorkflowContext, (v) => v.workflowDebugData);
|
||||||
const hoverEdgeId = useContextSelector(WorkflowEventContext, (v) => v.hoverEdgeId);
|
const hoverEdgeId = useContextSelector(WorkflowEventContext, (v) => v.hoverEdgeId);
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { CommentNode } from '@fastgpt/global/core/workflow/template/system/comme
|
|||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../context';
|
import { WorkflowContext } from '../../context';
|
||||||
import { useReactFlow } from 'reactflow';
|
import { useReactFlow } from 'reactflow';
|
||||||
import { WorkflowActionContext } from '../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
||||||
|
|
||||||
const ContextMenu = () => {
|
const ContextMenu = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const setNodes = useContextSelector(WorkflowActionContext, (v) => v.setNodes);
|
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||||
const menu = useContextSelector(WorkflowEventContext, (v) => v.menu);
|
const menu = useContextSelector(WorkflowEventContext, (v) => v.menu);
|
||||||
const setMenu = useContextSelector(WorkflowEventContext, (ctx) => ctx.setMenu);
|
const setMenu = useContextSelector(WorkflowEventContext, (ctx) => ctx.setMenu);
|
||||||
|
|
||||||
|
|||||||
@@ -51,13 +51,12 @@ const FlowController = React.memo(function FlowController() {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (!mouseInCanvas) return;
|
if (!mouseInCanvas) return;
|
||||||
|
|
||||||
const isUndo = e.key.toLowerCase() === 'z' && !e.shiftKey;
|
|
||||||
const isRedo = (e.key.toLowerCase() === 'z' && e.shiftKey) || e.key.toLowerCase() === 'y';
|
const isRedo = (e.key.toLowerCase() === 'z' && e.shiftKey) || e.key.toLowerCase() === 'y';
|
||||||
|
|
||||||
if (isUndo) {
|
if (isRedo) {
|
||||||
undo();
|
|
||||||
} else if (isRedo) {
|
|
||||||
redo();
|
redo();
|
||||||
|
} else {
|
||||||
|
undo();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import { AppContext } from '../../../context';
|
|||||||
import { VariableInputItem } from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput';
|
import { VariableInputItem } from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput';
|
||||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||||
import { WorkflowActionContext } from '../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
|
||||||
|
|
||||||
const MyRightDrawer = dynamic(
|
const MyRightDrawer = dynamic(
|
||||||
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
|
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
|
||||||
@@ -50,9 +50,9 @@ export const useDebug = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const setNodes = useContextSelector(WorkflowActionContext, (v) => v.setNodes);
|
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||||
const getNodes = useContextSelector(WorkflowActionContext, (v) => v.getNodes);
|
const getNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.getNodes);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
|
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
|
||||||
const onStartNodeDebug = useContextSelector(WorkflowContext, (v) => v.onStartNodeDebug);
|
const onStartNodeDebug = useContextSelector(WorkflowContext, (v) => v.onStartNodeDebug);
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import { useContextSelector } from 'use-context-selector';
|
|||||||
import { useWorkflowUtils } from './useUtils';
|
import { useWorkflowUtils } from './useUtils';
|
||||||
import { useKeyPress as useKeyPressEffect } from 'ahooks';
|
import { useKeyPress as useKeyPressEffect } from 'ahooks';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { WorkflowActionContext } from '../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
||||||
|
|
||||||
export const useKeyboard = () => {
|
export const useKeyboard = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const getNodes = useContextSelector(WorkflowActionContext, (v) => v.getNodes);
|
const getNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.getNodes);
|
||||||
const setNodes = useContextSelector(WorkflowActionContext, (v) => v.setNodes);
|
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||||
const mouseInCanvas = useContextSelector(WorkflowEventContext, (v) => v.mouseInCanvas);
|
const mouseInCanvas = useContextSelector(WorkflowEventContext, (v) => v.mouseInCanvas);
|
||||||
|
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
Input_Template_Node_Width
|
Input_Template_Node_Width
|
||||||
} from '@fastgpt/global/core/workflow/template/input';
|
} from '@fastgpt/global/core/workflow/template/input';
|
||||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import { WorkflowActionContext, WorkflowInitContext } from '../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext, WorkflowInitContext } from '../../context/workflowInitContext';
|
||||||
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
|
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
|
||||||
import { AppContext } from '../../../context';
|
import { AppContext } from '../../../context';
|
||||||
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
||||||
@@ -278,10 +278,10 @@ export const useWorkflow = () => {
|
|||||||
const appDetail = useContextSelector(AppContext, (e) => e.appDetail);
|
const appDetail = useContextSelector(AppContext, (e) => e.appDetail);
|
||||||
|
|
||||||
const nodes = useContextSelector(WorkflowInitContext, (state) => state.nodes);
|
const nodes = useContextSelector(WorkflowInitContext, (state) => state.nodes);
|
||||||
const onNodesChange = useContextSelector(WorkflowActionContext, (state) => state.onNodesChange);
|
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (state) => state.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (state) => state.edges);
|
||||||
const setEdges = useContextSelector(WorkflowActionContext, (v) => v.setEdges);
|
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges);
|
||||||
const onEdgesChange = useContextSelector(WorkflowActionContext, (v) => v.onEdgesChange);
|
const onEdgesChange = useContextSelector(WorkflowNodeEdgeContext, (v) => v.onEdgesChange);
|
||||||
const { setConnectingEdge, nodeList, onChangeNode, pushPastSnapshot } = useContextSelector(
|
const { setConnectingEdge, nodeList, onChangeNode, pushPastSnapshot } = useContextSelector(
|
||||||
WorkflowContext,
|
WorkflowContext,
|
||||||
(v) => v
|
(v) => v
|
||||||
@@ -294,20 +294,9 @@ export const useWorkflow = () => {
|
|||||||
|
|
||||||
// Loop node size and position
|
// Loop node size and position
|
||||||
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
||||||
const { childNodes, loopNode } = nodes.reduce(
|
const childNodes = nodes.filter((node) => node.data.parentNodeId === parentId);
|
||||||
(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);
|
|
||||||
|
|
||||||
|
const rect = getNodesBounds(childNodes);
|
||||||
// Calculate parent node size with minimum width/height constraints
|
// Calculate parent node size with minimum width/height constraints
|
||||||
const width = Math.max(rect.width + 80, 840);
|
const width = Math.max(rect.width + 80, 840);
|
||||||
const height = Math.max(rect.height + 80, 600);
|
const height = Math.max(rect.height + 80, 600);
|
||||||
@@ -332,9 +321,6 @@ export const useWorkflow = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Calculate position offset
|
|
||||||
const offsetHeight = loopNode?.height ? loopNode.height - height - 380 : 0;
|
|
||||||
|
|
||||||
// Update parentNode position
|
// Update parentNode position
|
||||||
onNodesChange([
|
onNodesChange([
|
||||||
{
|
{
|
||||||
@@ -342,7 +328,7 @@ export const useWorkflow = () => {
|
|||||||
type: 'position',
|
type: 'position',
|
||||||
position: {
|
position: {
|
||||||
x: rect.x - 70,
|
x: rect.x - 70,
|
||||||
y: rect.y - (320 + offsetHeight)
|
y: rect.y - 320
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@@ -392,15 +378,6 @@ export const useWorkflow = () => {
|
|||||||
|
|
||||||
// Check if a node is placed on top of a loop node
|
// Check if a node is placed on top of a loop node
|
||||||
const checkNodeOverLoopNode = useMemoizedFn((node: Node) => {
|
const checkNodeOverLoopNode = useMemoizedFn((node: Node) => {
|
||||||
if (!node) return;
|
|
||||||
|
|
||||||
// 获取所有与当前节点相交的节点
|
|
||||||
const intersections = getIntersectingNodes(node);
|
|
||||||
// 获取所有与当前节点相交的节点中,类型为 loop 的节点且它不能是折叠状态
|
|
||||||
const parentNode = intersections.find(
|
|
||||||
(item) => !item.data.isFolded && item.type === FlowNodeTypeEnum.loop
|
|
||||||
);
|
|
||||||
|
|
||||||
const unSupportedTypes = [
|
const unSupportedTypes = [
|
||||||
FlowNodeTypeEnum.workflowStart,
|
FlowNodeTypeEnum.workflowStart,
|
||||||
FlowNodeTypeEnum.loop,
|
FlowNodeTypeEnum.loop,
|
||||||
@@ -409,7 +386,16 @@ export const useWorkflow = () => {
|
|||||||
FlowNodeTypeEnum.systemConfig
|
FlowNodeTypeEnum.systemConfig
|
||||||
];
|
];
|
||||||
|
|
||||||
if (parentNode && !node.data.parentNodeId) {
|
if (!node || node.data.parentNodeId) return;
|
||||||
|
|
||||||
|
// 获取所有与当前节点相交的节点
|
||||||
|
const intersections = getIntersectingNodes(node);
|
||||||
|
// 获取所有与当前节点相交的节点中,类型为 loop 的节点且它不能是折叠状态
|
||||||
|
const parentNode = intersections.find(
|
||||||
|
(item) => !item.data.isFolded && item.type === FlowNodeTypeEnum.loop
|
||||||
|
);
|
||||||
|
|
||||||
|
if (parentNode) {
|
||||||
if (unSupportedTypes.includes(node.type as FlowNodeTypeEnum)) {
|
if (unSupportedTypes.includes(node.type as FlowNodeTypeEnum)) {
|
||||||
return toast({
|
return toast({
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
@@ -427,8 +413,6 @@ export const useWorkflow = () => {
|
|||||||
setEdges((state) =>
|
setEdges((state) =>
|
||||||
state.filter((edge) => edge.source !== node.id && edge.target !== node.id)
|
state.filter((edge) => edge.source !== node.id && edge.target !== node.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
resetParentNodeSizeAndPosition(parentNode.id);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { useWorkflow } from './hooks/useWorkflow';
|
|||||||
import HelperLines from './components/HelperLines';
|
import HelperLines from './components/HelperLines';
|
||||||
import FlowController from './components/FlowController';
|
import FlowController from './components/FlowController';
|
||||||
import ContextMenu from './components/ContextMenu';
|
import ContextMenu from './components/ContextMenu';
|
||||||
import { WorkflowActionContext, WorkflowInitContext } from '../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext, WorkflowInitContext } from '../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../context/workflowEventContext';
|
import { WorkflowEventContext } from '../context/workflowEventContext';
|
||||||
|
|
||||||
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
|
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
|
||||||
@@ -66,7 +66,7 @@ const edgeTypes = {
|
|||||||
|
|
||||||
const Workflow = () => {
|
const Workflow = () => {
|
||||||
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const reactFlowWrapper = useContextSelector(WorkflowEventContext, (v) => v.reactFlowWrapper);
|
const reactFlowWrapper = useContextSelector(WorkflowEventContext, (v) => v.reactFlowWrapper);
|
||||||
const workflowControlMode = useContextSelector(
|
const workflowControlMode = useContextSelector(
|
||||||
WorkflowEventContext,
|
WorkflowEventContext,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import React, { useEffect, useMemo, useRef, useCallback } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
import { Background, NodeProps } from 'reactflow';
|
import { Background, NodePositionChange, NodeProps } from 'reactflow';
|
||||||
import NodeCard from '../render/NodeCard';
|
import NodeCard from '../render/NodeCard';
|
||||||
import Container from '../../components/Container';
|
import Container from '../../components/Container';
|
||||||
import IOTitle from '../../components/IOTitle';
|
import IOTitle from '../../components/IOTitle';
|
||||||
@@ -26,18 +26,27 @@ import { useContextSelector } from 'use-context-selector';
|
|||||||
import { WorkflowContext } from '../../../context';
|
import { WorkflowContext } from '../../../context';
|
||||||
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 { isValidArrayReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
import { ReferenceArrayValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||||
import { useWorkflow } from '../../hooks/useWorkflow';
|
import { useWorkflow } from '../../hooks/useWorkflow';
|
||||||
|
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
|
||||||
|
import { useSize } from 'ahooks';
|
||||||
|
|
||||||
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 getNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.getNodes);
|
||||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (v) => v.onNodesChange);
|
||||||
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
|
|
||||||
const { resetParentNodeSizeAndPosition } = useWorkflow();
|
const { resetParentNodeSizeAndPosition } = useWorkflow();
|
||||||
|
|
||||||
const loopInputArray = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray);
|
const loopInputArray = useMemo(
|
||||||
|
() => inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray),
|
||||||
|
[inputs]
|
||||||
|
);
|
||||||
|
|
||||||
const { nodeWidth, nodeHeight } = useMemo(() => {
|
const { nodeWidth, nodeHeight } = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
@@ -46,23 +55,28 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
};
|
};
|
||||||
}, [inputs]);
|
}, [inputs]);
|
||||||
|
|
||||||
const childrenNodeIdList = useMemo(() => {
|
// Update array input type
|
||||||
return JSON.stringify(
|
// Computed the reference value type
|
||||||
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
|
|
||||||
);
|
|
||||||
}, [nodeId, nodeList]);
|
|
||||||
|
|
||||||
// Detect and update array input type
|
|
||||||
const newValueType = useMemo(() => {
|
const newValueType = useMemo(() => {
|
||||||
if (!loopInputArray) return WorkflowIOValueTypeEnum.arrayAny;
|
if (!loopInputArray) return WorkflowIOValueTypeEnum.arrayAny;
|
||||||
|
const value = loopInputArray.value as ReferenceArrayValueType;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!value ||
|
||||||
|
value.length === 0 ||
|
||||||
|
!isValidArrayReferenceValue(
|
||||||
|
value,
|
||||||
|
nodeList.map((node) => node.nodeId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return WorkflowIOValueTypeEnum.arrayAny;
|
||||||
|
|
||||||
const nodeIds = nodeList.map((node) => node.nodeId);
|
|
||||||
const globalVariables = getWorkflowGlobalVariables({
|
const globalVariables = getWorkflowGlobalVariables({
|
||||||
nodes: nodeList,
|
nodes: nodeList,
|
||||||
chatConfig: appDetail.chatConfig
|
chatConfig: appDetail.chatConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
const getValueType = (value: ReferenceItemValueType) => {
|
const valueType = ((value) => {
|
||||||
if (value?.[0] === VARIABLE_NODE_ID) {
|
if (value?.[0] === VARIABLE_NODE_ID) {
|
||||||
return globalVariables.find((item) => item.key === value[1])?.valueType;
|
return globalVariables.find((item) => item.key === value[1])?.valueType;
|
||||||
} else {
|
} else {
|
||||||
@@ -70,21 +84,8 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
const output = node?.outputs.find((output) => output.id === value?.[1]);
|
const output = node?.outputs.find((output) => output.id === value?.[1]);
|
||||||
return output?.valueType;
|
return output?.valueType;
|
||||||
}
|
}
|
||||||
};
|
})(value[0]);
|
||||||
|
return ArrayTypeMap[valueType as keyof typeof ArrayTypeMap] ?? WorkflowIOValueTypeEnum.arrayAny;
|
||||||
const valueType = (() => {
|
|
||||||
if (isReferenceValue(loopInputArray?.value, nodeIds)) {
|
|
||||||
return getValueType(loopInputArray?.value);
|
|
||||||
} else if (isReferenceValueArray(loopInputArray?.value, nodeIds)) {
|
|
||||||
return getValueType(loopInputArray?.value?.[0]);
|
|
||||||
} else {
|
|
||||||
return WorkflowIOValueTypeEnum.arrayAny;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const type = ArrayTypeMap[valueType as keyof typeof ArrayTypeMap];
|
|
||||||
|
|
||||||
return type ?? WorkflowIOValueTypeEnum.arrayAny;
|
|
||||||
}, [appDetail.chatConfig, loopInputArray, nodeList]);
|
}, [appDetail.chatConfig, loopInputArray, nodeList]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -101,6 +102,11 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
}, [newValueType]);
|
}, [newValueType]);
|
||||||
|
|
||||||
// Update childrenNodeIdList
|
// Update childrenNodeIdList
|
||||||
|
const childrenNodeIdList = useMemo(() => {
|
||||||
|
return JSON.stringify(
|
||||||
|
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
|
||||||
|
);
|
||||||
|
}, [nodeId, nodeList]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -111,20 +117,47 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
value: JSON.parse(childrenNodeIdList)
|
value: JSON.parse(childrenNodeIdList)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [childrenNodeIdList, nodeId, onChangeNode]);
|
resetParentNodeSizeAndPosition(nodeId);
|
||||||
|
}, [childrenNodeIdList]);
|
||||||
|
|
||||||
|
// Update child node position
|
||||||
|
const inputBoxRef = useRef<HTMLDivElement>(null);
|
||||||
|
const size = useSize(inputBoxRef);
|
||||||
|
const prevHeightRef = useRef<number>(); // 添加 ref 来存储前一个高度值
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
if (!size?.height) return;
|
||||||
resetParentNodeSizeAndPosition(nodeId);
|
if (prevHeightRef.current === size.height) return;
|
||||||
}, 0);
|
const diffHeight = prevHeightRef.current ? size.height - prevHeightRef.current : 0;
|
||||||
}, [loopInputArray, nodeId, resetParentNodeSizeAndPosition]);
|
prevHeightRef.current = size.height;
|
||||||
|
|
||||||
|
if (diffHeight === 0) return;
|
||||||
|
|
||||||
|
// Get the height of the input box
|
||||||
|
// Computed input
|
||||||
|
const nodes = getNodes();
|
||||||
|
const childNodes = nodes.filter((n) => n.data.parentNodeId === nodeId);
|
||||||
|
|
||||||
|
const childNodesChange: NodePositionChange[] = childNodes.map((node) => {
|
||||||
|
return {
|
||||||
|
type: 'position',
|
||||||
|
id: node.id,
|
||||||
|
position: {
|
||||||
|
x: node.position.x,
|
||||||
|
y: node.position.y + diffHeight
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
console.log(childNodesChange);
|
||||||
|
|
||||||
|
onNodesChange(childNodesChange);
|
||||||
|
}, [size?.height]);
|
||||||
|
|
||||||
const Render = useMemo(() => {
|
const Render = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}>
|
<NodeCard selected={selected} maxW="full" 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'} ref={inputBoxRef}>
|
||||||
<RenderInput nodeId={nodeId} flowInputList={inputs} />
|
<RenderInput nodeId={nodeId} flowInputList={inputs} />
|
||||||
</Box>
|
</Box>
|
||||||
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ const VariableSelector = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback(
|
||||||
(e: ReferenceItemValueType) => {
|
(e?: ReferenceItemValueType) => {
|
||||||
if (!e) return;
|
if (!e) return;
|
||||||
|
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
|||||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { getEditorVariables } from '../../../utils';
|
import { getEditorVariables } from '../../../utils';
|
||||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||||
import { WorkflowActionContext } from '../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
|
||||||
const CurlImportModal = dynamic(() => import('./CurlImportModal'));
|
const CurlImportModal = dynamic(() => import('./CurlImportModal'));
|
||||||
|
|
||||||
const defaultFormBody = {
|
const defaultFormBody = {
|
||||||
@@ -82,7 +82,7 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||||
@@ -259,7 +259,7 @@ export function RenderHttpProps({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedTab, setSelectedTab] = useState(TabEnum.params);
|
const [selectedTab, setSelectedTab] = useState(TabEnum.params);
|
||||||
|
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
|
|
||||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ const VariableSelector = ({
|
|||||||
}: {
|
}: {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
variable?: ReferenceItemValueType;
|
variable?: ReferenceItemValueType;
|
||||||
onSelect: (e: ReferenceItemValueType) => void;
|
onSelect: (e?: ReferenceItemValueType) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ function Reference({
|
|||||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback(
|
||||||
(e: ReferenceValueType) => {
|
(e?: ReferenceValueType) => {
|
||||||
|
if (!e) return;
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
type: 'updateInput',
|
type: 'updateInput',
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
|||||||
import { useCreation, useMemoizedFn } from 'ahooks';
|
import { useCreation, useMemoizedFn } from 'ahooks';
|
||||||
import { getEditorVariables } from '../../utils';
|
import { getEditorVariables } from '../../utils';
|
||||||
import { isArray } from 'lodash';
|
import { isArray } from 'lodash';
|
||||||
import { WorkflowActionContext } from '../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
|
||||||
|
|
||||||
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
const { inputs = [], nodeId } = data;
|
const { inputs = [], nodeId } = data;
|
||||||
@@ -43,7 +43,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
|||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
|
|
||||||
const menuList = useRef([
|
const menuList = useRef([
|
||||||
{
|
{
|
||||||
@@ -104,14 +104,14 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
|||||||
(item) => item.renderType === updateItem.renderType
|
(item) => item.renderType === updateItem.renderType
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpdate = (newValue: ReferenceValueType | string) => {
|
const handleUpdate = (newValue?: ReferenceValueType | string) => {
|
||||||
if (typeof newValue === 'string') {
|
if (typeof newValue === 'string') {
|
||||||
onUpdateList(
|
onUpdateList(
|
||||||
updateList.map((update, i) =>
|
updateList.map((update, i) =>
|
||||||
i === index ? { ...update, value: ['', newValue] } : update
|
i === index ? { ...update, value: ['', newValue] } : update
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else if (newValue) {
|
||||||
onUpdateList(
|
onUpdateList(
|
||||||
updateList.map((update, i) =>
|
updateList.map((update, i) =>
|
||||||
i === index ? { ...update, value: newValue as ReferenceItemValueType } : update
|
i === index ? { ...update, value: newValue as ReferenceItemValueType } : update
|
||||||
@@ -181,7 +181,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
|||||||
if (i === index) {
|
if (i === index) {
|
||||||
return {
|
return {
|
||||||
...update,
|
...update,
|
||||||
value: ['', ''],
|
value: undefined,
|
||||||
renderType:
|
renderType:
|
||||||
updateItem.renderType === FlowNodeInputTypeEnum.input
|
updateItem.renderType === FlowNodeInputTypeEnum.input
|
||||||
? FlowNodeInputTypeEnum.reference
|
? FlowNodeInputTypeEnum.reference
|
||||||
@@ -318,7 +318,7 @@ const VariableSelector = ({
|
|||||||
nodeId: string;
|
nodeId: string;
|
||||||
variable?: ReferenceValueType;
|
variable?: ReferenceValueType;
|
||||||
valueType?: WorkflowIOValueTypeEnum;
|
valueType?: WorkflowIOValueTypeEnum;
|
||||||
onSelect: (e: ReferenceValueType) => void;
|
onSelect: (e?: ReferenceValueType) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
|||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../../../context';
|
import { WorkflowContext } from '../../../../context';
|
||||||
import { WorkflowActionContext } from '../../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../../context/workflowInitContext';
|
||||||
|
|
||||||
export const ConnectionSourceHandle = ({
|
export const ConnectionSourceHandle = ({
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -14,7 +14,7 @@ export const ConnectionSourceHandle = ({
|
|||||||
nodeId: string;
|
nodeId: string;
|
||||||
isFoldNode?: boolean;
|
isFoldNode?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const { connectingEdge, nodeList } = useContextSelector(WorkflowContext, (ctx) => ctx);
|
const { connectingEdge, nodeList } = useContextSelector(WorkflowContext, (ctx) => ctx);
|
||||||
|
|
||||||
const { showSourceHandle, RightHandle, LeftHandlee, TopHandlee, BottomHandlee } = useMemo(() => {
|
const { showSourceHandle, RightHandle, LeftHandlee, TopHandlee, BottomHandlee } = useMemo(() => {
|
||||||
@@ -137,7 +137,7 @@ export const ConnectionTargetHandle = React.memo(function ConnectionTargetHandle
|
|||||||
}: {
|
}: {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
}) {
|
}) {
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const { connectingEdge, nodeList } = useContextSelector(WorkflowContext, (ctx) => ctx);
|
const { connectingEdge, nodeList } = useContextSelector(WorkflowContext, (ctx) => ctx);
|
||||||
|
|
||||||
const { LeftHandle, rightHandle, topHandle, bottomHandle } = useMemo(() => {
|
const { LeftHandle, rightHandle, topHandle, bottomHandle } = useMemo(() => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Connection, Handle, Position } from 'reactflow';
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
|
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
|
||||||
import { WorkflowActionContext } from '../../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../../context/workflowInitContext';
|
||||||
|
|
||||||
const handleSize = '16px';
|
const handleSize = '16px';
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ type ToolHandleProps = BoxProps & {
|
|||||||
export const ToolTargetHandle = ({ show, nodeId }: ToolHandleProps) => {
|
export const ToolTargetHandle = ({ show, nodeId }: ToolHandleProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
|
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
|
|
||||||
const handleId = NodeOutputKeyEnum.selectedTools;
|
const handleId = NodeOutputKeyEnum.selectedTools;
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export const ToolTargetHandle = ({ show, nodeId }: ToolHandleProps) => {
|
|||||||
|
|
||||||
export const ToolSourceHandle = () => {
|
export const ToolSourceHandle = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const setEdges = useContextSelector(WorkflowActionContext, (v) => v.setEdges);
|
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges);
|
||||||
|
|
||||||
/* onConnect edge, delete tool input and switch */
|
/* onConnect edge, delete tool input and switch */
|
||||||
const onConnect = useCallback(
|
const onConnect = useCallback(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useContextSelector } from 'use-context-selector';
|
|||||||
import { WorkflowContext } from '../../../../context';
|
import { WorkflowContext } from '../../../../context';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import {
|
import {
|
||||||
WorkflowActionContext,
|
WorkflowNodeEdgeContext,
|
||||||
WorkflowInitContext
|
WorkflowInitContext
|
||||||
} from '../../../../context/workflowInitContext';
|
} from '../../../../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../../../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../../../context/workflowEventContext';
|
||||||
@@ -29,7 +29,7 @@ const MySourceHandle = React.memo(function MySourceHandle({
|
|||||||
highlightStyle: Record<string, any>;
|
highlightStyle: Record<string, any>;
|
||||||
connectedStyle: Record<string, any>;
|
connectedStyle: Record<string, any>;
|
||||||
}) {
|
}) {
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
|
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
|
||||||
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
|
||||||
const hoverNodeId = useContextSelector(WorkflowEventContext, (v) => v.hoverNodeId);
|
const hoverNodeId = useContextSelector(WorkflowEventContext, (v) => v.hoverNodeId);
|
||||||
@@ -154,7 +154,7 @@ const MyTargetHandle = React.memo(function MyTargetHandle({
|
|||||||
highlightStyle: Record<string, any>;
|
highlightStyle: Record<string, any>;
|
||||||
connectedStyle: Record<string, any>;
|
connectedStyle: Record<string, any>;
|
||||||
}) {
|
}) {
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
|
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
|
||||||
|
|
||||||
const connected = edges.some((edge) => edge.targetHandle === handleId);
|
const connected = edges.some((edge) => edge.targetHandle === handleId);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||||||
import { useWorkflowUtils } from '../../hooks/useUtils';
|
import { useWorkflowUtils } from '../../hooks/useUtils';
|
||||||
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
|
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
|
||||||
import { getDocPath } from '@/web/common/system/doc';
|
import { getDocPath } from '@/web/common/system/doc';
|
||||||
import { WorkflowActionContext } from '../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../../context/workflowEventContext';
|
||||||
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
||||||
|
|
||||||
@@ -394,8 +394,8 @@ const MenuRender = React.memo(function MenuRender({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { openDebugNode, DebugInputModal } = useDebug();
|
const { openDebugNode, DebugInputModal } = useDebug();
|
||||||
|
|
||||||
const setNodes = useContextSelector(WorkflowActionContext, (v) => v.setNodes);
|
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||||
const setEdges = useContextSelector(WorkflowActionContext, (v) => v.setEdges);
|
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges);
|
||||||
const { computedNewNodeName } = useWorkflowUtils();
|
const { computedNewNodeName } = useWorkflowUtils();
|
||||||
|
|
||||||
const onCopyNode = useCallback(
|
const onCopyNode = useCallback(
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ function Reference({
|
|||||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback(
|
||||||
(e: ReferenceValueType) => {
|
(e?: ReferenceValueType) => {
|
||||||
|
if (!e) return;
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
type: 'replaceInput',
|
type: 'replaceInput',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { useContextSelector } from 'use-context-selector';
|
|||||||
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
|
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { AppContext } from '@/pages/app/detail/components/context';
|
import { AppContext } from '@/pages/app/detail/components/context';
|
||||||
import { WorkflowActionContext } from '../../../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../../../context/workflowInitContext';
|
||||||
|
|
||||||
const MultipleRowSelect = dynamic(() =>
|
const MultipleRowSelect = dynamic(() =>
|
||||||
import('@fastgpt/web/components/common/MySelect/MultipleRowSelect').then(
|
import('@fastgpt/web/components/common/MySelect/MultipleRowSelect').then(
|
||||||
@@ -64,7 +64,7 @@ export const useReference = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
|
|
||||||
// 获取可选的变量列表
|
// 获取可选的变量列表
|
||||||
@@ -119,7 +119,7 @@ const Reference = ({ item, nodeId }: RenderInputProps) => {
|
|||||||
const isArray = item.valueType?.includes('array') ?? false;
|
const isArray = item.valueType?.includes('array') ?? false;
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback(
|
||||||
(e: ReferenceValueType) => {
|
(e?: ReferenceValueType) => {
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
type: 'updateInput',
|
type: 'updateInput',
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponent
|
|||||||
import { useCreation } from 'ahooks';
|
import { useCreation } from 'ahooks';
|
||||||
import { AppContext } from '@/pages/app/detail/components/context';
|
import { AppContext } from '@/pages/app/detail/components/context';
|
||||||
import { getEditorVariables } from '../../../../../utils';
|
import { getEditorVariables } from '../../../../../utils';
|
||||||
import { WorkflowActionContext } from '../../../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../../../context/workflowInitContext';
|
||||||
|
|
||||||
const TextInputRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
|
const TextInputRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponent
|
|||||||
import { useCreation } from 'ahooks';
|
import { useCreation } from 'ahooks';
|
||||||
import { AppContext } from '@/pages/app/detail/components/context';
|
import { AppContext } from '@/pages/app/detail/components/context';
|
||||||
import { getEditorVariables } from '../../../../../utils';
|
import { getEditorVariables } from '../../../../../utils';
|
||||||
import { WorkflowActionContext } from '../../../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../../../context/workflowInitContext';
|
||||||
|
|
||||||
const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
|
const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const edges = useContextSelector(WorkflowActionContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
|
|
||||||
|
|||||||
@@ -39,9 +39,16 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import { formatTime2YMDHMS, formatTime2YMDHMW } from '@fastgpt/global/common/string/time';
|
import { formatTime2YMDHMS, formatTime2YMDHMW } from '@fastgpt/global/common/string/time';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||||
import WorkflowInitContextProvider, { WorkflowActionContext } from './workflowInitContext';
|
import WorkflowInitContextProvider, { WorkflowNodeEdgeContext } from './workflowInitContext';
|
||||||
import WorkflowEventContextProvider from './workflowEventContext';
|
import WorkflowEventContextProvider from './workflowEventContext';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Context
|
||||||
|
1. WorkflowInitContext: 带 nodes
|
||||||
|
2. WorkflowNodeEdgeContext: 除了 nodes 外的,nodes 操作。以及 edges 和其操作
|
||||||
|
3. WorkflowContextProvider: 旧的 context,未拆分
|
||||||
|
4. WorkflowEventContextProvider:一些边缘的 event
|
||||||
|
*/
|
||||||
export const ReactFlowCustomProvider = ({
|
export const ReactFlowCustomProvider = ({
|
||||||
templates,
|
templates,
|
||||||
children
|
children
|
||||||
@@ -322,8 +329,8 @@ const WorkflowContextProvider = ({
|
|||||||
const appId = appDetail._id;
|
const appId = appDetail._id;
|
||||||
|
|
||||||
/* edge */
|
/* edge */
|
||||||
const edges = useContextSelector(WorkflowActionContext, (state) => state.edges);
|
const edges = useContextSelector(WorkflowNodeEdgeContext, (state) => state.edges);
|
||||||
const setEdges = useContextSelector(WorkflowActionContext, (state) => state.setEdges);
|
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (state) => state.setEdges);
|
||||||
const onDelEdge = useCallback(
|
const onDelEdge = useCallback(
|
||||||
({
|
({
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -351,9 +358,12 @@ const WorkflowContextProvider = ({
|
|||||||
const [connectingEdge, setConnectingEdge] = useState<OnConnectStartParams>();
|
const [connectingEdge, setConnectingEdge] = useState<OnConnectStartParams>();
|
||||||
|
|
||||||
/* node */
|
/* node */
|
||||||
const setNodes = useContextSelector(WorkflowActionContext, (state) => state.setNodes);
|
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (state) => state.setNodes);
|
||||||
const getNodes = useContextSelector(WorkflowActionContext, (state) => state.getNodes);
|
const getNodes = useContextSelector(WorkflowNodeEdgeContext, (state) => state.getNodes);
|
||||||
const nodeListString = useContextSelector(WorkflowActionContext, (state) => state.nodeListString);
|
const nodeListString = useContextSelector(
|
||||||
|
WorkflowNodeEdgeContext,
|
||||||
|
(state) => state.nodeListString
|
||||||
|
);
|
||||||
|
|
||||||
const nodeList = useMemo(
|
const nodeList = useMemo(
|
||||||
() => JSON.parse(nodeListString) as FlowNodeItemType[],
|
() => JSON.parse(nodeListString) as FlowNodeItemType[],
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ type WorkflowActionContextType = {
|
|||||||
setEdges: Dispatch<SetStateAction<Edge<any>[]>>;
|
setEdges: Dispatch<SetStateAction<Edge<any>[]>>;
|
||||||
onEdgesChange: OnChange<EdgeChange>;
|
onEdgesChange: OnChange<EdgeChange>;
|
||||||
};
|
};
|
||||||
export const WorkflowActionContext = createContext<WorkflowActionContextType>({
|
export const WorkflowNodeEdgeContext = createContext<WorkflowActionContextType>({
|
||||||
setNodes: function (
|
setNodes: function (
|
||||||
value: React.SetStateAction<Node<FlowNodeItemType, string | undefined>[]>
|
value: React.SetStateAction<Node<FlowNodeItemType, string | undefined>[]>
|
||||||
): void {
|
): void {
|
||||||
@@ -128,9 +128,9 @@ const WorkflowInitContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
nodes
|
nodes
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WorkflowActionContext.Provider value={actionContextValue}>
|
<WorkflowNodeEdgeContext.Provider value={actionContextValue}>
|
||||||
{children}
|
{children}
|
||||||
</WorkflowActionContext.Provider>
|
</WorkflowNodeEdgeContext.Provider>
|
||||||
</WorkflowInitContext.Provider>
|
</WorkflowInitContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ import { EmptyNode } from '@fastgpt/global/core/workflow/template/system/emptyNo
|
|||||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
import { getGlobalVariableNode } from './adapt';
|
import { getGlobalVariableNode } from './adapt';
|
||||||
import { VARIABLE_NODE_ID, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||||
import {
|
import {
|
||||||
formatEditorVariablePickerIcon,
|
formatEditorVariablePickerIcon,
|
||||||
getAppChatConfig,
|
getAppChatConfig,
|
||||||
getGuideModule,
|
getGuideModule,
|
||||||
isReferenceValue,
|
isValidArrayReferenceValue,
|
||||||
isReferenceValueFormat
|
isValidReferenceValue
|
||||||
} 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,17 +263,72 @@ export const getRefData = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 根据数据类型,过滤无效的节点输出
|
||||||
export const filterWorkflowNodeOutputsByType = (
|
export const filterWorkflowNodeOutputsByType = (
|
||||||
outputs: FlowNodeOutputItemType[],
|
outputs: FlowNodeOutputItemType[],
|
||||||
valueType: WorkflowIOValueTypeEnum
|
valueType: WorkflowIOValueTypeEnum
|
||||||
): FlowNodeOutputItemType[] => {
|
): FlowNodeOutputItemType[] => {
|
||||||
|
const validTypeMap: Record<WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum[]> = {
|
||||||
|
[WorkflowIOValueTypeEnum.string]: [WorkflowIOValueTypeEnum.string],
|
||||||
|
[WorkflowIOValueTypeEnum.number]: [WorkflowIOValueTypeEnum.number],
|
||||||
|
[WorkflowIOValueTypeEnum.boolean]: [WorkflowIOValueTypeEnum.boolean],
|
||||||
|
[WorkflowIOValueTypeEnum.object]: [WorkflowIOValueTypeEnum.object],
|
||||||
|
[WorkflowIOValueTypeEnum.arrayString]: [
|
||||||
|
WorkflowIOValueTypeEnum.string,
|
||||||
|
WorkflowIOValueTypeEnum.arrayString,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.arrayNumber]: [
|
||||||
|
WorkflowIOValueTypeEnum.number,
|
||||||
|
WorkflowIOValueTypeEnum.arrayNumber,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.arrayBoolean]: [
|
||||||
|
WorkflowIOValueTypeEnum.boolean,
|
||||||
|
WorkflowIOValueTypeEnum.arrayBoolean,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.arrayObject]: [
|
||||||
|
WorkflowIOValueTypeEnum.object,
|
||||||
|
WorkflowIOValueTypeEnum.arrayObject,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny,
|
||||||
|
WorkflowIOValueTypeEnum.chatHistory,
|
||||||
|
WorkflowIOValueTypeEnum.datasetQuote,
|
||||||
|
WorkflowIOValueTypeEnum.dynamic,
|
||||||
|
WorkflowIOValueTypeEnum.selectDataset,
|
||||||
|
WorkflowIOValueTypeEnum.selectApp
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.chatHistory]: [
|
||||||
|
WorkflowIOValueTypeEnum.chatHistory,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.datasetQuote]: [
|
||||||
|
WorkflowIOValueTypeEnum.datasetQuote,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.dynamic]: [
|
||||||
|
WorkflowIOValueTypeEnum.dynamic,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.selectDataset]: [
|
||||||
|
WorkflowIOValueTypeEnum.selectDataset,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.selectApp]: [
|
||||||
|
WorkflowIOValueTypeEnum.selectApp,
|
||||||
|
WorkflowIOValueTypeEnum.arrayAny
|
||||||
|
],
|
||||||
|
[WorkflowIOValueTypeEnum.arrayAny]: [WorkflowIOValueTypeEnum.arrayAny],
|
||||||
|
[WorkflowIOValueTypeEnum.any]: [WorkflowIOValueTypeEnum.arrayAny]
|
||||||
|
};
|
||||||
|
|
||||||
return outputs.filter(
|
return outputs.filter(
|
||||||
(output) =>
|
(output) =>
|
||||||
valueType === WorkflowIOValueTypeEnum.any ||
|
valueType === WorkflowIOValueTypeEnum.any ||
|
||||||
valueType === WorkflowIOValueTypeEnum.arrayAny ||
|
valueType === WorkflowIOValueTypeEnum.arrayAny ||
|
||||||
|
!output.valueType ||
|
||||||
output.valueType === WorkflowIOValueTypeEnum.any ||
|
output.valueType === WorkflowIOValueTypeEnum.any ||
|
||||||
output.valueType === valueType ||
|
validTypeMap[valueType].includes(output.valueType)
|
||||||
valueType?.replace('array', '').toLowerCase() === output.valueType
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -341,46 +396,25 @@ export const checkWorkflowNodeAndConnection = ({
|
|||||||
if (input.value === undefined) return true;
|
if (input.value === undefined) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
// Check plugin output
|
||||||
node.data.flowNodeType === FlowNodeTypeEnum.pluginOutput &&
|
// if (
|
||||||
(input.value?.length === 0 ||
|
// node.data.flowNodeType === FlowNodeTypeEnum.pluginOutput &&
|
||||||
(isReferenceValue(input.value, nodeIds) && !input.value?.[1]))
|
// (input.value?.length === 0 ||
|
||||||
) {
|
// (isValidReferenceValue(input.value, nodeIds) && !input.value?.[1]))
|
||||||
return true;
|
// ) {
|
||||||
}
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
// check reference invalid
|
// check reference invalid
|
||||||
const renderType = input.renderTypeList[input.selectedTypeIndex || 0];
|
const renderType = input.renderTypeList[input.selectedTypeIndex || 0];
|
||||||
if (renderType === FlowNodeInputTypeEnum.reference) {
|
if (renderType === FlowNodeInputTypeEnum.reference) {
|
||||||
const checkReference = (value: [string, string]) => {
|
if (input.valueType?.startsWith('array')) {
|
||||||
if (value[0] === VARIABLE_NODE_ID) {
|
if (input.required && (!input.value || input.value.length === 0)) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourceNode = nodes.find((item) => item.data.nodeId === value[0]);
|
|
||||||
if (!sourceNode) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return isValidArrayReferenceValue(input.value, nodeIds);
|
||||||
const sourceOutput = filterWorkflowNodeOutputsByType(
|
|
||||||
sourceNode.data.outputs,
|
|
||||||
input.valueType as WorkflowIOValueTypeEnum
|
|
||||||
).find((item) => item.id === value[1]);
|
|
||||||
return !sourceOutput;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Old format
|
|
||||||
if (isReferenceValueFormat(input.value)) {
|
|
||||||
return input.required && checkReference(input.value);
|
|
||||||
}
|
}
|
||||||
|
return input.required && !isValidReferenceValue(input.value, nodeIds);
|
||||||
// New format
|
|
||||||
return input.value.some((inputItem: ReferenceItemValueType) => {
|
|
||||||
if (!Array.isArray(inputItem) || inputItem.length !== 2) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return checkReference(inputItem as [string, string]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user