mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 12:32:30 +08:00
### What problem does this PR solve? Feat: Add variable aggregator node #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -110,6 +110,7 @@ export enum Operator {
|
||||
Placeholder = 'Placeholder',
|
||||
DataOperations = 'DataOperations',
|
||||
VariableAssigner = 'VariableAssigner',
|
||||
VariableAggregator = 'VariableAggregator',
|
||||
File = 'File', // pipeline
|
||||
Parser = 'Parser',
|
||||
Tokenizer = 'Tokenizer',
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -1471,6 +1471,9 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
variableAssigner: '变量赋值器',
|
||||
variableAssignerDescription:
|
||||
'此组件对数据对象执行操作,包括提取、筛选和编辑数据中的键和值。',
|
||||
variableAggregator: '变量聚合',
|
||||
variableAggregatorDescription: `将多路分支的变量聚合为一个变量,以实现下游节点统一配置。
|
||||
变量聚合节点(原变量赋值节点)是工作流程中的一个关键节点,它负责整合不同分支的输出结果,确保无论哪个分支被执行,其结果都能通过一个统一的变量来引用和访问。这在多分支的情况下非常有用,可将不同分支下相同作用的变量映射为一个输出变量,避免下游节点重复定义。`,
|
||||
inputVariables: '输入变量',
|
||||
addVariable: '新增变量',
|
||||
runningHintText: '正在运行中...🕞',
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -80,6 +80,7 @@ export function AccordionOperators({
|
||||
Operator.StringTransform,
|
||||
Operator.DataOperations,
|
||||
Operator.VariableAssigner,
|
||||
Operator.VariableAggregator,
|
||||
]}
|
||||
isCustomDropdown={isCustomDropdown}
|
||||
mousePosition={mousePosition}
|
||||
|
||||
11
web/src/pages/agent/canvas/node/variable-aggregator-node.tsx
Normal file
11
web/src/pages/agent/canvas/node/variable-aggregator-node.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { IRagNode } from '@/interfaces/database/agent';
|
||||
import { NodeProps } from '@xyflow/react';
|
||||
import { RagNode } from '.';
|
||||
|
||||
export function VariableAggregatorNode({ ...props }: NodeProps<IRagNode>) {
|
||||
return (
|
||||
<RagNode {...props}>
|
||||
<section>VariableAggregatorNode</section>
|
||||
</RagNode>
|
||||
);
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
100
web/src/pages/agent/form/variable-assigner-form/index.tsx
Normal file
100
web/src/pages/agent/form/variable-assigner-form/index.tsx
Normal file
@ -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<typeof FormSchema>;
|
||||
|
||||
const outputList = buildOutputList(initialDataOperationsValues.outputs);
|
||||
|
||||
function VariableAssignerForm({ node }: INextOperatorForm) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const defaultValues = useFormValues(initialDataOperationsValues, node);
|
||||
|
||||
const form = useForm<DataOperationsFormSchemaType>({
|
||||
defaultValues: defaultValues,
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(FormSchema),
|
||||
shouldUnregister: true,
|
||||
});
|
||||
|
||||
const OperationsOptions = buildOptions(
|
||||
Operations,
|
||||
t,
|
||||
`flow.operationsOptions`,
|
||||
true,
|
||||
);
|
||||
|
||||
useWatchFormChange(node?.id, form, true);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<QueryVariableList
|
||||
tooltip={t('flow.queryTip')}
|
||||
label={t('flow.query')}
|
||||
types={[JsonSchemaDataType.Array, JsonSchemaDataType.Object]}
|
||||
></QueryVariableList>
|
||||
<Separator />
|
||||
<RAGFlowFormItem name="operations" label={t('flow.operations')}>
|
||||
<SelectWithSearch options={OperationsOptions} allowClear />
|
||||
</RAGFlowFormItem>
|
||||
|
||||
<Output list={outputList} isFormRequired></Output>
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(VariableAssignerForm);
|
||||
@ -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]);
|
||||
|
||||
|
||||
@ -25,7 +25,10 @@ export function LogSheet({
|
||||
}: LogSheetProps) {
|
||||
return (
|
||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||
<SheetContent className={cn('top-20 right-[620px]')}>
|
||||
<SheetContent
|
||||
className={cn('top-20 right-[620px]')}
|
||||
onInteractOutside={(e) => e.preventDefault()}
|
||||
>
|
||||
<SheetHeader>
|
||||
<SheetTitle className="flex items-center gap-1">
|
||||
<NotebookText className="size-4" />
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
Reference in New Issue
Block a user