Skip to content

Commit 2ab5c4e

Browse files
committed
Feat: Use one-way data flow to synchronize the form data to the canvas #3221
1 parent bd4678b commit 2ab5c4e

File tree

16 files changed

+462
-92
lines changed

16 files changed

+462
-92
lines changed

web/src/components/llm-setting-items/use-watch-change.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,13 @@ export function useHandleFreedomChange() {
2323
updateNodeForm(node?.id, nextValues);
2424
}
2525

26-
console.info('xx:', node);
26+
for (const key in values) {
27+
if (Object.prototype.hasOwnProperty.call(values, key)) {
28+
const element = values[key];
2729

28-
// form.reset({ ...currentValues, ...values });
29-
30-
// for (const key in values) {
31-
// if (Object.prototype.hasOwnProperty.call(values, key)) {
32-
// const element = values[key];
33-
34-
// form.setValue(key, element);
35-
// }
36-
// }
30+
form.setValue(key, element);
31+
}
32+
}
3733
},
3834
[form, node, updateNodeForm],
3935
);

web/src/pages/agent/form-sheet/next.tsx

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,20 @@ import { IModalProps } from '@/interfaces/common';
1010
import { RAGFlowNodeType } from '@/interfaces/database/flow';
1111
import { cn } from '@/lib/utils';
1212
import { zodResolver } from '@hookform/resolvers/zod';
13-
import { get, isPlainObject, lowerFirst } from 'lodash';
14-
import omit from 'lodash/omit';
13+
import { lowerFirst } from 'lodash';
1514
import { Play, X } from 'lucide-react';
16-
import { useEffect, useRef } from 'react';
15+
import { useRef } from 'react';
1716
import { useForm } from 'react-hook-form';
1817
import { BeginId, Operator, operatorMap } from '../constant';
1918
import { FlowFormContext } from '../context';
2019
import { RunTooltip } from '../flow-tooltip';
2120
import { useHandleNodeNameChange } from '../hooks';
2221
import { useHandleFormValuesChange } from '../hooks/use-watch-form-change';
2322
import OperatorIcon from '../operator-icon';
24-
import {
25-
buildCategorizeListFromObject,
26-
convertToObjectArray,
27-
needsSingleStepDebugging,
28-
} from '../utils';
23+
import { needsSingleStepDebugging } from '../utils';
2924
import SingleDebugDrawer from './single-debug-drawer';
3025
import { useFormConfigMap } from './use-form-config-map';
26+
import { useValues } from './use-values';
3127

