From 66cf78775564de400b2413593282418e3c450e92 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 22 Mar 2024 14:35:12 +0800 Subject: [PATCH] fix: can remove struct --- .../model-parameter-modal/index.tsx | 2 +- .../components/workflow/hooks/use-workflow.ts | 34 ++++++- .../nodes/_base/components/variable/utils.ts | 97 ++++++++++++++++++- .../nodes/start/components/var-list.tsx | 12 ++- .../workflow/nodes/start/use-config.ts | 8 +- web/app/components/workflow/types.ts | 3 +- 6 files changed, 147 insertions(+), 9 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 5a4923fbaf..c9ce37a7e5 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -88,7 +88,7 @@ const ModelParameterModal: FC = ({ }) => { const { t } = useTranslation() const { hasSettedApiKey } = useProviderContext() - const [open, setOpen] = useState(true) + const [open, setOpen] = useState(false) const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules) const { currentProvider, diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index fd4cfa4956..a168f81994 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -42,7 +42,7 @@ import { START_INITIAL_POSITION, SUPPORT_OUTPUT_VARS_NODE, } from '../constants' -import { findUsedVarNodes, updateNodeVars } from '../nodes/_base/components/variable/utils' +import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils' import { useNodesExtraData, useNodesInitialData, @@ -233,6 +233,35 @@ export const useWorkflow = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [store]) + const isVarUsedInNodes = useCallback((nodeId: string, varSelector: ValueSelector) => { + const afterNodes = getAfterNodesInSameBranch(nodeId) + const effectNodes = findUsedVarNodes(varSelector, afterNodes) + return effectNodes.length > 0 + }, [getAfterNodesInSameBranch]) + + const removeUsedVarInNodes = useCallback((nodeId: string, varSelector: ValueSelector) => { + const { getNodes, setNodes } = store.getState() + const afterNodes = getAfterNodesInSameBranch(nodeId) + const effectNodes = findUsedVarNodes(varSelector, afterNodes) + if (effectNodes.length > 0) { + const newNodes = getNodes().map((node) => { + if (effectNodes.find(n => n.id === node.id)) + return updateNodeVars(node, varSelector, []) + + return node + }) + setNodes(newNodes) + } + }, [getAfterNodesInSameBranch, store]) + + const isNodeVarsUsedInNodes = useCallback((node: Node, isChatMode: boolean) => { + const outputVars = getNodeOutputVars(node, isChatMode) + const isUsed = outputVars.some((varSelector) => { + return isVarUsedInNodes(node.id, varSelector) + }) + return isUsed + }, [isVarUsedInNodes]) + const isValidConnection = useCallback(({ source, target }: Connection) => { const { getNodes } = store.getState() const nodes = getNodes() @@ -317,6 +346,9 @@ export const useWorkflow = () => { getBeforeNodesInSameBranch, getAfterNodesInSameBranch, handleOutVarRenameChange, + isVarUsedInNodes, + removeUsedVarInNodes, + isNodeVarsUsedInNodes, isValidConnection, formatTimeFromNow, getValidTreeNodes, diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index c167c936c4..9cc462815a 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -211,7 +211,7 @@ export const getVarType = (value: ValueSelector, availableNodes: any[], isChatMo } } -const getNodeUsedVars = (node: Node): ValueSelector[] => { +export const getNodeUsedVars = (node: Node): ValueSelector[] => { const { data } = node const { type } = data let res: ValueSelector[] = [] @@ -421,3 +421,98 @@ export const updateNodeVars = (oldNode: Node, oldVarSelector: ValueSelector, new }) return newNode } +const varToValueSelectorList = (v: Var, parentValueSelector: ValueSelector, res: ValueSelector[]) => { + if (!v.variable) + return + + res.push([...parentValueSelector, v.variable]) + + if (v.children && v.children.length > 0) { + v.children.forEach((child) => { + varToValueSelectorList(child, [...parentValueSelector, v.variable], res) + }) + } +} + +const varsToValueSelectorList = (vars: Var | Var[], parentValueSelector: ValueSelector, res: ValueSelector[]) => { + if (Array.isArray(vars)) { + vars.forEach((v) => { + varToValueSelectorList(v, parentValueSelector, res) + }) + } + varToValueSelectorList(vars as Var, parentValueSelector, res) +} + +// const test: ValueSelector[] = [] +// varsToValueSelectorList(LLM_OUTPUT_STRUCT, ['ttt'], test) +// console.log(test) + +export const getNodeOutputVars = (node: Node, isChatMode: boolean): ValueSelector[] => { + const { data, id } = node + const { type } = data + let res: ValueSelector[] = [] + + switch (type) { + case BlockEnum.Start: { + const { + variables, + } = data as StartNodeType + res = variables.map((v) => { + return [id, v.variable] + }) + + if (isChatMode) { + res.push([id, 'sys', 'query']) + res.push([id, 'sys', 'files']) + } + break + } + + case BlockEnum.LLM: { + varsToValueSelectorList(LLM_OUTPUT_STRUCT, [id], res) + break + } + + case BlockEnum.KnowledgeRetrieval: { + varsToValueSelectorList(KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT, [id], res) + break + } + + case BlockEnum.Code: { + const { + outputs, + } = data as CodeNodeType + Object.keys(outputs).forEach((key) => { + res.push([id, key]) + }) + break + } + + case BlockEnum.TemplateTransform: { + varsToValueSelectorList(TEMPLATE_TRANSFORM_OUTPUT_STRUCT, [id], res) + break + } + + case BlockEnum.QuestionClassifier: { + varsToValueSelectorList(isChatMode ? CHAT_QUESTION_CLASSIFIER_OUTPUT_STRUCT : COMPLETION_QUESTION_CLASSIFIER_OUTPUT_STRUCT, [id], res) + break + } + + case BlockEnum.HttpRequest: { + varsToValueSelectorList(HTTP_REQUEST_OUTPUT_STRUCT, [id], res) + break + } + + case BlockEnum.VariableAssigner: { + res.push([id, 'output']) + break + } + + case BlockEnum.Tool: { + varsToValueSelectorList(TOOL_OUTPUT_STRUCT, [id], res) + break + } + } + + return res +} diff --git a/web/app/components/workflow/nodes/start/components/var-list.tsx b/web/app/components/workflow/nodes/start/components/var-list.tsx index a0a47caa66..f35ab77b32 100644 --- a/web/app/components/workflow/nodes/start/components/var-list.tsx +++ b/web/app/components/workflow/nodes/start/components/var-list.tsx @@ -4,7 +4,7 @@ import React, { useCallback } from 'react' import produce from 'immer' import { useTranslation } from 'react-i18next' import VarItem from './var-item' -import type { InputVar, MoreInfo } from '@/app/components/workflow/types' +import { ChangeType, type InputVar, type MoreInfo } from '@/app/components/workflow/types' type Props = { readonly: boolean list: InputVar[] @@ -32,7 +32,15 @@ const VarList: FC = ({ const newList = produce(list, (draft) => { draft.splice(index, 1) }) - onChange(newList) + onChange(newList, { + index, + payload: { + type: ChangeType.remove, + payload: { + beforeKey: list[index].variable, + }, + }, + }) } }, [list, onChange]) diff --git a/web/app/components/workflow/nodes/start/use-config.ts b/web/app/components/workflow/nodes/start/use-config.ts index 289e89c268..d77096767d 100644 --- a/web/app/components/workflow/nodes/start/use-config.ts +++ b/web/app/components/workflow/nodes/start/use-config.ts @@ -2,7 +2,7 @@ import { useCallback } from 'react' import produce from 'immer' import { useBoolean } from 'ahooks' import type { StartNodeType } from './types' -import type { InputVar, MoreInfo } from '@/app/components/workflow/types' +import { ChangeType, type InputVar, type MoreInfo } from '@/app/components/workflow/types' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { useIsChatMode, @@ -12,7 +12,7 @@ import { const useConfig = (id: string, payload: StartNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() - const { handleOutVarRenameChange } = useWorkflow() + const { handleOutVarRenameChange, removeUsedVarInNodes } = useWorkflow() const isChatMode = useIsChatMode() const { inputs, setInputs } = useNodeCrud(id, payload) @@ -27,10 +27,12 @@ const useConfig = (id: string, payload: StartNodeType) => { draft.variables = newList }) setInputs(newInputs) - if (moreInfo) { + if (moreInfo?.payload?.type === ChangeType.changeVarName) { const changedVar = newList[moreInfo.index] handleOutVarRenameChange(id, [id, inputs.variables[moreInfo.index].variable], [id, changedVar.variable]) } + if (moreInfo?.payload?.type === ChangeType.remove) + removeUsedVarInNodes(id, [id, moreInfo?.payload?.payload?.beforeKey || '']) }, [handleOutVarRenameChange, id, inputs, setInputs]) const handleAddVariable = useCallback((payload: InputVar) => { diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index d9d8e243aa..e8713dcb6c 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -255,13 +255,14 @@ export type HistoryWorkflowData = { export enum ChangeType { changeVarName = 'changeVarName', + remove = 'remove', } export type MoreInfo = { type: ChangeType payload?: { beforeKey: string - afterKey: string + afterKey?: string } }