From 98829f5dbe317747b356958baeb85464c5b767ab Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 10 Jul 2025 18:36:34 +0800 Subject: [PATCH] Feat: Modify the agent tool name #3221 (#8780) ### What problem does this PR solve? Feat: Modify the agent tool name #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/interfaces/database/agent.ts | 1 + web/src/pages/agent/form-sheet/next.tsx | 2 +- .../tool-popover/use-update-tools.ts | 1 + web/src/pages/agent/hooks.tsx | 171 +----------------- .../pages/agent/hooks/use-change-node-name.ts | 120 ++++++++++++ 5 files changed, 125 insertions(+), 170 deletions(-) create mode 100644 web/src/pages/agent/hooks/use-change-node-name.ts diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index ca954c050..088564bc1 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -155,6 +155,7 @@ export interface IAgentForm { exception_comment: any; exception_goto: any; tools: Array<{ + name: string; component_name: string; params: Record; }>; diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 39794bfca..d707e219b 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -14,7 +14,7 @@ import { Play, X } from 'lucide-react'; import { BeginId, Operator } from '../constant'; import { AgentFormContext } from '../context'; import { RunTooltip } from '../flow-tooltip'; -import { useHandleNodeNameChange } from '../hooks'; +import { useHandleNodeNameChange } from '../hooks/use-change-node-name'; import OperatorIcon from '../operator-icon'; import { needsSingleStepDebugging } from '../utils'; import SingleDebugDrawer from './single-debug-drawer'; diff --git a/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts b/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts index 8db1df8ab..52d893cc2 100644 --- a/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts +++ b/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts @@ -29,6 +29,7 @@ export function useUpdateAgentNodeTools() { ? tool : { component_name: cur, + name: cur, params: DefaultAgentToolValuesMap[ cur as keyof typeof DefaultAgentToolValuesMap diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index 507d73e76..c1b44b605 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -5,34 +5,19 @@ import { Position, ReactFlowInstance, } from '@xyflow/react'; -import React, { - ChangeEvent, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; // import { shallow } from 'zustand/shallow'; import { settledModelVariableMap } from '@/constants/knowledge'; import { useFetchModelId } from '@/hooks/logic-hooks'; -import { ISwitchForm } from '@/interfaces/database/agent'; -import { - ICategorizeForm, - IRelevantForm, - RAGFlowNodeType, -} from '@/interfaces/database/flow'; -import { message } from 'antd'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { humanId } from 'human-id'; import { get, lowerFirst, omit } from 'lodash'; -import trim from 'lodash/trim'; import { UseFormReturn } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { v4 as uuid } from 'uuid'; import { NodeMap, Operator, RestrictedUpstreamMap, - SwitchElseTo, initialAgentValues, initialAkShareValues, initialArXivValues, @@ -76,7 +61,6 @@ import useGraphStore, { RFState } from './store'; import { buildCategorizeObjectFromList, generateNodeNamesWithIncreasingIndex, - generateSwitchHandleText, getNodeDragHandle, getRelativePositionToIterationNode, replaceIdWithText, @@ -389,43 +373,6 @@ export const useValidateConnection = () => { return isValidConnection; }; -export const useHandleNodeNameChange = ({ - id, - data, -}: { - id?: string; - data: any; -}) => { - const [name, setName] = useState(''); - const { updateNodeName, nodes } = useGraphStore((state) => state); - const previousName = data?.name; - - const handleNameBlur = useCallback(() => { - const existsSameName = nodes.some((x) => x.data.name === name); - if (trim(name) === '' || existsSameName) { - if (existsSameName && previousName !== name) { - message.error('The name cannot be repeated'); - } - setName(previousName); - return; - } - - if (id) { - updateNodeName(id, name); - } - }, [name, id, updateNodeName, previousName, nodes]); - - const handleNameChange = useCallback((e: ChangeEvent) => { - setName(e.target.value); - }, []); - - useEffect(() => { - setName(previousName); - }, [previousName]); - - return { name, handleNameBlur, handleNameChange }; -}; - export const useReplaceIdWithName = () => { const getNode = useGraphStore((state) => state.getNode); @@ -448,120 +395,6 @@ export const useReplaceIdWithText = (output: unknown) => { }; }; -/** - * monitor changes in the data.form field of the categorize and relevant operators - * and then synchronize them to the edge - */ -export const useWatchNodeFormDataChange = () => { - const { getNode, nodes, setEdgesByNodeId } = useGraphStore((state) => state); - - const buildCategorizeEdgesByFormData = useCallback( - (nodeId: string, form: ICategorizeForm) => { - // add - // delete - // edit - const categoryDescription = form.category_description; - const downstreamEdges = Object.keys(categoryDescription).reduce( - (pre, sourceHandle) => { - const target = categoryDescription[sourceHandle]?.to; - if (target) { - pre.push({ - id: uuid(), - source: nodeId, - target, - sourceHandle, - }); - } - - return pre; - }, - [], - ); - - setEdgesByNodeId(nodeId, downstreamEdges); - }, - [setEdgesByNodeId], - ); - - const buildRelevantEdgesByFormData = useCallback( - (nodeId: string, form: IRelevantForm) => { - const downstreamEdges = ['yes', 'no'].reduce((pre, cur) => { - const target = form[cur as keyof IRelevantForm] as string; - if (target) { - pre.push({ id: uuid(), source: nodeId, target, sourceHandle: cur }); - } - - return pre; - }, []); - - setEdgesByNodeId(nodeId, downstreamEdges); - }, - [setEdgesByNodeId], - ); - - const buildSwitchEdgesByFormData = useCallback( - (nodeId: string, form: ISwitchForm) => { - // add - // delete - // edit - const conditions = form.conditions; - const downstreamEdges = conditions.reduce((pre, _, idx) => { - const target = conditions[idx]?.to; - if (target) { - pre.push({ - id: uuid(), - source: nodeId, - target, - sourceHandle: generateSwitchHandleText(idx), - }); - } - - return pre; - }, []); - - // Splice the else condition of the conditional judgment to the edge list - const elseTo = form[SwitchElseTo]; - if (elseTo) { - downstreamEdges.push({ - id: uuid(), - source: nodeId, - target: elseTo, - sourceHandle: SwitchElseTo, - }); - } - - setEdgesByNodeId(nodeId, downstreamEdges); - }, - [setEdgesByNodeId], - ); - - useEffect(() => { - nodes.forEach((node) => { - const currentNode = getNode(node.id); - const form = currentNode?.data.form ?? {}; - const operatorType = currentNode?.data.label; - switch (operatorType) { - case Operator.Relevant: - buildRelevantEdgesByFormData(node.id, form as IRelevantForm); - break; - case Operator.Categorize: - buildCategorizeEdgesByFormData(node.id, form as ICategorizeForm); - break; - // case Operator.Switch: - // buildSwitchEdgesByFormData(node.id, form as ISwitchForm); - // break; - default: - break; - } - }); - }, [ - nodes, - buildCategorizeEdgesByFormData, - getNode, - buildRelevantEdgesByFormData, - ]); -}; - export const useDuplicateNode = () => { const duplicateNodeById = useGraphStore((store) => store.duplicateNode); const getNodeName = useGetNodeName(); diff --git a/web/src/pages/agent/hooks/use-change-node-name.ts b/web/src/pages/agent/hooks/use-change-node-name.ts new file mode 100644 index 000000000..61a5653d7 --- /dev/null +++ b/web/src/pages/agent/hooks/use-change-node-name.ts @@ -0,0 +1,120 @@ +import message from '@/components/ui/message'; +import { trim } from 'lodash'; +import { + ChangeEvent, + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { Operator } from '../constant'; +import useGraphStore from '../store'; +import { getAgentNodeTools } from '../utils'; + +export function useHandleTooNodeNameChange({ + id, + name, + setName, +}: { + id?: string; + name?: string; + setName: Dispatch>; +}) { + const { clickedToolId, findUpstreamNodeById, updateNodeForm } = useGraphStore( + (state) => state, + ); + const agentNode = findUpstreamNodeById(id); + const tools = getAgentNodeTools(agentNode); + + const previousName = useMemo(() => { + const tool = tools.find((x) => x.component_name === clickedToolId); + return tool?.name || tool?.component_name; + }, [clickedToolId, tools]); + + const handleToolNameBlur = useCallback(() => { + const trimmedName = trim(name); + const existsSameName = tools.some((x) => x.name === trimmedName); + if (trimmedName === '' || existsSameName) { + if (existsSameName && previousName !== name) { + message.error('The name cannot be repeated'); + } + setName(previousName || ''); + return; + } + + if (agentNode?.id) { + const nextTools = tools.map((x) => { + if (x.component_name === clickedToolId) { + return { + ...x, + name, + }; + } + return x; + }); + updateNodeForm(agentNode?.id, nextTools, ['tools']); + } + }, [ + agentNode?.id, + clickedToolId, + name, + previousName, + setName, + tools, + updateNodeForm, + ]); + + return { handleToolNameBlur, previousToolName: previousName }; +} + +export const useHandleNodeNameChange = ({ + id, + data, +}: { + id?: string; + data: any; +}) => { + const [name, setName] = useState(''); + const { updateNodeName, nodes, getOperatorTypeFromId } = useGraphStore( + (state) => state, + ); + const previousName = data?.name; + const isToolNode = getOperatorTypeFromId(id) === Operator.Tool; + + const { handleToolNameBlur, previousToolName } = useHandleTooNodeNameChange({ + id, + name, + setName, + }); + + const handleNameBlur = useCallback(() => { + const existsSameName = nodes.some((x) => x.data.name === name); + if (trim(name) === '' || existsSameName) { + if (existsSameName && previousName !== name) { + message.error('The name cannot be repeated'); + } + setName(previousName); + return; + } + + if (id) { + updateNodeName(id, name); + } + }, [name, id, updateNodeName, previousName, nodes]); + + const handleNameChange = useCallback((e: ChangeEvent) => { + setName(e.target.value); + }, []); + + useEffect(() => { + setName(isToolNode ? previousToolName : previousName); + }, [isToolNode, previousName, previousToolName]); + + return { + name, + handleNameBlur: isToolNode ? handleToolNameBlur : handleNameBlur, + handleNameChange, + }; +};