Co-authored-by: crazywoola <427733928@qq.com>tags/1.4.0
| @@ -31,6 +31,7 @@ type NodesExtraData = { | |||
| getAvailablePrevNodes: (isChatMode: boolean) => BlockEnum[] | |||
| getAvailableNextNodes: (isChatMode: boolean) => BlockEnum[] | |||
| checkValid: any | |||
| checkVarValid?: any | |||
| } | |||
| export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| [BlockEnum.Start]: { | |||
| @@ -59,6 +60,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: AnswerDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: AnswerDefault.getAvailableNextNodes, | |||
| checkValid: AnswerDefault.checkValid, | |||
| checkVarValid: AnswerDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.LLM]: { | |||
| author: 'Dify', | |||
| @@ -68,6 +70,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: LLMDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: LLMDefault.getAvailableNextNodes, | |||
| checkValid: LLMDefault.checkValid, | |||
| checkVarValid: LLMDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.KnowledgeRetrieval]: { | |||
| author: 'Dify', | |||
| @@ -77,6 +80,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: KnowledgeRetrievalDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: KnowledgeRetrievalDefault.getAvailableNextNodes, | |||
| checkValid: KnowledgeRetrievalDefault.checkValid, | |||
| checkVarValid: KnowledgeRetrievalDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.IfElse]: { | |||
| author: 'Dify', | |||
| @@ -86,6 +90,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: IfElseDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: IfElseDefault.getAvailableNextNodes, | |||
| checkValid: IfElseDefault.checkValid, | |||
| checkVarValid: IfElseDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.Iteration]: { | |||
| author: 'Dify', | |||
| @@ -95,6 +100,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: IterationDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: IterationDefault.getAvailableNextNodes, | |||
| checkValid: IterationDefault.checkValid, | |||
| checkVarValid: IterationDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.IterationStart]: { | |||
| author: 'Dify', | |||
| @@ -140,6 +146,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: CodeDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: CodeDefault.getAvailableNextNodes, | |||
| checkValid: CodeDefault.checkValid, | |||
| checkVarValid: CodeDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.TemplateTransform]: { | |||
| author: 'Dify', | |||
| @@ -149,6 +156,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: TemplateTransformDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: TemplateTransformDefault.getAvailableNextNodes, | |||
| checkValid: TemplateTransformDefault.checkValid, | |||
| checkVarValid: TemplateTransformDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.QuestionClassifier]: { | |||
| author: 'Dify', | |||
| @@ -158,6 +166,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: QuestionClassifierDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: QuestionClassifierDefault.getAvailableNextNodes, | |||
| checkValid: QuestionClassifierDefault.checkValid, | |||
| checkVarValid: QuestionClassifierDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.HttpRequest]: { | |||
| author: 'Dify', | |||
| @@ -167,6 +176,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: HttpRequestDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: HttpRequestDefault.getAvailableNextNodes, | |||
| checkValid: HttpRequestDefault.checkValid, | |||
| checkVarValid: HttpRequestDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.VariableAssigner]: { | |||
| author: 'Dify', | |||
| @@ -185,6 +195,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: AssignerDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: AssignerDefault.getAvailableNextNodes, | |||
| checkValid: AssignerDefault.checkValid, | |||
| checkVarValid: AssignerDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.VariableAggregator]: { | |||
| author: 'Dify', | |||
| @@ -194,6 +205,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: VariableAssignerDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: VariableAssignerDefault.getAvailableNextNodes, | |||
| checkValid: VariableAssignerDefault.checkValid, | |||
| checkVarValid: VariableAssignerDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.ParameterExtractor]: { | |||
| author: 'Dify', | |||
| @@ -203,6 +215,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: ParameterExtractorDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: ParameterExtractorDefault.getAvailableNextNodes, | |||
| checkValid: ParameterExtractorDefault.checkValid, | |||
| checkVarValid: ParameterExtractorDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.Tool]: { | |||
| author: 'Dify', | |||
| @@ -212,6 +225,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: ToolDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: ToolDefault.getAvailableNextNodes, | |||
| checkValid: ToolDefault.checkValid, | |||
| checkVarValid: ToolDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.DocExtractor]: { | |||
| author: 'Dify', | |||
| @@ -221,6 +235,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: DocExtractorDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: DocExtractorDefault.getAvailableNextNodes, | |||
| checkValid: DocExtractorDefault.checkValid, | |||
| checkVarValid: DocExtractorDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.ListFilter]: { | |||
| author: 'Dify', | |||
| @@ -230,6 +245,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = { | |||
| getAvailablePrevNodes: ListFilterDefault.getAvailablePrevNodes, | |||
| getAvailableNextNodes: ListFilterDefault.getAvailableNextNodes, | |||
| checkValid: ListFilterDefault.checkValid, | |||
| checkVarValid: ListFilterDefault.checkVarValid, | |||
| }, | |||
| [BlockEnum.Agent]: { | |||
| author: 'Dify', | |||
| @@ -140,6 +140,16 @@ const WorkflowChecklist = ({ | |||
| </div> | |||
| ) | |||
| } | |||
| { | |||
| node.varErrorMessage?.map((errorMessage: string) => ( | |||
| <div className='rounded-b-lg bg-gray-25 px-3 py-2'> | |||
| <div className='flex text-xs leading-[18px] text-gray-500'> | |||
| <AlertTriangle className='mr-2 mt-[3px] h-3 w-3 text-[#F79009]' /> | |||
| {errorMessage} | |||
| </div> | |||
| </div> | |||
| )) | |||
| } | |||
| </div> | |||
| </div> | |||
| )) | |||
| @@ -15,6 +15,7 @@ import { useStore } from '../store' | |||
| import { | |||
| getToolCheckParams, | |||
| getValidTreeNodes, | |||
| transformStartNodeVariables, | |||
| } from '../utils' | |||
| import { | |||
| CUSTOM_NODE, | |||
| @@ -45,6 +46,9 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { | |||
| const { data: strategyProviders } = useStrategyProviders() | |||
| const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail) | |||
| const chatVarList = useStore(s => s.conversationVariables) | |||
| const environmentVariables = useStore(s => s.environmentVariables) | |||
| const getCheckData = useCallback((data: CommonNodeType<{}>) => { | |||
| let checkData = data | |||
| if (data.type === BlockEnum.KnowledgeRetrieval) { | |||
| @@ -64,7 +68,10 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { | |||
| const needWarningNodes = useMemo(() => { | |||
| const list = [] | |||
| const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges) | |||
| const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true) | |||
| const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables) | |||
| const errMessageMap = new Map() | |||
| for (let i = 0; i < nodes.length; i++) { | |||
| const node = nodes[i] | |||
| @@ -110,8 +117,32 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { | |||
| toolIcon, | |||
| unConnected: !validNodes.find(n => n.id === node.id), | |||
| errorMessage, | |||
| varErrorMessage: [], | |||
| }) | |||
| } | |||
| errMessageMap.set(node.id, list[list.length - 1]) | |||
| if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) { | |||
| const { errorMessage: varErrorMessages } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t) | |||
| if (varErrorMessages?.length) { | |||
| const errMessage = errMessageMap.get(node.id) | |||
| if (errMessage) { | |||
| errMessage.varErrorMessage = varErrorMessages | |||
| } | |||
| else { | |||
| list.push({ | |||
| id: node.id, | |||
| type: node.data.type, | |||
| title: node.data.title, | |||
| toolIcon, | |||
| unConnected: !validNodes.find(n => n.id === node.id), | |||
| errorMessage: '', | |||
| varErrorMessage: varErrorMessages, | |||
| }) | |||
| errMessageMap.set(node.id, list[list.length - 1]) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -133,8 +164,13 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { | |||
| }) | |||
| } | |||
| for (let i = 0; i < validNodes.length; i++) { | |||
| const node = validNodes[i] | |||
| delete node._parentOutputVarMap | |||
| } | |||
| return list | |||
| }, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData]) | |||
| }, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData, chatVarList, environmentVariables]) | |||
| return needWarningNodes | |||
| } | |||
| @@ -153,6 +189,9 @@ export const useChecklistBeforePublish = () => { | |||
| const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail) | |||
| const updateTime = useRef(0) | |||
| const chatVarList = useStore(s => s.conversationVariables) | |||
| const environmentVariables = useStore(s => s.environmentVariables) | |||
| const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => { | |||
| let checkData = data | |||
| if (data.type === BlockEnum.KnowledgeRetrieval) { | |||
| @@ -183,12 +222,15 @@ export const useChecklistBeforePublish = () => { | |||
| const { | |||
| validNodes, | |||
| maxDepth, | |||
| } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges) | |||
| } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true) | |||
| if (maxDepth > MAX_TREE_DEPTH) { | |||
| notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) }) | |||
| return false | |||
| } | |||
| const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables) | |||
| // Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed | |||
| const knowledgeRetrievalNodes = nodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval) | |||
| const allDatasetIds = knowledgeRetrievalNodes.reduce<string[]>((acc, node) => { | |||
| @@ -239,6 +281,14 @@ export const useChecklistBeforePublish = () => { | |||
| notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` }) | |||
| return false | |||
| } | |||
| if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) { | |||
| const { errorMessage: varErrorMessage } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t) | |||
| if (varErrorMessage?.length) { | |||
| notify({ type: 'error', message: `[${node.data.title}] ${varErrorMessage[0]}` }) | |||
| return false | |||
| } | |||
| } | |||
| } | |||
| if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) { | |||
| @@ -252,7 +302,7 @@ export const useChecklistBeforePublish = () => { | |||
| } | |||
| return true | |||
| }, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData]) | |||
| }, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, chatVarList, environmentVariables]) | |||
| return { | |||
| handleCheckBeforePublish, | |||
| @@ -164,7 +164,7 @@ const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: Val | |||
| return res | |||
| } | |||
| const formatItem = ( | |||
| export const formatItem = ( | |||
| item: any, | |||
| isChatMode: boolean, | |||
| filterVar: (payload: Var, selector: ValueSelector) => boolean, | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByText } from '../../utils/workflow' | |||
| import type { AnswerNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -31,6 +32,19 @@ const nodeDefault: NodeDefault<AnswerNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: AnswerNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const answer_warnings = getNotExistVariablesByText(payload.answer || '', varMap) | |||
| if (answer_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.answer.answer')} ${t('workflow.common.referenceVar')}${answer_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...answer_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import { type AssignerNodeType, WriteMode } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| const i18nPrefix = 'workflow.errorMsg' | |||
| @@ -47,6 +48,23 @@ const nodeDefault: NodeDefault<AssignerNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: AssignerNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr: string[] = [] | |||
| const variables_warnings = getNotExistVariablesByArray(payload.items.map(item => item.variable_selector ?? []) ?? [], varMap) | |||
| if (variables_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.assigner.assignedVariable')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| const value_warnings = getNotExistVariablesByArray(payload.items.map(item => item.value ?? []) ?? [], varMap) | |||
| if (value_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.assigner.setVariable')} ${t('workflow.common.referenceVar')}${value_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...variables_warnings, ...value_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import { CodeLanguage, type CodeNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -37,7 +38,20 @@ const nodeDefault: NodeDefault<CodeNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: CodeNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const variables_selector = payload.variables.map(v => v.value_selector) | |||
| const variables_selector_warnings = getNotExistVariablesByArray(variables_selector, varMap) | |||
| if (variables_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.code.inputVars')} ${t('workflow.common.referenceVar')}${variables_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: variables_selector_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import type { DocExtractorNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| const i18nPrefix = 'workflow.errorMsg' | |||
| @@ -31,6 +32,19 @@ const nodeDefault: NodeDefault<DocExtractorNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: DocExtractorNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr: string[] = [] | |||
| const variables_warnings = getNotExistVariablesByArray([payload.variable_selector], varMap) | |||
| if (variables_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.docExtractor.inputVar')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: variables_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow' | |||
| import { AuthorizationType, BodyType, Method } from './types' | |||
| import type { BodyPayload, HttpNodeType } from './types' | |||
| import { | |||
| @@ -50,8 +51,8 @@ const nodeDefault: NodeDefault<HttpNodeType> = { | |||
| errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.api') }) | |||
| if (!errorMessages | |||
| && payload.body.type === BodyType.binary | |||
| && ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0) | |||
| && payload.body.type === BodyType.binary | |||
| && ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0) | |||
| ) | |||
| errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.binaryFileVariable') }) | |||
| @@ -60,6 +61,53 @@ const nodeDefault: NodeDefault<HttpNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: HttpNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr: string[] = [] | |||
| const url_warnings = getNotExistVariablesByText(payload.url, varMap) | |||
| if (url_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.http.api')} ${t('workflow.common.referenceVar')}${url_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| const headers_warnings = getNotExistVariablesByText(payload.headers, varMap) | |||
| if (headers_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.http.headers')} ${t('workflow.common.referenceVar')}${headers_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| const params_warnings = getNotExistVariablesByText(payload.params, varMap) | |||
| if (params_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.http.params')} ${t('workflow.common.referenceVar')}${params_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| const body_warnings: string[] = [] | |||
| if ([BodyType.binary].includes(payload.body.type)) { | |||
| const body_data = payload.body.data as BodyPayload | |||
| body_data.forEach((item) => { | |||
| const key_warnings = getNotExistVariablesByText(item.key || '', varMap) | |||
| if (key_warnings.length) | |||
| body_warnings.push(...key_warnings) | |||
| const warnings = getNotExistVariablesByArray([item.file || []], varMap) | |||
| if (warnings.length) | |||
| body_warnings.push(...warnings) | |||
| }) | |||
| } | |||
| else { | |||
| const body_data = payload.body.data as BodyPayload | |||
| body_data.forEach((item) => { | |||
| const key_warnings = getNotExistVariablesByText(item.key || '', varMap) | |||
| if (key_warnings.length) | |||
| body_warnings.push(...key_warnings) | |||
| const value_warnings = getNotExistVariablesByText(item.value || '', varMap) | |||
| if (value_warnings.length) | |||
| body_warnings.push(...value_warnings) | |||
| }) | |||
| } | |||
| if (body_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.http.body')} ${t('workflow.common.referenceVar')}${body_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...url_warnings, ...headers_warnings, ...params_warnings, ...body_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,4 +1,6 @@ | |||
| import type { Var } from '../../types' | |||
| import { BlockEnum, type NodeDefault } from '../../types' | |||
| import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow' | |||
| import { type IfElseNodeType, LogicalOperator } from './types' | |||
| import { isEmptyRelatedOperator } from './utils' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -75,6 +77,41 @@ const nodeDefault: NodeDefault<IfElseNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: IfElseNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const condition_variable_selector_warnings: string[] = [] | |||
| const condition_value_warnings: string[] = [] | |||
| payload.cases.forEach((caseItem) => { | |||
| caseItem.conditions.forEach((condition) => { | |||
| if (!condition.variable_selector) | |||
| return | |||
| const selector_warnings = getNotExistVariablesByArray([condition.variable_selector], varMap) | |||
| if (selector_warnings.length) | |||
| condition_variable_selector_warnings.push(...selector_warnings) | |||
| const value_warnings = Array.isArray(condition.value) ? getNotExistVariablesByArray([condition.value], varMap) : getNotExistVariablesByText(condition.value, varMap) | |||
| if (value_warnings.length) | |||
| condition_value_warnings.push(...value_warnings) | |||
| condition.sub_variable_condition?.conditions.forEach((subCondition) => { | |||
| const sub_variable_value_warnings = Array.isArray(subCondition.value) ? getNotExistVariablesByArray([subCondition.value], varMap) : getNotExistVariablesByText(subCondition.value, varMap) | |||
| if (sub_variable_value_warnings.length) | |||
| condition_value_warnings.push(...sub_variable_value_warnings) | |||
| }) | |||
| }) | |||
| }) | |||
| if (condition_variable_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.ifElse.condition')} ${t('workflow.common.referenceVar')}${condition_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| if (condition_value_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.ifElse.enterValue')} ${t('workflow.common.referenceVar')}${condition_value_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: condition_variable_selector_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum, ErrorHandleMode } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import type { IterationNodeType } from './types' | |||
| import { | |||
| ALL_CHAT_AVAILABLE_BLOCKS, | |||
| @@ -58,6 +59,19 @@ const nodeDefault: NodeDefault<IterationNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: IterationNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr: string[] = [] | |||
| const iterator_selector_warnings = getNotExistVariablesByArray([payload.iterator_selector], varMap) | |||
| if (iterator_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.iteration.input')} ${t('workflow.common.referenceVar')}${iterator_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: iterator_selector_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import type { KnowledgeRetrievalNodeType } from './types' | |||
| import { checkoutRerankModelConfigedInRetrievalSettings } from './utils' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -52,6 +53,19 @@ const nodeDefault: NodeDefault<KnowledgeRetrievalNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: KnowledgeRetrievalNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const query_variable_selector_warnings = getNotExistVariablesByArray([payload.query_variable_selector], varMap) | |||
| if (query_variable_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.knowledgeRetrieval.queryVariable')} ${t('workflow.common.referenceVar')}${query_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...query_variable_selector_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum, VarType } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import { comparisonOperatorNotRequireValue } from '../if-else/utils' | |||
| import { type ListFilterNodeType, OrderBy } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -60,6 +61,18 @@ const nodeDefault: NodeDefault<ListFilterNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: ListFilterNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const variable_warnings = getNotExistVariablesByArray([payload.variable], varMap) | |||
| if (variable_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.listFilter.inputVar')} ${t('workflow.common.referenceVar')}${variable_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: variable_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,7 @@ | |||
| import { BlockEnum, EditionType } from '../../types' | |||
| import type { Var } from '../../types' | |||
| import { BlockEnum, EditionType, VarType } from '../../types' | |||
| import { type NodeDefault, type PromptItem, PromptRole } from '../../types' | |||
| import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow' | |||
| import type { LLMNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -86,6 +88,37 @@ const nodeDefault: NodeDefault<LLMNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: LLMNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const prompt_templates_warnings: string[] = [] | |||
| if (payload.context?.enabled && payload.context?.variable_selector?.length) { | |||
| const context_variable_selector_warnings = getNotExistVariablesByArray([payload.context.variable_selector], varMap) | |||
| if (context_variable_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.llm.context')} ${t('workflow.common.referenceVar')}${context_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| } | |||
| const prompt_templates = Array.isArray(payload.prompt_template) ? payload.prompt_template : [payload.prompt_template] as PromptItem[] | |||
| prompt_templates.forEach((v) => { | |||
| prompt_templates_warnings.push(...getNotExistVariablesByText(v.text, { context: { variable: 'context', type: VarType.string }, ...varMap })) | |||
| }) | |||
| if (prompt_templates_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.llm.prompt')} ${t('workflow.common.referenceVar')}${prompt_templates_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| const memory_query_prompt_template_warnings = getNotExistVariablesByText(payload.memory?.query_prompt_template || '', varMap) | |||
| if (memory_query_prompt_template_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.common.memories.title')}USER ${t('workflow.common.referenceVar')}${memory_query_prompt_template_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| if (payload.vision?.enabled && payload.vision?.configs?.variable_selector?.length) { | |||
| const vision_variable_selector_warnings = getNotExistVariablesByArray([payload.vision.configs.variable_selector], varMap) | |||
| if (vision_variable_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| } | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...prompt_templates_warnings, ...memory_query_prompt_template_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow' | |||
| import { type ParameterExtractorNodeType, ReasoningModeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| const i18nPrefix = 'workflow' | |||
| @@ -64,6 +65,30 @@ const nodeDefault: NodeDefault<ParameterExtractorNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: ParameterExtractorNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr: string[] = [] | |||
| const variables_warnings = getNotExistVariablesByArray([payload.query], varMap) | |||
| if (variables_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.parameterExtractor.inputVar')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| let vision_variable_warnings: string[] = [] | |||
| if (payload.vision?.configs?.variable_selector?.length) { | |||
| vision_variable_warnings = getNotExistVariablesByArray([payload.vision.configs.variable_selector], varMap) | |||
| if (vision_variable_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| } | |||
| const instruction_warnings = getNotExistVariablesByText(payload.instruction, varMap) | |||
| if (instruction_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.parameterExtractor.instruction')} ${t('workflow.common.referenceVar')}${instruction_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...variables_warnings, ...vision_variable_warnings, ...instruction_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { BlockEnum } from '../../types' | |||
| import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow' | |||
| import type { QuestionClassifierNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -71,6 +72,30 @@ const nodeDefault: NodeDefault<QuestionClassifierNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: QuestionClassifierNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const query_variable_selector_warnings = getNotExistVariablesByArray([payload.query_variable_selector], varMap) | |||
| if (query_variable_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.questionClassifiers.inputVars')} ${t('workflow.common.referenceVar')}${query_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| let vision_variable_selector_warnings: string[] = [] | |||
| if (payload.vision?.configs?.variable_selector?.length) { | |||
| vision_variable_selector_warnings = getNotExistVariablesByArray([payload.vision?.configs?.variable_selector], varMap) | |||
| if (vision_variable_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| } | |||
| const instruction_warnings: string[] = getNotExistVariablesByText(payload.instruction, varMap) | |||
| if (instruction_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.questionClassifiers.advancedSetting')}-${t('workflow.nodes.questionClassifiers.instruction')} ${t('workflow.common.referenceVar')}${instruction_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: [...query_variable_selector_warnings, ...vision_variable_selector_warnings, ...instruction_warnings], | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,6 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import type { TemplateTransformNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| const i18nPrefix = 'workflow.errorMsg' | |||
| @@ -33,6 +34,19 @@ const nodeDefault: NodeDefault<TemplateTransformNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: TemplateTransformNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const variables_selector = payload.variables.map(v => v.value_selector) | |||
| const variables_selector_warnings = getNotExistVariablesByArray(variables_selector, varMap) | |||
| if (variables_selector_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.templateTransform.inputVars')} ${t('workflow.common.referenceVar')}${variables_selector_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,8 +1,9 @@ | |||
| import { BlockEnum } from '../../types' | |||
| import type { NodeDefault } from '../../types' | |||
| import type { NodeDefault, Var } from '../../types' | |||
| import type { ToolNodeType } from './types' | |||
| import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow' | |||
| const i18nPrefix = 'workflow.errorMsg' | |||
| @@ -63,6 +64,35 @@ const nodeDefault: NodeDefault<ToolNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: ToolNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr = [] | |||
| const tool_parametersMap = payload.tool_parameters | |||
| const tool_parameters_array = Object.values(tool_parametersMap) | |||
| const tool_parameters_warnings: string[] = [] | |||
| tool_parameters_array?.forEach((item) => { | |||
| if (!item.value) | |||
| return | |||
| if (Array.isArray(item.value)) { | |||
| const warnings = getNotExistVariablesByArray([item.value], varMap) | |||
| if (warnings.length) | |||
| tool_parameters_warnings.push(...warnings) | |||
| return | |||
| } | |||
| if (typeof item.value === 'string') { | |||
| const warnings = getNotExistVariablesByText(item.value, varMap) | |||
| if (warnings.length) | |||
| tool_parameters_warnings.push(...warnings) | |||
| } | |||
| }) | |||
| if (tool_parameters_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.tool.inputVars')} ${t('workflow.common.referenceVar')}${tool_parameters_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: tool_parameters_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -1,5 +1,7 @@ | |||
| import type { Var } from '../../types' | |||
| import { type NodeDefault, VarType } from '../../types' | |||
| import { BlockEnum } from '../../types' | |||
| import { getNotExistVariablesByArray } from '../../utils/workflow' | |||
| import type { VariableAssignerNodeType } from './types' | |||
| import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' | |||
| @@ -54,6 +56,18 @@ const nodeDefault: NodeDefault<VariableAssignerNodeType> = { | |||
| errorMessage: errorMessages, | |||
| } | |||
| }, | |||
| checkVarValid(payload: VariableAssignerNodeType, varMap: Record<string, Var>, t: any) { | |||
| const errorMessageArr: string[] = [] | |||
| const variables_warnings = getNotExistVariablesByArray(payload.variables ?? [], varMap) | |||
| if (variables_warnings.length) | |||
| errorMessageArr.push(`${t('workflow.nodes.variableAssigner.title')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`) | |||
| return { | |||
| isValid: true, | |||
| warning_vars: variables_warnings, | |||
| errorMessage: errorMessageArr, | |||
| } | |||
| }, | |||
| } | |||
| export default nodeDefault | |||
| @@ -297,6 +297,7 @@ export type NodeDefault<T> = { | |||
| getAvailablePrevNodes: (isChatMode: boolean) => BlockEnum[] | |||
| getAvailableNextNodes: (isChatMode: boolean) => BlockEnum[] | |||
| checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string } | |||
| checkVarValid?: (payload: T, varMap: Record<string, Var>, t: any,) => { isValid: boolean; errorMessage?: string[] } | |||
| } | |||
| export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void | |||
| @@ -5,6 +5,18 @@ import type { | |||
| } from '@/app/components/workflow/types' | |||
| import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' | |||
| jest.mock('ky', () => ({ | |||
| __esModule: true, | |||
| default: { | |||
| create: jest.fn(), | |||
| }, | |||
| })) | |||
| jest.mock('lodash-es/groupBy', () => ({ | |||
| __esModule: true, | |||
| default: jest.fn(), | |||
| })) | |||
| describe('preprocessNodesAndEdges', () => { | |||
| it('process nodes without iteration node or loop node should return origin nodes and edges.', () => { | |||
| const nodes = [ | |||
| @@ -10,14 +10,20 @@ import { | |||
| uniqBy, | |||
| } from 'lodash-es' | |||
| import type { | |||
| ConversationVariable, | |||
| Edge, | |||
| EnvironmentVariable, | |||
| Node, | |||
| Var, | |||
| } from '../types' | |||
| import { | |||
| BlockEnum, | |||
| } from '../types' | |||
| import type { IterationNodeType } from '../nodes/iteration/types' | |||
| import type { LoopNodeType } from '../nodes/loop/types' | |||
| import { VAR_REGEX_TEXT } from '@/config' | |||
| import { formatItem } from '../nodes/_base/components/variable/utils' | |||
| import type { StructuredOutput } from '../nodes/llm/types' | |||
| export const canRunBySingle = (nodeType: BlockEnum) => { | |||
| return nodeType === BlockEnum.LLM | |||
| @@ -86,7 +92,17 @@ export const getNodesConnectedSourceOrTargetHandleIdsMap = (changes: ConnectedSo | |||
| return nodesConnectedSourceOrTargetHandleIdsMap | |||
| } | |||
| export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => { | |||
| function getParentOutputVarMap(item: Var, path: string, varMap: Record<string, Var>) { | |||
| if (!item.children || (Array.isArray(item.children) && !item.children.length) || ((item.children as StructuredOutput).schema)) | |||
| return | |||
| (item.children as Var[]).forEach((child) => { | |||
| const newPath = `${path}.${child.variable}` | |||
| varMap[newPath] = child | |||
| getParentOutputVarMap(child, newPath, varMap) | |||
| }) | |||
| } | |||
| export const getValidTreeNodes = (nodes: Node[], edges: Edge[], isCollectVar?: boolean) => { | |||
| const startNode = nodes.find(node => node.data.type === BlockEnum.Start) | |||
| if (!startNode) { | |||
| @@ -109,6 +125,19 @@ export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => { | |||
| outgoers.forEach((outgoer) => { | |||
| list.push(outgoer) | |||
| if (isCollectVar) { | |||
| const nodeObj = formatItem(root, false, () => true) | |||
| const varMap = {} as Record<string, Var> | |||
| nodeObj.vars.forEach((item) => { | |||
| if (item.variable.startsWith('sys.')) | |||
| return | |||
| const newPath = `${nodeObj.nodeId}.${item.variable}` | |||
| varMap[newPath] = item | |||
| getParentOutputVarMap(item, newPath, varMap) | |||
| }) | |||
| outgoer._parentOutputVarMap = { ...(root._parentOutputVarMap ?? {}), ...varMap } | |||
| } | |||
| if (outgoer.data.type === BlockEnum.Iteration) | |||
| list.push(...nodes.filter(node => node.parentId === outgoer.id)) | |||
| if (outgoer.data.type === BlockEnum.Loop) | |||
| @@ -327,3 +356,48 @@ export const getParallelInfo = (nodes: Node[], edges: Edge[], parentNodeId?: str | |||
| export const hasErrorHandleNode = (nodeType?: BlockEnum) => { | |||
| return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code | |||
| } | |||
| export const transformStartNodeVariables = (chatVarList: ConversationVariable[], environmentVariables: EnvironmentVariable[]) => { | |||
| const variablesMap: Record<string, ConversationVariable | EnvironmentVariable> = {} | |||
| chatVarList.forEach((variable) => { | |||
| variablesMap[`conversation.${variable.name}`] = variable | |||
| }) | |||
| environmentVariables.forEach((variable) => { | |||
| variablesMap[`env.${variable.name}`] = variable | |||
| }) | |||
| return variablesMap | |||
| } | |||
| export const getNotExistVariablesByText = (text: string, varMap: Record<string, Var>) => { | |||
| const var_warnings: string[] = [] | |||
| text?.replace(VAR_REGEX_TEXT, (str, id_name) => { | |||
| if (id_name.startsWith('sys.')) | |||
| return str | |||
| if (varMap[id_name]) | |||
| return str | |||
| const arr = id_name.split('.') | |||
| arr.shift() | |||
| var_warnings.push(arr.join('.')) | |||
| return str | |||
| }) | |||
| return var_warnings | |||
| } | |||
| export const getNotExistVariablesByArray = (array: string[][], varMap: Record<string, Var>) => { | |||
| if (!array.length) | |||
| return [] | |||
| const var_warnings: string[] = [] | |||
| array.forEach((item) => { | |||
| if (!item.length) | |||
| return | |||
| if (['sys'].includes(item[0])) | |||
| return | |||
| const var_warning = varMap[item.join('.')] | |||
| if (var_warning) | |||
| return | |||
| const arr = [...item] | |||
| arr.shift() | |||
| var_warnings.push(arr.join('.')) | |||
| }) | |||
| return var_warnings | |||
| } | |||
| @@ -282,6 +282,8 @@ Thought: {{agent_scratchpad}} | |||
| export const VAR_REGEX = /\{\{(#[a-zA-Z0-9_-]{1,50}(\.[a-zA-Z_]\w{0,29}){1,10}#)\}\}/gi | |||
| export const VAR_REGEX_TEXT = /\{\{#([a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*)#\}\}/gi | |||
| export const resetReg = () => VAR_REGEX.lastIndex = 0 | |||
| export let textGenerationTimeoutMs = 60000 | |||
| @@ -113,6 +113,8 @@ const translation = { | |||
| addFailureBranch: 'Add Fail Branch', | |||
| loadMore: 'Load More', | |||
| noHistory: 'No History', | |||
| referenceVar: 'Reference Variable', | |||
| noExist: 'No such variable', | |||
| }, | |||
| env: { | |||
| envPanelTitle: 'Environment Variables', | |||
| @@ -596,6 +598,7 @@ const translation = { | |||
| selectVariable: 'Select variable...', | |||
| addSubVariable: 'Sub Variable', | |||
| select: 'Select', | |||
| condition: 'Condition', | |||
| }, | |||
| variableAssigner: { | |||
| title: 'Assign variables', | |||
| @@ -113,6 +113,8 @@ const translation = { | |||
| addFailureBranch: '失敗ブランチを追加', | |||
| loadMore: 'さらに読み込む', | |||
| noHistory: '履歴がありません', | |||
| referenceVar: '参照変数', | |||
| noExist: '存在しません', | |||
| }, | |||
| env: { | |||
| envPanelTitle: '環境変数', | |||
| @@ -596,6 +598,7 @@ const translation = { | |||
| }, | |||
| select: '選ぶ', | |||
| addSubVariable: 'サブ変数', | |||
| condition: '条件', | |||
| }, | |||
| variableAssigner: { | |||
| title: '変数を代入する', | |||
| @@ -113,6 +113,8 @@ const translation = { | |||
| openInExplore: '在“探索”中打开', | |||
| loadMore: '加载更多', | |||
| noHistory: '没有历史版本', | |||
| referenceVar: '引用变量', | |||
| noExist: '不存在', | |||
| }, | |||
| env: { | |||
| envPanelTitle: '环境变量', | |||
| @@ -597,6 +599,7 @@ const translation = { | |||
| selectVariable: '选择变量', | |||
| addSubVariable: '添加子变量', | |||
| select: '选择', | |||
| condition: '条件', | |||
| }, | |||
| variableAssigner: { | |||
| title: '变量赋值', | |||