diff --git a/web/src/constants/agent.tsx b/web/src/constants/agent.tsx index 20cd9557e..6ee8ab516 100644 --- a/web/src/constants/agent.tsx +++ b/web/src/constants/agent.tsx @@ -110,6 +110,7 @@ export enum Operator { Placeholder = 'Placeholder', DataOperations = 'DataOperations', VariableAssigner = 'VariableAssigner', + VariableAggregator = 'VariableAggregator', File = 'File', // pipeline Parser = 'Parser', Tokenizer = 'Tokenizer', diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index dfe83be52..b21113d79 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1550,6 +1550,10 @@ This delimiter is used to split the input text into several text pieces echo of variableAssigner: 'Variable assigner', variableAssignerDescription: 'This component performs operations on Data objects, including extracting, filtering, and editing keys and values in the Data.', + variableAggregator: 'Variable aggregator', + variableAggregatorDescription: `This process aggregates variables from multiple branches into a single variable to achieve unified configuration for downstream nodes. + +The variable aggregation node (originally the variable assignment node) is a crucial node in the workflow. It is responsible for integrating the output results of different branches, ensuring that regardless of which branch is executed, its result can be referenced and accessed through a unified variable. This is extremely useful in multi-branch scenarios, as it maps variables with the same function across different branches to a single output variable, avoiding redundant definitions in downstream nodes.`, inputVariables: 'Input variables', runningHintText: 'is running...🕞', openingSwitch: 'Opening switch', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index b99f4704d..aea5c2519 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1471,6 +1471,9 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 variableAssigner: '变量赋值器', variableAssignerDescription: '此组件对数据对象执行操作,包括提取、筛选和编辑数据中的键和值。', + variableAggregator: '变量聚合', + variableAggregatorDescription: `将多路分支的变量聚合为一个变量,以实现下游节点统一配置。 +变量聚合节点(原变量赋值节点)是工作流程中的一个关键节点,它负责整合不同分支的输出结果,确保无论哪个分支被执行,其结果都能通过一个统一的变量来引用和访问。这在多分支的情况下非常有用,可将不同分支下相同作用的变量映射为一个输出变量,避免下游节点重复定义。`, inputVariables: '输入变量', addVariable: '新增变量', runningHintText: '正在运行中...🕞', diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index f9d615824..7a25771e9 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -72,6 +72,7 @@ import { SwitchNode } from './node/switch-node'; import { TemplateNode } from './node/template-node'; import TokenizerNode from './node/tokenizer-node'; import { ToolNode } from './node/tool-node'; +import { VariableAggregatorNode } from './node/variable-aggregator-node'; import { VariableAssignerNode } from './node/variable-assigner-node'; export const nodeTypes: NodeTypes = { @@ -100,6 +101,7 @@ export const nodeTypes: NodeTypes = { contextNode: ExtractorNode, dataOperationsNode: DataOperationsNode, variableAssignerNode: VariableAssignerNode, + variableAggregatorNode: VariableAggregatorNode, }; const edgeTypes = { diff --git a/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx b/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx index d3c0b0c4d..1ab0dde1f 100644 --- a/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx @@ -80,6 +80,7 @@ export function AccordionOperators({ Operator.StringTransform, Operator.DataOperations, Operator.VariableAssigner, + Operator.VariableAggregator, ]} isCustomDropdown={isCustomDropdown} mousePosition={mousePosition} diff --git a/web/src/pages/agent/canvas/node/variable-aggregator-node.tsx b/web/src/pages/agent/canvas/node/variable-aggregator-node.tsx new file mode 100644 index 000000000..a6f9ff493 --- /dev/null +++ b/web/src/pages/agent/canvas/node/variable-aggregator-node.tsx @@ -0,0 +1,11 @@ +import { IRagNode } from '@/interfaces/database/agent'; +import { NodeProps } from '@xyflow/react'; +import { RagNode } from '.'; + +export function VariableAggregatorNode({ ...props }: NodeProps) { + return ( + +
VariableAggregatorNode
+
+ ); +} diff --git a/web/src/pages/agent/constant/index.tsx b/web/src/pages/agent/constant/index.tsx index f224e7563..2e85f46c2 100644 --- a/web/src/pages/agent/constant/index.tsx +++ b/web/src/pages/agent/constant/index.tsx @@ -22,7 +22,6 @@ export enum AgentDialogueMode { } import { ModelVariableType } from '@/constants/knowledge'; -import i18n from '@/locales/config'; import { t } from 'i18next'; // DuckDuckGo's channel options @@ -109,14 +108,6 @@ export const initialBeginValues = { prologue: `Hi! I'm your assistant. What can I do for you?`, }; -export const initialGenerateValues = { - ...initialLlmBaseValues, - prompt: i18n.t('flow.promptText'), - cite: true, - message_history_window_size: 12, - parameters: [], -}; - export const initialRewriteQuestionValues = { ...initialLlmBaseValues, language: '', @@ -607,6 +598,8 @@ export const initialDataOperationsValues = { export const initialVariableAssignerValues = {}; +export const initialVariableAggregatorValues = {}; + export const CategorizeAnchorPointPositions = [ { top: 1, right: 34 }, { top: 8, right: 18 }, @@ -737,6 +730,7 @@ export const NodeMap = { [Operator.Extractor]: 'contextNode', [Operator.DataOperations]: 'dataOperationsNode', [Operator.VariableAssigner]: 'variableAssignerNode', + [Operator.VariableAggregator]: 'variableAggregatorNode', }; export enum BeginQueryType { diff --git a/web/src/pages/agent/form-sheet/form-config-map.tsx b/web/src/pages/agent/form-sheet/form-config-map.tsx index a259c8e1f..6634ea594 100644 --- a/web/src/pages/agent/form-sheet/form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/form-config-map.tsx @@ -38,6 +38,7 @@ import TokenizerForm from '../form/tokenizer-form'; import ToolForm from '../form/tool-form'; import TuShareForm from '../form/tushare-form'; import UserFillUpForm from '../form/user-fill-up-form'; +import VariableAssignerForm from '../form/variable-assigner-form'; import WenCaiForm from '../form/wencai-form'; import WikipediaForm from '../form/wikipedia-form'; import YahooFinanceForm from '../form/yahoo-finance-form'; @@ -182,4 +183,7 @@ export const FormConfigMap = { [Operator.DataOperations]: { component: DataOperationsForm, }, + [Operator.VariableAssigner]: { + component: VariableAssignerForm, + }, }; diff --git a/web/src/pages/agent/form/variable-assigner-form/index.tsx b/web/src/pages/agent/form/variable-assigner-form/index.tsx new file mode 100644 index 000000000..97695f877 --- /dev/null +++ b/web/src/pages/agent/form/variable-assigner-form/index.tsx @@ -0,0 +1,100 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Form } from '@/components/ui/form'; +import { Separator } from '@/components/ui/separator'; +import { buildOptions } from '@/utils/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { + JsonSchemaDataType, + Operations, + initialDataOperationsValues, +} from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output, OutputSchema } from '../components/output'; +import { QueryVariableList } from '../components/query-variable-list'; + +export const RetrievalPartialSchema = { + query: z.array(z.object({ input: z.string().optional() })), + operations: z.string(), + select_keys: z.array(z.object({ name: z.string().optional() })).optional(), + remove_keys: z.array(z.object({ name: z.string().optional() })).optional(), + updates: z + .array( + z.object({ key: z.string().optional(), value: z.string().optional() }), + ) + .optional(), + rename_keys: z + .array( + z.object({ + old_key: z.string().optional(), + new_key: z.string().optional(), + }), + ) + .optional(), + filter_values: z + .array( + z.object({ + key: z.string().optional(), + value: z.string().optional(), + operator: z.string().optional(), + }), + ) + .optional(), + ...OutputSchema, +}; + +export const FormSchema = z.object(RetrievalPartialSchema); + +export type DataOperationsFormSchemaType = z.infer; + +const outputList = buildOutputList(initialDataOperationsValues.outputs); + +function VariableAssignerForm({ node }: INextOperatorForm) { + const { t } = useTranslation(); + + const defaultValues = useFormValues(initialDataOperationsValues, node); + + const form = useForm({ + defaultValues: defaultValues, + mode: 'onChange', + resolver: zodResolver(FormSchema), + shouldUnregister: true, + }); + + const OperationsOptions = buildOptions( + Operations, + t, + `flow.operationsOptions`, + true, + ); + + useWatchFormChange(node?.id, form, true); + + return ( +
+ + + + + + + + + +
+ ); +} + +export default memo(VariableAssignerForm); diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 81630eb39..ed092a01b 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -48,6 +48,7 @@ import { initialTokenizerValues, initialTuShareValues, initialUserFillUpValues, + initialVariableAggregatorValues, initialVariableAssignerValues, initialWaitingDialogueValues, initialWenCaiValues, @@ -129,6 +130,7 @@ export const useInitializeOperatorParams = () => { }, [Operator.DataOperations]: initialDataOperationsValues, [Operator.VariableAssigner]: initialVariableAssignerValues, + [Operator.VariableAggregator]: initialVariableAggregatorValues, }; }, [llmId]); diff --git a/web/src/pages/agent/log-sheet/index.tsx b/web/src/pages/agent/log-sheet/index.tsx index 138a53e09..76c0b0865 100644 --- a/web/src/pages/agent/log-sheet/index.tsx +++ b/web/src/pages/agent/log-sheet/index.tsx @@ -25,7 +25,10 @@ export function LogSheet({ }: LogSheetProps) { return ( - + e.preventDefault()} + > diff --git a/web/src/pages/agent/operator-icon.tsx b/web/src/pages/agent/operator-icon.tsx index 6a0ae7f7d..a7ece8ead 100644 --- a/web/src/pages/agent/operator-icon.tsx +++ b/web/src/pages/agent/operator-icon.tsx @@ -14,7 +14,7 @@ import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.s import { IconFont } from '@/components/icon-font'; import { cn } from '@/lib/utils'; -import { Equal, FileCode, HousePlus } from 'lucide-react'; +import { Equal, FileCode, HousePlus, Variable } from 'lucide-react'; import { Operator } from './constant'; interface IProps { @@ -55,10 +55,10 @@ export const SVGIconMap = { [Operator.WenCai]: WenCaiIcon, [Operator.Crawler]: CrawlerIcon, }; - export const LucideIconMap = { [Operator.DataOperations]: FileCode, [Operator.VariableAssigner]: Equal, + [Operator.VariableAggregator]: Variable, }; const Empty = () => {