Fix: Fixed the issue that the condition of deleting the classification operator cannot be connected anymore #3221 (#9068)

### What problem does this PR solve?
Fix: Fixed the issue that the condition of deleting the classification
operator cannot be connected anymore #3221
### Type of change



- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
balibabu
2025-07-28 14:16:20 +08:00
committed by GitHub
parent 905dab22a6
commit cc0227cf6e
16 changed files with 150 additions and 146 deletions

View File

@ -4,11 +4,12 @@ export interface ICategorizeItem {
examples?: { value: string }[]; examples?: { value: string }[];
index: number; index: number;
to: string[]; to: string[];
uuid: string;
} }
export type ICategorizeItemResult = Record< export type ICategorizeItemResult = Record<
string, string,
Omit<ICategorizeItem, 'name' | 'examples'> & { examples: string[] } Omit<ICategorizeItem, 'name' | 'examples' | 'uuid'> & { examples: string[] }
>; >;
export interface ISwitchCondition { export interface ISwitchCondition {
@ -101,6 +102,7 @@ export interface IGenerateForm {
export interface ICategorizeForm extends IGenerateForm { export interface ICategorizeForm extends IGenerateForm {
category_description: ICategorizeItemResult; category_description: ICategorizeItemResult;
items: ICategorizeItem[];
} }
export interface IRelevantForm extends IGenerateForm { export interface IRelevantForm extends IGenerateForm {

View File

@ -5,6 +5,7 @@ import {
EdgeProps, EdgeProps,
getBezierPath, getBezierPath,
} from '@xyflow/react'; } from '@xyflow/react';
import { memo } from 'react';
import useGraphStore from '../../store'; import useGraphStore from '../../store';
import { useFetchAgent } from '@/hooks/use-agent-request'; import { useFetchAgent } from '@/hooks/use-agent-request';
@ -12,7 +13,7 @@ import { cn } from '@/lib/utils';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { NodeHandleId, Operator } from '../../constant'; import { NodeHandleId, Operator } from '../../constant';
export function ButtonEdge({ function InnerButtonEdge({
id, id,
sourceX, sourceX,
sourceY, sourceY,
@ -77,7 +78,8 @@ export function ButtonEdge({
const visible = useMemo(() => { const visible = useMemo(() => {
return ( return (
data?.isHovered && data?.isHovered &&
sourceHandleId !== NodeHandleId.Tool && // The connection between the agent node and the tool node does not need to display the delete button sourceHandleId !== NodeHandleId.Tool &&
sourceHandleId !== NodeHandleId.AgentBottom && // The connection between the agent node and the tool node does not need to display the delete button
!target.startsWith(Operator.Tool) !target.startsWith(Operator.Tool)
); );
}, [data?.isHovered, sourceHandleId, target]); }, [data?.isHovered, sourceHandleId, target]);
@ -120,3 +122,5 @@ export function ButtonEdge({
</> </>
); );
} }
export const ButtonEdge = memo(InnerButtonEdge);

View File

@ -1,40 +0,0 @@
import { Handle, Position } from '@xyflow/react';
import React, { memo } from 'react';
import styles from './index.less';
const DEFAULT_HANDLE_STYLE = {
width: 6,
height: 6,
bottom: -5,
fontSize: 8,
};
interface IProps extends React.PropsWithChildren {
top: number;
right: number;
id: string;
idx?: number;
}
const CategorizeHandle = ({ top, right, id, children }: IProps) => {
return (
<Handle
type="source"
position={Position.Right}
id={id}
isConnectable
style={{
...DEFAULT_HANDLE_STYLE,
top: `${top}%`,
right: `${right}%`,
background: 'red',
color: 'black',
}}
>
<span className={styles.categorizeAnchorPointText}>{children || id}</span>
</Handle>
);
};
export default memo(CategorizeHandle);

View File

@ -34,15 +34,15 @@ export function InnerCategorizeNode({
<div className={'bg-background-card rounded-sm px-1'}> <div className={'bg-background-card rounded-sm px-1'}>
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
</div> </div>
{positions.map((position, idx) => { {positions.map((position) => {
return ( return (
<div key={idx}> <div key={position.uuid}>
<div className={'bg-background-card rounded-sm p-1'}> <div className={'bg-background-card rounded-sm p-1 truncate'}>
{position.text} {position.name}
</div> </div>
<CommonHandle <CommonHandle
key={position.text} // key={position.text}
id={position.text} id={position.uuid}
type="source" type="source"
position={Position.Right} position={Position.Right}
isConnectable isConnectable

View File

@ -1,8 +1,9 @@
import { ICategorizeItemResult } from '@/interfaces/database/agent';
import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { useUpdateNodeInternals } from '@xyflow/react'; import { useUpdateNodeInternals } from '@xyflow/react';
import { get } from 'lodash'; import { get } from 'lodash';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { z } from 'zod';
import { useCreateCategorizeFormSchema } from '../../form/categorize-form/use-form-schema';
export const useBuildCategorizeHandlePositions = ({ export const useBuildCategorizeHandlePositions = ({
data, data,
@ -13,33 +14,35 @@ export const useBuildCategorizeHandlePositions = ({
}) => { }) => {
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const categoryData: ICategorizeItemResult = useMemo(() => { const FormSchema = useCreateCategorizeFormSchema();
return get(data, `form.category_description`, {});
type FormSchemaType = z.infer<typeof FormSchema>;
const items: Required<FormSchemaType['items']> = useMemo(() => {
return get(data, `form.items`, []);
}, [data]); }, [data]);
const positions = useMemo(() => { const positions = useMemo(() => {
const list: Array<{ const list: Array<{
text: string;
top: number; top: number;
idx: number; name: string;
}> = []; uuid: string;
}> &
Required<FormSchemaType['items']> = [];
Object.keys(categoryData) items.forEach((x, idx) => {
.sort((a, b) => categoryData[a].index - categoryData[b].index) list.push({
.forEach((x, idx) => { ...x,
list.push({ top: idx === 0 ? 86 : list[idx - 1].top + 8 + 24,
text: x,
idx,
top: idx === 0 ? 86 : list[idx - 1].top + 8 + 24,
});
}); });
});
return list; return list;
}, [categoryData]); }, [items]);
useEffect(() => { useEffect(() => {
updateNodeInternals(id); updateNodeInternals(id);
}, [id, updateNodeInternals, categoryData]); }, [id, updateNodeInternals, items]);
return { positions }; return { positions };
}; };

View File

@ -321,7 +321,7 @@ export const initialCategorizeValues = {
query: AgentGlobals.SysQuery, query: AgentGlobals.SysQuery,
parameter: ModelVariableType.Precise, parameter: ModelVariableType.Precise,
message_history_window_size: 1, message_history_window_size: 1,
category_description: {}, items: [],
outputs: { outputs: {
category_name: { category_name: {
type: 'string', type: 'string',
@ -760,6 +760,7 @@ export const RestrictedUpstreamMap = {
[Operator.TavilyExtract]: [Operator.Begin], [Operator.TavilyExtract]: [Operator.Begin],
[Operator.StringTransform]: [Operator.Begin], [Operator.StringTransform]: [Operator.Begin],
[Operator.UserFillUp]: [Operator.Begin], [Operator.UserFillUp]: [Operator.Begin],
[Operator.Tool]: [Operator.Begin],
}; };
export const NodeMap = { export const NodeMap = {

View File

@ -28,7 +28,11 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form'; import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form';
import { v4 as uuid } from 'uuid';
import { z } from 'zod';
import useGraphStore from '../../store';
import DynamicExample from './dynamic-example'; import DynamicExample from './dynamic-example';
import { useCreateCategorizeFormSchema } from './use-form-schema';
interface IProps { interface IProps {
nodeId?: string; nodeId?: string;
@ -155,6 +159,12 @@ const InnerFormSet = ({ index }: IProps & { index: number }) => {
</FormItem> </FormItem>
)} )}
/> />
{/* Create a hidden field to make Form instance record this */}
<FormField
control={form.control}
name={'uuid'}
render={() => <div></div>}
/>
<DynamicExample name={buildFieldName('examples')}></DynamicExample> <DynamicExample name={buildFieldName('examples')}></DynamicExample>
</section> </section>
); );
@ -164,21 +174,38 @@ const FormSet = memo(InnerFormSet);
const DynamicCategorize = ({ nodeId }: IProps) => { const DynamicCategorize = ({ nodeId }: IProps) => {
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const form = useFormContext(); const FormSchema = useCreateCategorizeFormSchema();
const deleteCategorizeCaseEdges = useGraphStore(
(state) => state.deleteCategorizeCaseEdges,
);
const form = useFormContext<z.infer<typeof FormSchema>>();
const { t } = useTranslate('flow'); const { t } = useTranslate('flow');
const { fields, remove, append } = useFieldArray({ const { fields, remove, append } = useFieldArray({
name: 'items', name: 'items',
control: form.control, control: form.control,
}); });
const handleAdd = () => { const handleAdd = useCallback(() => {
append({ append({
name: humanId(), name: humanId(),
description: '', description: '',
uuid: uuid(),
examples: [{ value: '' }], examples: [{ value: '' }],
}); });
if (nodeId) updateNodeInternals(nodeId); if (nodeId) updateNodeInternals(nodeId);
}; }, [append, nodeId, updateNodeInternals]);
const handleRemove = useCallback(
(index: number) => () => {
remove(index);
if (nodeId) {
const uuid = fields[index].uuid;
deleteCategorizeCaseEdges(nodeId, uuid);
}
},
[deleteCategorizeCaseEdges, fields, nodeId, remove],
);
return ( return (
<div className="flex flex-col gap-4 "> <div className="flex flex-col gap-4 ">
@ -194,7 +221,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
variant="ghost" variant="ghost"
size="sm" size="sm"
className="w-9 p-0" className="w-9 p-0"
onClick={() => remove(index)} onClick={handleRemove(index)}
> >
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>

View File

@ -1,50 +1,26 @@
import { FormContainer } from '@/components/form-container'; import { FormContainer } from '@/components/form-container';
import { LargeModelFormField } from '@/components/large-model-form-field'; import { LargeModelFormField } from '@/components/large-model-form-field';
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
import { Form } from '@/components/ui/form'; import { Form } from '@/components/ui/form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { memo } from 'react'; import { memo } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { initialCategorizeValues } from '../../constant'; import { initialCategorizeValues } from '../../constant';
import { INextOperatorForm } from '../../interface'; import { INextOperatorForm } from '../../interface';
import { buildOutputList } from '../../utils/build-output-list'; import { buildOutputList } from '../../utils/build-output-list';
import { Output } from '../components/output'; import { Output } from '../components/output';
import { QueryVariable } from '../components/query-variable'; import { QueryVariable } from '../components/query-variable';
import DynamicCategorize from './dynamic-categorize'; import DynamicCategorize from './dynamic-categorize';
import { useCreateCategorizeFormSchema } from './use-form-schema';
import { useValues } from './use-values'; import { useValues } from './use-values';
import { useWatchFormChange } from './use-watch-change'; import { useWatchFormChange } from './use-watch-change';
const outputList = buildOutputList(initialCategorizeValues.outputs); const outputList = buildOutputList(initialCategorizeValues.outputs);
function CategorizeForm({ node }: INextOperatorForm) { function CategorizeForm({ node }: INextOperatorForm) {
const { t } = useTranslation();
const values = useValues(node); const values = useValues(node);
const FormSchema = z.object({ const FormSchema = useCreateCategorizeFormSchema();
query: z.string().optional(),
parameter: z.string().optional(),
...LlmSettingSchema,
message_history_window_size: z.coerce.number(),
items: z.array(
z
.object({
name: z.string().min(1, t('flow.nameMessage')).trim(),
description: z.string().optional(),
examples: z
.array(
z.object({
value: z.string(),
}),
)
.optional(),
})
.optional(),
),
});
const form = useForm({ const form = useForm({
defaultValues: values, defaultValues: values,

View File

@ -0,0 +1,32 @@
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
export function useCreateCategorizeFormSchema() {
const { t } = useTranslation();
const FormSchema = z.object({
query: z.string().optional(),
parameter: z.string().optional(),
...LlmSettingSchema,
message_history_window_size: z.coerce.number(),
items: z.array(
z
.object({
name: z.string().min(1, t('flow.nameMessage')).trim(),
description: z.string().optional(),
uuid: z.string(),
examples: z
.array(
z.object({
value: z.string(),
}),
)
.optional(),
})
.optional(),
),
});
return FormSchema;
}

View File

@ -1,6 +1,6 @@
import { ModelVariableType } from '@/constants/knowledge'; import { ModelVariableType } from '@/constants/knowledge';
import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { get, isEmpty, isPlainObject, omit } from 'lodash'; import { get, isEmpty, isPlainObject } from 'lodash';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { buildCategorizeListFromObject } from '../../utils'; import { buildCategorizeListFromObject } from '../../utils';
@ -25,12 +25,12 @@ export function useValues(node?: RAGFlowNodeType) {
get(node, 'data.form.category_description', {}), get(node, 'data.form.category_description', {}),
); );
if (isPlainObject(formData)) { if (isPlainObject(formData)) {
const nextValues = { // const nextValues = {
...omit(formData, 'category_description'), // ...omit(formData, 'category_description'),
items, // items,
}; // };
return nextValues; return formData;
} }
}, [node]); }, [node]);

View File

@ -1,8 +1,6 @@
import { omit } from 'lodash';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { UseFormReturn, useWatch } from 'react-hook-form'; import { UseFormReturn, useWatch } from 'react-hook-form';
import useGraphStore from '../../store'; import useGraphStore from '../../store';
import { buildCategorizeObjectFromList } from '../../utils';
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) { export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
let values = useWatch({ control: form?.control }); let values = useWatch({ control: form?.control });
@ -10,21 +8,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
useEffect(() => { useEffect(() => {
// Manually triggered form updates are synchronized to the canvas // Manually triggered form updates are synchronized to the canvas
if (id && form?.formState.isDirty) { if (id) {
values = form?.getValues(); values = form?.getValues();
let nextValues: any = values;
const categoryDescription = Array.isArray(values.items) updateNodeForm(id, { ...values, items: values.items?.slice() || [] });
? buildCategorizeObjectFromList(values.items)
: {};
if (categoryDescription) {
nextValues = {
...omit(values, 'items'),
category_description: categoryDescription,
};
}
updateNodeForm(id, nextValues);
} }
}, [form?.formState.isDirty, id, updateNodeForm, values]); }, [id, updateNodeForm, values]);
} }

View File

@ -24,7 +24,7 @@ export function Output({ list }: OutputProps) {
key={idx} key={idx}
className="bg-background-highlight text-background-checked rounded-sm px-2 py-1" className="bg-background-highlight text-background-checked rounded-sm px-2 py-1"
> >
{x.title}: {x.type} {x.title}: <span className="text-text-sub-title">{x.type}</span>
</li> </li>
))} ))}
</ul> </ul>

View File

@ -49,6 +49,7 @@ import {
initialRetrievalValues, initialRetrievalValues,
initialRewriteQuestionValues, initialRewriteQuestionValues,
initialSwitchValues, initialSwitchValues,
initialTavilyExtractValues,
initialTavilyValues, initialTavilyValues,
initialTemplateValues, initialTemplateValues,
initialTuShareValues, initialTuShareValues,
@ -135,6 +136,7 @@ export const useInitializeOperatorParams = () => {
[Operator.WaitingDialogue]: initialWaitingDialogueValues, [Operator.WaitingDialogue]: initialWaitingDialogueValues,
[Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, [Operator.Agent]: { ...initialAgentValues, llm_id: llmId },
[Operator.TavilySearch]: initialTavilyValues, [Operator.TavilySearch]: initialTavilyValues,
[Operator.TavilyExtract]: initialTavilyExtractValues,
}; };
}, [llmId]); }, [llmId]);
@ -331,7 +333,7 @@ export const useHandleFormValuesChange = (
}; };
export const useValidateConnection = () => { export const useValidateConnection = () => {
const { edges, getOperatorTypeFromId, getParentIdById } = useGraphStore( const { getOperatorTypeFromId, getParentIdById } = useGraphStore(
(state) => state, (state) => state,
); );
@ -354,20 +356,19 @@ export const useValidateConnection = () => {
const isSelfConnected = connection.target === connection.source; const isSelfConnected = connection.target === connection.source;
// limit the connection between two nodes to only one connection line in one direction // limit the connection between two nodes to only one connection line in one direction
const hasLine = edges.some( // const hasLine = edges.some(
(x) => x.source === connection.source && x.target === connection.target, // (x) => x.source === connection.source && x.target === connection.target,
); // );
const ret = const ret =
!isSelfConnected && !isSelfConnected &&
!hasLine &&
RestrictedUpstreamMap[ RestrictedUpstreamMap[
getOperatorTypeFromId(connection.source) as Operator getOperatorTypeFromId(connection.source) as Operator
]?.every((x) => x !== getOperatorTypeFromId(connection.target)) && ]?.every((x) => x !== getOperatorTypeFromId(connection.target)) &&
isSameNodeChild(connection); isSameNodeChild(connection);
return ret; return ret;
}, },
[edges, getOperatorTypeFromId, isSameNodeChild], [getOperatorTypeFromId, isSameNodeChild],
); );
return isValidConnection; return isValidConnection;

View File

@ -84,6 +84,7 @@ export type RFState = {
setClickedNodeId: (id?: string) => void; setClickedNodeId: (id?: string) => void;
setClickedToolId: (id?: string) => void; setClickedToolId: (id?: string) => void;
findUpstreamNodeById: (id?: string | null) => RAGFlowNodeType | undefined; findUpstreamNodeById: (id?: string | null) => RAGFlowNodeType | undefined;
deleteCategorizeCaseEdges: (source: string, sourceHandle: string) => void; // Deleting a condition of a classification operator will delete the related edge
}; };
// this is our useStore hook that we can use in our components to get parts of the store and call actions // this is our useStore hook that we can use in our components to get parts of the store and call actions
@ -307,14 +308,14 @@ const useGraphStore = create<RFState>()(
[sourceHandle as string]: undefined, [sourceHandle as string]: undefined,
}); });
break; break;
case Operator.Categorize: // case Operator.Categorize:
if (sourceHandle) // if (sourceHandle)
updateNodeForm(source, undefined, [ // updateNodeForm(source, undefined, [
'category_description', // 'category_description',
sourceHandle, // sourceHandle,
'to', // 'to',
]); // ]);
break; // break;
case Operator.Switch: { case Operator.Switch: {
updateSwitchFormData(source, sourceHandle, target, false); updateSwitchFormData(source, sourceHandle, target, false);
break; break;
@ -508,6 +509,15 @@ const useGraphStore = create<RFState>()(
const edge = edges.find((x) => x.target === id); const edge = edges.find((x) => x.target === id);
return getNode(edge?.source); return getNode(edge?.source);
}, },
deleteCategorizeCaseEdges: (source, sourceHandle) => {
const { edges, setEdges } = get();
setEdges(
edges.filter(
(edge) =>
!(edge.source === source && edge.sourceHandle === sourceHandle),
),
);
},
})), })),
{ name: 'graph', trace: true }, { name: 'graph', trace: true },
), ),

View File

@ -13,13 +13,12 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { RAGFlowSelect } from '@/components/ui/select';
import { FileMimeType, Platform } from '@/constants/common'; import { FileMimeType, Platform } from '@/constants/common';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { TagRenameId } from '@/pages/add-knowledge/constant'; import { TagRenameId } from '@/pages/add-knowledge/constant';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const options = Object.values(Platform).map((x) => ({ label: x, value: x })); // const options = Object.values(Platform).map((x) => ({ label: x, value: x }));
export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) { export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
const { t } = useTranslation(); const { t } = useTranslation();
@ -72,7 +71,7 @@ export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
</FormItem> </FormItem>
)} )}
/> />
<FormField {/* <FormField
control={form.control} control={form.control}
name="platform" name="platform"
render={({ field }) => ( render={({ field }) => (
@ -84,7 +83,7 @@ export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> /> */}
</form> </form>
</Form> </Form>
); );

