From c4b58ed19557a7203bae72eadabce554e6918507 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 25 Jun 2025 16:23:20 +0800 Subject: [PATCH] Feat: Filter the query variable drop-down box options by type #3221 (#8485) ### What problem does this PR solve? Feat: Filter the query variable drop-down box options by type #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../originui/select-with-search.tsx | 2 ++ .../pages/agent/canvas/node/agent-node.tsx | 14 +++++------ web/src/pages/agent/constant.tsx | 5 ++++ .../agent/form-sheet/use-form-config-map.tsx | 3 ++- .../agent/form/begin-form/use-edit-query.ts | 6 +++-- .../agent/form/components/query-variable.tsx | 17 ++++++++++--- .../pages/agent/form/iteration-form/index.tsx | 7 ++++-- web/src/pages/agent/hooks/use-add-node.ts | 22 +++++++++++++++- .../pages/agent/hooks/use-get-begin-query.tsx | 25 ++++++++++++++++--- web/src/pages/agent/hooks/use-show-drawer.tsx | 2 +- web/src/pages/agent/store.ts | 11 ++++++++ web/src/pages/flow/constant.tsx | 11 +++++++- .../flow/form/iteration-start-from/index.tsx | 22 ++++++++++++++++ 13 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 web/src/pages/flow/form/iteration-start-from/index.tsx diff --git a/web/src/components/originui/select-with-search.tsx b/web/src/components/originui/select-with-search.tsx index fbf1584e0..469802d34 100644 --- a/web/src/components/originui/select-with-search.tsx +++ b/web/src/components/originui/select-with-search.tsx @@ -168,3 +168,5 @@ export const SelectWithSearch = forwardRef< ); }); + +SelectWithSearch.displayName = 'SelectWithSearch'; diff --git a/web/src/pages/agent/canvas/node/agent-node.tsx b/web/src/pages/agent/canvas/node/agent-node.tsx index 731f155a7..8a585760e 100644 --- a/web/src/pages/agent/canvas/node/agent-node.tsx +++ b/web/src/pages/agent/canvas/node/agent-node.tsx @@ -1,8 +1,9 @@ import { IAgentNode } from '@/interfaces/database/flow'; import { Handle, NodeProps, Position } from '@xyflow/react'; import { memo, useMemo } from 'react'; -import { NodeHandleId, Operator } from '../../constant'; +import { NodeHandleId } from '../../constant'; import useGraphStore from '../../store'; +import { isBottomSubAgent } from '../../utils'; import { CommonHandle } from './handle'; import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; import styles from './index.less'; @@ -16,19 +17,16 @@ function InnerAgentNode({ isConnectable = true, selected, }: NodeProps) { - const getNode = useGraphStore((state) => state.getNode); const edges = useGraphStore((state) => state.edges); - const isNotParentAgent = useMemo(() => { - const edge = edges.find((x) => x.target === id); - const label = getNode(edge?.source)?.data.label; - return label !== Operator.Agent; - }, [edges, getNode, id]); + const isHeadAgent = useMemo(() => { + return !isBottomSubAgent(edges, id); + }, [edges, id]); return ( - {isNotParentAgent && ( + {isHeadAgent && ( <> <>, + component: IterationStartForm, defaultValues: {}, schema: z.object({}), }, diff --git a/web/src/pages/agent/form/begin-form/use-edit-query.ts b/web/src/pages/agent/form/begin-form/use-edit-query.ts index a1bec8b3d..05b8240f2 100644 --- a/web/src/pages/agent/form/begin-form/use-edit-query.ts +++ b/web/src/pages/agent/form/begin-form/use-edit-query.ts @@ -1,9 +1,12 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { useCallback, useMemo, useState } from 'react'; +import { UseFormReturn } from 'react-hook-form'; import { BeginQuery, INextOperatorForm } from '../../interface'; -export const useEditQueryRecord = ({ form, node }: INextOperatorForm) => { +export const useEditQueryRecord = ({ + form, +}: INextOperatorForm & { form: UseFormReturn }) => { const { setRecord, currentRecord } = useSetSelectedRecord(); const { visible, hideModal, showModal } = useSetModalState(); const [index, setIndex] = useState(-1); @@ -16,7 +19,6 @@ export const useEditQueryRecord = ({ form, node }: INextOperatorForm) => { const handleEditRecord = useCallback( (record: BeginQuery) => { const inputs: BeginQuery[] = form?.getValues('inputs') || []; - console.log('🚀 ~ useEditQueryRecord ~ inputs:', inputs); const nextQuery: BeginQuery[] = index > -1 ? inputs.toSpliced(index, 1, record) : [...inputs, record]; diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index 213d8dbe5..caa85cd7a 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -6,18 +6,29 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; +import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import { VariableType } from '../../constant'; import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; -type QueryVariableProps = { name?: string }; +type QueryVariableProps = { name?: string; type?: VariableType }; -export function QueryVariable({ name = 'query' }: QueryVariableProps) { +export function QueryVariable({ + name = 'query', + type = VariableType.String, +}: QueryVariableProps) { const { t } = useTranslation(); const form = useFormContext(); const nextOptions = useBuildQueryVariableOptions(); + const finalOptions = useMemo(() => { + return nextOptions.map((x) => { + return { ...x, options: x.options.filter((y) => y.type === type) }; + }); + }, [nextOptions, type]); + return ( {t('flow.query')} diff --git a/web/src/pages/agent/form/iteration-form/index.tsx b/web/src/pages/agent/form/iteration-form/index.tsx index bb9b7436f..7a9d58dc1 100644 --- a/web/src/pages/agent/form/iteration-form/index.tsx +++ b/web/src/pages/agent/form/iteration-form/index.tsx @@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; -import { initialRetrievalValues } from '../../constant'; +import { initialRetrievalValues, VariableType } from '../../constant'; import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; import { Output } from '../components/output'; @@ -50,7 +50,10 @@ const IterationForm = ({ node }: INextOperatorForm) => { }} > - + diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index d4008ee01..28aeef549 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -232,8 +232,15 @@ function useAddToolNode() { return { addToolNode }; } +function isBottomSubAgent(type: string, position: Position) { + return ( + (type === Operator.Agent && position === Position.Bottom) || + type === Operator.Tool + ); +} + export function useAddNode(reactFlowInstance?: ReactFlowInstance) { - const { edges, nodes, addEdge, addNode, getNode } = useGraphStore( + const { edges, nodes, addEdge, addNode, getNode, updateNode } = useGraphStore( (state) => state, ); const getNodeName = useGetNodeName(); @@ -291,6 +298,18 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { if (node && node.parentId) { newNode.parentId = node.parentId; newNode.extent = 'parent'; + const parentNode = getNode(node.parentId); + if (parentNode && !isBottomSubAgent(type, params.position)) { + const MoveRightDistance = 310; + updateNode({ + ...parentNode, + width: (parentNode.width || 0) + MoveRightDistance, + position: { + x: parentNode.position.x + MoveRightDistance / 2, + y: parentNode.position.y, + }, + }); + } } if (type === Operator.Iteration) { @@ -377,6 +396,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { initializeOperatorParams, nodes, reactFlowInstance, + updateNode, ], ); diff --git a/web/src/pages/agent/hooks/use-get-begin-query.tsx b/web/src/pages/agent/hooks/use-get-begin-query.tsx index c53e23f48..69ba6b8af 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -5,7 +5,7 @@ import { DefaultOptionType } from 'antd/es/select'; import { isEmpty } from 'lodash'; import get from 'lodash/get'; import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { BeginId, Operator } from '../constant'; +import { BeginId, BeginQueryType, Operator, VariableType } from '../constant'; import { AgentFormContext } from '../context'; import { buildBeginInputListFromObject } from '../form/begin-form/utils'; import { BeginQuery } from '../interface'; @@ -65,6 +65,7 @@ function buildOutputOptions( return Object.keys(outputs).map((x) => ({ label: x, value: `${nodeId}@${x}`, + type: outputs[x]?.type, })); } @@ -104,6 +105,19 @@ const ExcludedNodes = [ Operator.Note, ]; +const StringList = [ + BeginQueryType.Line, + BeginQueryType.Paragraph, + BeginQueryType.Options, +]; + +function transferToVariableType(type: string) { + if (StringList.some((x) => x === type)) { + return VariableType.String; + } + return type; +} + export function useBuildBeginVariableOptions() { const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); @@ -116,6 +130,7 @@ export function useBuildBeginVariableOptions() { options: query.map((x) => ({ label: x.name, value: `begin@${x.key}`, + type: transferToVariableType(x.type), })), }, ]; @@ -141,9 +156,11 @@ export function useBuildQueryVariableOptions() { const options = useBuildVariableOptions(node?.id); const nextOptions = useMemo(() => { - const globalOptions = Object.keys(data?.dsl?.globals ?? {}).map((x) => ({ - label: x, - value: x, + const globals = data?.dsl?.globals ?? {}; + const globalOptions = Object.entries(globals).map(([key, value]) => ({ + label: key, + value: key, + type: Array.isArray(value) ? VariableType.Array : typeof value, })); return [ { ...options[0], options: [...options[0]?.options, ...globalOptions] }, diff --git a/web/src/pages/agent/hooks/use-show-drawer.tsx b/web/src/pages/agent/hooks/use-show-drawer.tsx index 990ea6f92..b45863b79 100644 --- a/web/src/pages/agent/hooks/use-show-drawer.tsx +++ b/web/src/pages/agent/hooks/use-show-drawer.tsx @@ -57,7 +57,7 @@ export const useShowSingleDebugDrawer = () => { }; }; -const ExcludedNodes = [Operator.IterationStart, Operator.Note]; +const ExcludedNodes = [Operator.Note]; export function useShowDrawer({ drawerVisible, diff --git a/web/src/pages/agent/store.ts b/web/src/pages/agent/store.ts index 31ad0a46b..a78768e01 100644 --- a/web/src/pages/agent/store.ts +++ b/web/src/pages/agent/store.ts @@ -56,6 +56,7 @@ export type RFState = { onSelectionChange: OnSelectionChangeFunc; addNode: (nodes: RAGFlowNodeType) => void; getNode: (id?: string | null) => RAGFlowNodeType | undefined; + updateNode: (node: RAGFlowNodeType) => void; addEdge: (connection: Connection) => void; getEdge: (id: string) => Edge | undefined; updateFormDataOnConnect: (connection: Connection) => void; @@ -192,6 +193,16 @@ const useGraphStore = create()( addNode: (node: RAGFlowNodeType) => { set({ nodes: get().nodes.concat(node) }); }, + updateNode: (node) => { + const { nodes } = get(); + const nextNodes = nodes.map((x) => { + if (x.id === node.id) { + return node; + } + return x; + }); + set({ nodes: nextNodes }); + }, getNode: (id?: string | null) => { return get().nodes.find((x) => x.id === id); }, diff --git a/web/src/pages/flow/constant.tsx b/web/src/pages/flow/constant.tsx index 77ce94532..7e5a7604d 100644 --- a/web/src/pages/flow/constant.tsx +++ b/web/src/pages/flow/constant.tsx @@ -639,7 +639,16 @@ export const initialEmailValues = { export const initialIterationValues = { delimiter: ',', }; -export const initialIterationStartValues = {}; +export const initialIterationStartValues = { + outputs: { + item: { + type: 'unkown', + }, + index: { + type: 'integer', + }, + }, +}; export const initialCodeValues = { lang: 'python', diff --git a/web/src/pages/flow/form/iteration-start-from/index.tsx b/web/src/pages/flow/form/iteration-start-from/index.tsx new file mode 100644 index 000000000..f4257c2d4 --- /dev/null +++ b/web/src/pages/flow/form/iteration-start-from/index.tsx @@ -0,0 +1,22 @@ +import { Output, OutputType } from '@/pages/agent/form/components/output'; +import { initialIterationStartValues } from '../../constant'; + +const outputs = initialIterationStartValues.outputs; + +const outputList = Object.entries(outputs).reduce( + (pre, [key, value]) => { + pre.push({ title: key, type: value.type }); + + return pre; + }, + [], +); +const IterationStartForm = () => { + return ( +
+ +
+ ); +}; + +export default IterationStartForm;