3228
interface IProps {
3329
node?: RAGFlowNodeType;
@@ -54,8 +50,10 @@ const FormSheet = ({
5450

5551
const OperatorForm = currentFormMap.component ?? EmptyContent;
5652

53+
const values = useValues(node);
54+
5755
const form = useForm({
58-
values: currentFormMap.defaultValues,
56+
values: values,
5957
resolver: zodResolver(currentFormMap.schema),
6058
});
6159

@@ -74,43 +72,39 @@ const FormSheet = ({
7472
form,
7573
);
7674

77-
useEffect(() => {
78-
if (visible) {
79-
if (node?.id !== previousId.current) {
80-
form.reset();
81-
form.clearErrors();
82-
}
83-
84-
const formData = node?.data?.form;
85-
86-
if (operatorName === Operator.Categorize) {
87-
const items = buildCategorizeListFromObject(
88-
get(node, 'data.form.category_description', {}),
89-
);
90-
if (isPlainObject(formData)) {
91-
// form.setFieldsValue({ ...formData, items });
92-
console.info('xxx');
93-
const nextValues = {
94-
...omit(formData, 'category_description'),
95-
items,
96-
};
97-
// Object.entries(nextValues).forEach(([key, value]) => {
98-
// form.setValue(key, value, { shouldDirty: false });
99-
// });
100-
form.reset(nextValues);
101-
}
102-
} else if (operatorName === Operator.Message) {
103-
form.reset({
104-
...formData,
105-
content: convertToObjectArray(formData.content),
106-
});
107-
} else {
108-
// form.setFieldsValue(node?.data?.form);
109-
form.reset(node?.data?.form);
110-
}
111-
previousId.current = node?.id;
112-
}
113-
}, [visible, form, node?.data?.form, node?.id, node, operatorName]);
75+
// useEffect(() => {
76+
// if (visible && !form.formState.isDirty) {
77+
// // if (node?.id !== previousId.current) {
78+
// // form.reset();
79+
// // form.clearErrors();
80+
// // }
81+
82+
// const formData = node?.data?.form;
83+
84+
// if (operatorName === Operator.Categorize) {
85+
// const items = buildCategorizeListFromObject(
86+
// get(node, 'data.form.category_description', {}),
87+
// );
88+
// if (isPlainObject(formData)) {
89+
// console.info('xxx');
90+
// const nextValues = {
91+
// ...omit(formData, 'category_description'),
92+
// items,
93+
// };
94+
95+
// form.reset(nextValues);
96+
// }
97+
// } else if (operatorName === Operator.Message) {
98+
// form.reset({
99+
// ...formData,
100+
// content: convertToObjectArray(formData.content),
101+
// });
102+
// } else {
103+
// form.reset(node?.data?.form);
104+
// }
105+
// previousId.current = node?.id;
106+
// }
107+
// }, [visible, form, node?.data?.form, node?.id, node, operatorName]);
114108

115109
return (
116110
<Sheet open={visible} modal={false}>

web/src/pages/agent/form-sheet/use-form-config-map.tsx

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
22
import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent';
3-
import { ModelVariableType } from '@/constants/knowledge';
43
import { useTranslation } from 'react-i18next';
54
import { z } from 'zod';
6-
import { AgentDialogueMode, Operator } from '../constant';
5+
import { Operator } from '../constant';
76
import AkShareForm from '../form/akshare-form';
87
import AnswerForm from '../form/answer-form';
98
import ArXivForm from '../form/arxiv-form';
@@ -45,11 +44,7 @@ export function useFormConfigMap() {
4544
const FormConfigMap = {
4645
[Operator.Begin]: {
4746
component: BeginForm,
48-
defaultValues: {
49-
enablePrologue: true,
50-
prologue: t('chat.setAnOpenerInitial'),
51-
mode: AgentDialogueMode.Conversational,
52-
},
47+
defaultValues: {},
5348
schema: z.object({
5449
enablePrologue: z.boolean().optional(),
5550
prologue: z
@@ -116,16 +111,7 @@ export function useFormConfigMap() {
116111
},
117112
[Operator.Categorize]: {
118113
component: CategorizeForm,
119-
defaultValues: {
120-
parameter: ModelVariableType.Precise,
121-
message_history_window_size: 1,
122-
temperatureEnabled: true,
123-
topPEnabled: true,
124-
presencePenaltyEnabled: true,
125-
frequencyPenaltyEnabled: true,
126-
maxTokensEnabled: true,
127-
items: [],
128-
},
114+
defaultValues: {},
129115
schema: z.object({
130116
parameter: z.string().optional(),
131117
...LlmSettingSchema,
@@ -149,9 +135,7 @@ export function useFormConfigMap() {
149135
},
150136
[Operator.Message]: {
151137
component: MessageForm,
152-
defaultValues: {
153-
content: [],
154-
},
138+
defaultValues: {},
155139
schema: z.object({
156140
content: z
157141
.array(
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { RAGFlowNodeType } from '@/interfaces/database/flow';
2+
import { get, isEmpty, isPlainObject, omit } from 'lodash';
3+
import { useMemo, useRef } from 'react';
4+
import { Operator } from '../constant';
5+
import { buildCategorizeListFromObject, convertToObjectArray } from '../utils';
6+
import { useFormConfigMap } from './use-form-config-map';
7+
8+
export function useValues(node?: RAGFlowNodeType, isDirty?: boolean) {
9+
const operatorName: Operator = node?.data.label as Operator;
10+
const previousId = useRef<string | undefined>(node?.id);
11+
12+
const FormConfigMap = useFormConfigMap();
13+
14+
const currentFormMap = FormConfigMap[operatorName];
15+
16+
const values = useMemo(() => {
17+
const formData = node?.data?.form;
18+
if (operatorName === Operator.Categorize) {
19+
const items = buildCategorizeListFromObject(
20+
get(node, 'data.form.category_description', {}),
21+
);
22+
if (isPlainObject(formData)) {
23+
console.info('xxx');
24+
const nextValues = {
25+
...omit(formData, 'category_description'),
26+
items,
27+
};
28+
29+
return nextValues;
30+
}
31+
} else if (operatorName === Operator.Message) {
32+
return {
33+
...formData,
34+
content: convertToObjectArray(formData.content),
35+
};
36+
} else {
37+
return isEmpty(formData) ? currentFormMap : formData;
38+
}
39+
}, [currentFormMap, node, operatorName]);
40+
41+
return values;
42+
}

web/src/pages/agent/form/begin-form/index.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,61 @@ import { Switch } from '@/components/ui/switch';
1313
import { Textarea } from '@/components/ui/textarea';
1414
import { FormTooltip } from '@/components/ui/tooltip';
1515
import { buildSelectOptions } from '@/utils/component-util';
16+
import { zodResolver } from '@hookform/resolvers/zod';
1617
import { Plus } from 'lucide-react';
1718
import { useCallback } from 'react';
18-
import { useWatch } from 'react-hook-form';
19+
import { useForm, useWatch } from 'react-hook-form';
1920
import { useTranslation } from 'react-i18next';
21+
import { z } from 'zod';
2022
import { AgentDialogueMode } from '../../constant';
2123
import { INextOperatorForm } from '../../interface';
2224
import { ParameterDialog } from './parameter-dialog';
2325
import { QueryTable } from './query-table';
2426
import { useEditQueryRecord } from './use-edit-query';
27+
import { useValues } from './use-values';
28+
import { useWatchFormChange } from './use-watch-change';
2529

2630
const ModeOptions = buildSelectOptions([
2731
AgentDialogueMode.Conversational,
2832
AgentDialogueMode.Task,
2933
]);
3034

31-
const BeginForm = ({ form, node }: INextOperatorForm) => {
35+
const BeginForm = ({ node }: INextOperatorForm) => {
3236
const { t } = useTranslation();
3337

38+
const values = useValues(node);
39+
40+
const FormSchema = z.object({
41+
enablePrologue: z.boolean().optional(),
42+
prologue: z
43+
.string()
44+
.min(1, {
45+
message: t('common.namePlaceholder'),
46+
})
47+
.trim()
48+
.optional(),
49+
mode: z.string(),
50+
query: z
51+
.array(
52+
z.object({
53+
key: z.string(),
54+
type: z.string(),
55+
value: z.string(),
56+
optional: z.boolean(),
57+
name: z.string(),
58+
options: z.array(z.union([z.number(), z.string(), z.boolean()])),
59+
}),
60+
)
61+
.optional(),
62+
});
63+
64+
const form = useForm({
65+
defaultValues: values,
66+
resolver: zodResolver(FormSchema),
67+
});
68+
69+
useWatchFormChange(node?.id, form);
70+
3471
const query = useWatch({ control: form.control, name: 'query' });
3572
const mode = useWatch({ control: form.control, name: 'mode' });
3673

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { RAGFlowNodeType } from '@/interfaces/database/flow';
2+
import { isEmpty } from 'lodash';
3+
import { useMemo } from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
import { AgentDialogueMode } from '../../constant';
6+
7+
export function useValues(node?: RAGFlowNodeType) {
8+
const { t } = useTranslation();
9+
10+
const defaultValues = useMemo(
11+
() => ({
12+
enablePrologue: true,
13+
prologue: t('chat.setAnOpenerInitial'),
14+
mode: AgentDialogueMode.Conversational,
15+
}),
16+
[t],
17+
);
18+
19+
const values = useMemo(() => {
20+
const formData = node?.data?.form;
21+
22+
if (isEmpty(formData)) {
23+
return defaultValues;
24+
}
25+
26+
return formData;
27+
}, [defaultValues, node?.data?.form]);
28+
29+
return values;
30+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useEffect } from 'react';
2+
import { UseFormReturn, useWatch } from 'react-hook-form';
3+
import useGraphStore from '../../store';
4+
5+
export function useWatchFormChange(id?: string, form?: UseFormReturn) {
6+
let values = useWatch({ control: form?.control });
7+
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
8+
9+
useEffect(() => {
10+
// Manually triggered form updates are synchronized to the canvas
11+
if (id && form?.formState.isDirty) {
12+
values = form?.getValues();
13+
let nextValues: any = values;
14+
15+
updateNodeForm(id, nextValues);
16+
}
17+
}, [form?.formState.isDirty, id, updateNodeForm, values]);
18+
}

0 commit comments

Comments
 (0)