View File

@ -159,7 +159,7 @@ function buildAgentTools(edges: Edge[], nodes: Node[], nodeId: string) {
return { return {
component_name: Operator.Agent, component_name: Operator.Agent,
id, id,
name, name: name as string, // Cast name to string and provide fallback
params: { ...formData }, params: { ...formData },
}; };
}), }),
@ -172,27 +172,29 @@ function filterTargetsBySourceHandleId(edges: Edge[], handleId: string) {
return edges.filter((x) => x.sourceHandle === handleId).map((x) => x.target); return edges.filter((x) => x.sourceHandle === handleId).map((x) => x.target);
} }
function buildCategorizeTos(edges: Edge[], nodes: Node[], nodeId: string) { function buildCategorize(edges: Edge[], nodes: Node[], nodeId: string) {
const node = nodes.find((x) => x.id === nodeId); const node = nodes.find((x) => x.id === nodeId);
const params = { ...(node?.data.form ?? {}) } as ICategorizeForm; const params = { ...(node?.data.form ?? {}) } as ICategorizeForm;
if (node && node.data.label === Operator.Categorize) { if (node && node.data.label === Operator.Categorize) {
const subEdges = edges.filter((x) => x.source === nodeId); const subEdges = edges.filter((x) => x.source === nodeId);
const categoryDescription = params.category_description || {}; const items = params.items || [];
const nextCategoryDescription = Object.entries(categoryDescription).reduce< const nextCategoryDescription = items.reduce<
ICategorizeForm['category_description'] ICategorizeForm['category_description']
>((pre, [key, val]) => { >((pre, val) => {
const key = val.name;
pre[key] = { pre[key] = {
...val, ...omit(val, 'name', 'uuid'),
to: filterTargetsBySourceHandleId(subEdges, key), examples: val.examples?.map((x) => x.value) || [],
to: filterTargetsBySourceHandleId(subEdges, val.uuid),
}; };
return pre; return pre;
}, {}); }, {});
params.category_description = nextCategoryDescription; params.category_description = nextCategoryDescription;
} }
return params; return omit(params, 'items');
} }
const buildOperatorParams = (operatorName: string) => const buildOperatorParams = (operatorName: string) =>
@ -236,7 +238,7 @@ export const buildDslComponentsByGraph = (
break; break;
} }
case Operator.Categorize: case Operator.Categorize:
params = buildCategorizeTos(edges, nodes, id); params = buildCategorize(edges, nodes, id);
break; break;
default: default: