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

@ -28,7 +28,11 @@ import {
useState,
} from 'react';
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 { useCreateCategorizeFormSchema } from './use-form-schema';
interface IProps {
nodeId?: string;
@ -155,6 +159,12 @@ const InnerFormSet = ({ index }: IProps & { index: number }) => {
</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>
</section>
);
@ -164,21 +174,38 @@ const FormSet = memo(InnerFormSet);
const DynamicCategorize = ({ nodeId }: IProps) => {
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 { fields, remove, append } = useFieldArray({
name: 'items',
control: form.control,
});
const handleAdd = () => {
const handleAdd = useCallback(() => {
append({
name: humanId(),
description: '',
uuid: uuid(),
examples: [{ value: '' }],
});
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 (
<div className="flex flex-col gap-4 ">
@ -194,7 +221,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
variant="ghost"
size="sm"
className="w-9 p-0"
onClick={() => remove(index)}
onClick={handleRemove(index)}
>
<X className="h-4 w-4" />
</Button>

View File

@ -1,50 +1,26 @@
import { FormContainer } from '@/components/form-container';
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 { Form } from '@/components/ui/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 { initialCategorizeValues } from '../../constant';
import { INextOperatorForm } from '../../interface';
import { buildOutputList } from '../../utils/build-output-list';
import { Output } from '../components/output';
import { QueryVariable } from '../components/query-variable';
import DynamicCategorize from './dynamic-categorize';
import { useCreateCategorizeFormSchema } from './use-form-schema';
import { useValues } from './use-values';
import { useWatchFormChange } from './use-watch-change';
const outputList = buildOutputList(initialCategorizeValues.outputs);
function CategorizeForm({ node }: INextOperatorForm) {
const { t } = useTranslation();
const values = useValues(node);
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(),
examples: z
.array(
z.object({
value: z.string(),
}),
)
.optional(),
})
.optional(),
),
});
const FormSchema = useCreateCategorizeFormSchema();
const form = useForm({
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 { RAGFlowNodeType } from '@/interfaces/database/flow';
import { get, isEmpty, isPlainObject, omit } from 'lodash';
import { get, isEmpty, isPlainObject } from 'lodash';
import { useMemo } from 'react';
import { buildCategorizeListFromObject } from '../../utils';
@ -25,12 +25,12 @@ export function useValues(node?: RAGFlowNodeType) {
get(node, 'data.form.category_description', {}),
);
if (isPlainObject(formData)) {
const nextValues = {
...omit(formData, 'category_description'),
items,
};
// const nextValues = {
// ...omit(formData, 'category_description'),
// items,
// };
return nextValues;
return formData;
}
}, [node]);

View File

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

View File

@ -24,7 +24,7 @@ export function Output({ list }: OutputProps) {
key={idx}
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>
))}
</ul>