balibabu commited on
Commit
67a5f12
·
1 Parent(s): f72cd8c

feat: save the parameters of the generate operator to the form field … (#1390)

Browse files

### What problem does this PR solve?
feat: save the parameters of the generate operator to the form field of
the node #918

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/components/llm-setting-items/index.tsx CHANGED
@@ -51,7 +51,7 @@ const LlmSettingItems = ({ prefix, formItemLayout = {} }: IProps) => {
51
  <Divider></Divider>
52
  <Form.Item
53
  label={t('freedom')}
54
- name="parameters"
55
  tooltip={t('freedomTip')}
56
  {...formItemLayout}
57
  initialValue={ModelVariableType.Precise}
 
51
  <Divider></Divider>
52
  <Form.Item
53
  label={t('freedom')}
54
+ name="parameter"
55
  tooltip={t('freedomTip')}
56
  {...formItemLayout}
57
  initialValue={ModelVariableType.Precise}
web/src/locales/en.ts CHANGED
@@ -570,12 +570,15 @@ The above is the content you need to summarize.`,
570
  retrievalDescription: `This component is for the process of retrieving relevent information from knowledge base. So, knowledgebases should be selected. If there's nothing retrieved, the 'Empty response' will be returned.`,
571
  generateDescription: `This component is used to call LLM to generate text. Be careful about the prompt setting.`,
572
  categorizeDescription: `This component is used to categorize text. Please specify the name, description and examples of the category. Every single category leads to different downstream components.`,
573
- relevantDescription: `This component is used to judge if the retrieved information is relevent to user's question. 'Yes' represents that they're relevant. 'No' represents they're irrelevant.`,
574
  rewriteQuestionDescription: `This component is used to refine user's quesion. Typically, when a user's original question can't retrieve relevant information from knowledge base, this component help you change the question into a proper one which might be more consistant with the expressions in knowledge base. Only 'Retrieval' can be its downstreams.`,
575
  messageDescription:
576
  'This component is used to send user static information.',
577
  keywordDescription:
578
  'This component is used to send user static information.',
 
 
 
579
  },
580
  footer: {
581
  profile: 'All rights reserved @ React',
 
570
  retrievalDescription: `This component is for the process of retrieving relevent information from knowledge base. So, knowledgebases should be selected. If there's nothing retrieved, the 'Empty response' will be returned.`,
571
  generateDescription: `This component is used to call LLM to generate text. Be careful about the prompt setting.`,
572
  categorizeDescription: `This component is used to categorize text. Please specify the name, description and examples of the category. Every single category leads to different downstream components.`,
573
+ relevantDescription: `This component is used to judge if the output of upstream is relevent to user's latest question. 'Yes' represents that they're relevant. 'No' represents they're irrelevant.`,
574
  rewriteQuestionDescription: `This component is used to refine user's quesion. Typically, when a user's original question can't retrieve relevant information from knowledge base, this component help you change the question into a proper one which might be more consistant with the expressions in knowledge base. Only 'Retrieval' can be its downstreams.`,
575
  messageDescription:
576
  'This component is used to send user static information.',
577
  keywordDescription:
578
  'This component is used to send user static information.',
579
+ promptText: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following:
580
+ {input}
581
+ The above is the content you need to summarize.`,
582
  },
583
  footer: {
584
  profile: 'All rights reserved @ React',
web/src/pages/flow/constant.tsx CHANGED
@@ -145,13 +145,12 @@ const initialLlmBaseValues = {
145
  };
146
 
147
  export const initialGenerateValues = {
148
- // parameters: ModelVariableType.Precise,
149
- // temperatureEnabled: true,
150
  ...initialLlmBaseValues,
151
  prompt: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following:
152
- {cluster_content}
153
  The above is the content you need to summarize.`,
154
  cite: true,
 
155
  };
156
 
157
  export const initialRewriteQuestionValues = {
 
145
  };
146
 
147
  export const initialGenerateValues = {
 
 
148
  ...initialLlmBaseValues,
149
  prompt: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following:
150
+ {input}
151
  The above is the content you need to summarize.`,
152
  cite: true,
153
+ parameters: [],
154
  };
155
 
156
  export const initialRewriteQuestionValues = {
web/src/pages/flow/generate-form/dynamic-parameters.tsx CHANGED
@@ -1,103 +1,96 @@
 
1
  import { useTranslate } from '@/hooks/commonHooks';
2
- import { CloseOutlined } from '@ant-design/icons';
3
- import { Button, Card, Form, Input, Select, Typography } from 'antd';
4
- import { useUpdateNodeInternals } from 'reactflow';
5
- import { Operator } from '../constant';
6
- import {
7
- useBuildFormSelectOptions,
8
- useHandleFormSelectChange,
9
- } from '../form-hooks';
10
  import { IGenerateParameter } from '../interface';
11
 
 
 
 
 
 
 
12
  interface IProps {
13
  nodeId?: string;
14
  }
15
 
 
 
 
 
 
 
 
16
  const DynamicParameters = ({ nodeId }: IProps) => {
17
- const updateNodeInternals = useUpdateNodeInternals();
18
- const form = Form.useFormInstance();
19
- const buildCategorizeToOptions = useBuildFormSelectOptions(
20
- Operator.Categorize,
21
- nodeId,
22
- );
23
- const { handleSelectChange } = useHandleFormSelectChange(nodeId);
24
  const { t } = useTranslate('flow');
25
 
26
- return (
27
- <>
28
- <Form.List name="parameters">
29
- {(fields, { add, remove }) => {
30
- const handleAdd = () => {
31
- const idx = fields.length;
32
- add({ name: `parameter ${idx + 1}` });
33
- if (nodeId) updateNodeInternals(nodeId);
34
- };
35
- return (
36
- <div
37
- style={{ display: 'flex', rowGap: 10, flexDirection: 'column' }}
38
- >
39
- {fields.map((field) => (
40
- <Card
41
- size="small"
42
- key={field.key}
43
- extra={
44
- <CloseOutlined
45
- onClick={() => {
46
- remove(field.name);
47
- }}
48
- />
49
- }
50
- >
51
- <Form.Item
52
- label={t('key')} // TODO: repeatability check
53
- name={[field.name, 'key']}
54
- rules={[{ required: true, message: t('nameMessage') }]}
55
- >
56
- <Input />
57
- </Form.Item>
58
- <Form.Item
59
- label={t('componentId')}
60
- name={[field.name, 'component_id']}
61
- >
62
- <Select
63
- allowClear
64
- options={buildCategorizeToOptions(
65
- (form.getFieldValue(['parameters']) ?? [])
66
- .map((x: IGenerateParameter) => x.component_id)
67
- .filter(
68
- (x: string) =>
69
- x !==
70
- form.getFieldValue([
71
- 'parameters',
72
- field.name,
73
- 'component_id',
74
- ]),
75
- ),
76
- )}
77
- onChange={handleSelectChange(
78
- form.getFieldValue(['parameters', field.name, 'key']),
79
- )}
80
- />
81
- </Form.Item>
82
- </Card>
83
- ))}
84
 
85
- <Button type="dashed" onClick={handleAdd} block>
86
- + Add Item
87
- </Button>
88
- </div>
89
- );
90
- }}
91
- </Form.List>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- <Form.Item noStyle shouldUpdate>
94
- {() => (
95
- <Typography>
96
- <pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>
97
- </Typography>
98
- )}
99
- </Form.Item>
100
- </>
 
 
 
 
 
 
 
 
101
  );
102
  };
103
 
 
1
+ import { EditableCell, EditableRow } from '@/components/editable-cell';
2
  import { useTranslate } from '@/hooks/commonHooks';
3
+ import { DeleteOutlined } from '@ant-design/icons';
4
+ import { Button, Flex, Select, Table, TableProps } from 'antd';
 
 
 
 
 
 
5
  import { IGenerateParameter } from '../interface';
6
 
7
+ import {
8
+ useBuildComponentIdSelectOptions,
9
+ useHandleOperateParameters,
10
+ } from './hooks';
11
+ import styles from './index.less';
12
+
13
  interface IProps {
14
  nodeId?: string;
15
  }
16
 
17
+ const components = {
18
+ body: {
19
+ row: EditableRow,
20
+ cell: EditableCell,
21
+ },
22
+ };
23
+
24
  const DynamicParameters = ({ nodeId }: IProps) => {
 
 
 
 
 
 
 
25
  const { t } = useTranslate('flow');
26
 
27
+ const options = useBuildComponentIdSelectOptions(nodeId);
28
+ const {
29
+ dataSource,
30
+ handleAdd,
31
+ handleRemove,
32
+ handleSave,
33
+ handleComponentIdChange,
34
+ } = useHandleOperateParameters(nodeId!);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ const columns: TableProps<IGenerateParameter>['columns'] = [
37
+ {
38
+ title: t('key'),
39
+ dataIndex: 'key',
40
+ key: 'key',
41
+ onCell: (record: IGenerateParameter) => ({
42
+ record,
43
+ editable: true,
44
+ dataIndex: 'key',
45
+ title: 'key',
46
+ handleSave,
47
+ }),
48
+ },
49
+ {
50
+ title: t('componentId'),
51
+ dataIndex: 'component_id',
52
+ key: 'component_id',
53
+ align: 'center',
54
+ render(text, record) {
55
+ return (
56
+ <Select
57
+ style={{ width: '100%' }}
58
+ allowClear
59
+ options={options}
60
+ value={text}
61
+ onChange={handleComponentIdChange(record)}
62
+ />
63
+ );
64
+ },
65
+ },
66
+ {
67
+ title: t('operation'),
68
+ dataIndex: 'operation',
69
+ width: 20,
70
+ key: 'operation',
71
+ align: 'center',
72
+ render(_, record) {
73
+ return <DeleteOutlined onClick={handleRemove(record.id)} />;
74
+ },
75
+ },
76
+ ];
77
 
78
+ return (
79
+ <section>
80
+ <Flex justify="end">
81
+ <Button size="small" onClick={handleAdd}>
82
+ {t('add')}
83
+ </Button>
84
+ </Flex>
85
+ <Table
86
+ dataSource={dataSource}
87
+ columns={columns}
88
+ rowKey={'id'}
89
+ className={styles.variableTable}
90
+ components={components}
91
+ rowClassName={() => styles.editableRow}
92
+ />
93
+ </section>
94
  );
95
  };
96
 
web/src/pages/flow/generate-form/hooks.ts ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import get from 'lodash/get';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { Operator } from '../constant';
5
+ import { IGenerateParameter } from '../interface';
6
+ import useGraphStore from '../store';
7
+
8
+ // exclude nodes with branches
9
+ const ExcludedNodes = [Operator.Categorize, Operator.Relevant];
10
+
11
+ export const useBuildComponentIdSelectOptions = (nodeId?: string) => {
12
+ const nodes = useGraphStore((state) => state.nodes);
13
+
14
+ const options = useMemo(() => {
15
+ return nodes
16
+ .filter(
17
+ (x) =>
18
+ x.id !== nodeId && !ExcludedNodes.some((y) => y === x.data.label),
19
+ )
20
+ .map((x) => ({ label: x.data.name, value: x.id }));
21
+ }, [nodes, nodeId]);
22
+
23
+ return options;
24
+ };
25
+
26
+ export const useHandleOperateParameters = (nodeId: string) => {
27
+ const { getNode, updateNodeForm } = useGraphStore((state) => state);
28
+ const node = getNode(nodeId);
29
+ const dataSource: IGenerateParameter[] = useMemo(
30
+ () => get(node, 'data.form.parameters', []),
31
+ [node],
32
+ );
33
+
34
+ // const [x, setDataSource] = useState<IGenerateParameter[]>([]);
35
+
36
+ const handleComponentIdChange = useCallback(
37
+ (row: IGenerateParameter) => (value: string) => {
38
+ const newData = [...dataSource];
39
+ const index = newData.findIndex((item) => row.id === item.id);
40
+ const item = newData[index];
41
+ newData.splice(index, 1, {
42
+ ...item,
43
+ component_id: value,
44
+ });
45
+
46
+ updateNodeForm(nodeId, { parameters: newData });
47
+ // setDataSource(newData);
48
+ },
49
+ [updateNodeForm, nodeId, dataSource],
50
+ );
51
+
52
+ const handleRemove = useCallback(
53
+ (id?: string) => () => {
54
+ const newData = dataSource.filter((item) => item.id !== id);
55
+ updateNodeForm(nodeId, { parameters: newData });
56
+ // setDataSource(newData);
57
+ },
58
+ [updateNodeForm, nodeId, dataSource],
59
+ );
60
+
61
+ const handleAdd = useCallback(() => {
62
+ // setDataSource((state) => [
63
+ // ...state,
64
+ // {
65
+ // id: uuid(),
66
+ // key: '',
67
+ // component_id: undefined,
68
+ // },
69
+ // ]);
70
+ updateNodeForm(nodeId, {
71
+ parameters: [
72
+ ...dataSource,
73
+ {
74
+ id: uuid(),
75
+ key: '',
76
+ component_id: undefined,
77
+ },
78
+ ],
79
+ });
80
+ }, [dataSource, nodeId, updateNodeForm]);
81
+
82
+ const handleSave = (row: IGenerateParameter) => {
83
+ const newData = [...dataSource];
84
+ const index = newData.findIndex((item) => row.id === item.id);
85
+ const item = newData[index];
86
+ newData.splice(index, 1, {
87
+ ...item,
88
+ ...row,
89
+ });
90
+
91
+ updateNodeForm(nodeId, { parameters: newData });
92
+ // setDataSource(newData);
93
+ };
94
+
95
+ return {
96
+ handleAdd,
97
+ handleRemove,
98
+ handleComponentIdChange,
99
+ handleSave,
100
+ dataSource,
101
+ };
102
+ };
web/src/pages/flow/generate-form/index.less CHANGED
@@ -9,7 +9,7 @@
9
  :global(.editable-cell-value-wrap) {
10
  padding: 5px 12px;
11
  cursor: pointer;
12
- height: 22px !important;
13
  }
14
  &:hover {
15
  :global(.editable-cell-value-wrap) {
 
9
  :global(.editable-cell-value-wrap) {
10
  padding: 5px 12px;
11
  cursor: pointer;
12
+ height: 30px !important;
13
  }
14
  &:hover {
15
  :global(.editable-cell-value-wrap) {
web/src/pages/flow/generate-form/index.tsx CHANGED
@@ -3,9 +3,9 @@ import { useTranslate } from '@/hooks/commonHooks';
3
  import { Form, Input, Switch } from 'antd';
4
  import { useSetLlmSetting } from '../hooks';
5
  import { IOperatorForm } from '../interface';
6
- import DynamicParameters from './next-dynamic-parameters';
7
 
8
- const GenerateForm = ({ onValuesChange, form }: IOperatorForm) => {
9
  const { t } = useTranslate('flow');
10
 
11
  useSetLlmSetting(form);
@@ -29,7 +29,7 @@ const GenerateForm = ({ onValuesChange, form }: IOperatorForm) => {
29
  <Form.Item
30
  name={['prompt']}
31
  label={t('prompt', { keyPrefix: 'knowledgeConfiguration' })}
32
- initialValue={t('promptText', { keyPrefix: 'knowledgeConfiguration' })}
33
  tooltip={t('promptTip', { keyPrefix: 'knowledgeConfiguration' })}
34
  rules={[
35
  {
@@ -49,7 +49,7 @@ const GenerateForm = ({ onValuesChange, form }: IOperatorForm) => {
49
  >
50
  <Switch />
51
  </Form.Item>
52
- <DynamicParameters></DynamicParameters>
53
  </Form>
54
  );
55
  };
 
3
  import { Form, Input, Switch } from 'antd';
4
  import { useSetLlmSetting } from '../hooks';
5
  import { IOperatorForm } from '../interface';
6
+ import DynamicParameters from './dynamic-parameters';
7
 
8
+ const GenerateForm = ({ onValuesChange, form, node }: IOperatorForm) => {
9
  const { t } = useTranslate('flow');
10
 
11
  useSetLlmSetting(form);
 
29
  <Form.Item
30
  name={['prompt']}
31
  label={t('prompt', { keyPrefix: 'knowledgeConfiguration' })}
32
+ initialValue={t('promptText')}
33
  tooltip={t('promptTip', { keyPrefix: 'knowledgeConfiguration' })}
34
  rules={[
35
  {
 
49
  >
50
  <Switch />
51
  </Form.Item>
52
+ <DynamicParameters nodeId={node?.id}></DynamicParameters>
53
  </Form>
54
  );
55
  };
web/src/pages/flow/generate-form/next-dynamic-parameters.tsx DELETED
@@ -1,135 +0,0 @@
1
- import { EditableCell, EditableRow } from '@/components/editable-cell';
2
- import { useTranslate } from '@/hooks/commonHooks';
3
- import { DeleteOutlined } from '@ant-design/icons';
4
- import { Button, Flex, Select, Table, TableProps } from 'antd';
5
- import { useEffect, useState } from 'react';
6
- import { v4 as uuid } from 'uuid';
7
- import { IGenerateParameter } from '../interface';
8
-
9
- import { Operator } from '../constant';
10
- import { useBuildFormSelectOptions } from '../form-hooks';
11
- import styles from './index.less';
12
-
13
- interface IProps {
14
- nodeId?: string;
15
- }
16
-
17
- const components = {
18
- body: {
19
- row: EditableRow,
20
- cell: EditableCell,
21
- },
22
- };
23
-
24
- const DynamicParameters = ({ nodeId }: IProps) => {
25
- const [dataSource, setDataSource] = useState<IGenerateParameter[]>([]);
26
- const { t } = useTranslate('flow');
27
-
28
- const buildCategorizeToOptions = useBuildFormSelectOptions(
29
- Operator.Generate,
30
- nodeId,
31
- );
32
-
33
- const handleRemove = (id?: string) => () => {
34
- const newData = dataSource.filter((item) => item.id !== id);
35
- setDataSource(newData);
36
- };
37
-
38
- const handleAdd = () => {
39
- setDataSource((state) => [
40
- ...state,
41
- {
42
- id: uuid(),
43
- key: '',
44
- component_id: undefined,
45
- },
46
- ]);
47
- };
48
-
49
- const handleSave = (row: IGenerateParameter) => {
50
- const newData = [...dataSource];
51
- const index = newData.findIndex((item) => row.id === item.id);
52
- const item = newData[index];
53
- newData.splice(index, 1, {
54
- ...item,
55
- ...row,
56
- });
57
- setDataSource(newData);
58
- };
59
-
60
- useEffect(() => {}, [dataSource]);
61
-
62
- const handleOptionalChange = (row: IGenerateParameter) => (value: string) => {
63
- const newData = [...dataSource];
64
- const index = newData.findIndex((item) => row.id === item.id);
65
- const item = newData[index];
66
- newData.splice(index, 1, {
67
- ...item,
68
- component_id: value,
69
- });
70
- setDataSource(newData);
71
- };
72
-
73
- const columns: TableProps<IGenerateParameter>['columns'] = [
74
- {
75
- title: t('key'),
76
- dataIndex: 'key',
77
- key: 'key',
78
- onCell: (record: IGenerateParameter) => ({
79
- record,
80
- editable: true,
81
- dataIndex: 'key',
82
- title: 'key',
83
- handleSave,
84
- }),
85
- },
86
- {
87
- title: t('componentId'),
88
- dataIndex: 'component_id',
89
- key: 'component_id',
90
- align: 'center',
91
- render(text, record) {
92
- return (
93
- <Select
94
- style={{ width: '100%' }}
95
- allowClear
96
- options={buildCategorizeToOptions([])}
97
- // onChange={handleSelectChange(
98
- // form.getFieldValue(['parameters', field.name, 'key']),
99
- // )}
100
- />
101
- );
102
- },
103
- },
104
- {
105
- title: t('operation'),
106
- dataIndex: 'operation',
107
- width: 20,
108
- key: 'operation',
109
- align: 'center',
110
- render(_, record) {
111
- return <DeleteOutlined onClick={handleRemove(record.id)} />;
112
- },
113
- },
114
- ];
115
-
116
- return (
117
- <section>
118
- <Flex justify="end">
119
- <Button size="small" onClick={handleAdd}>
120
- {t('add')}
121
- </Button>
122
- </Flex>
123
- <Table
124
- dataSource={dataSource}
125
- columns={columns}
126
- rowKey={'id'}
127
- className={styles.variableTable}
128
- components={components}
129
- rowClassName={() => styles.editableRow}
130
- />
131
- </section>
132
- );
133
- };
134
-
135
- export default DynamicParameters;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
web/src/pages/flow/store.ts CHANGED
@@ -189,7 +189,17 @@ const useGraphStore = create<RFState>()(
189
  set({
190
  nodes: get().nodes.map((node) => {
191
  if (node.id === nodeId) {
192
- node.data = { ...node.data, form: values };
 
 
 
 
 
 
 
 
 
 
193
  }
194
 
195
  return node;
 
189
  set({
190
  nodes: get().nodes.map((node) => {
191
  if (node.id === nodeId) {
192
+ // node.data = {
193
+ // ...node.data,
194
+ // form: { ...node.data.form, ...values },
195
+ // };
196
+ return {
197
+ ...node,
198
+ data: {
199
+ ...node.data,
200
+ form: { ...node.data.form, ...values },
201
+ },
202
+ } as any;
203
  }
204
 
205
  return node;
web/src/utils/form.ts CHANGED
@@ -20,7 +20,7 @@ export const excludeUnEnabledVariables = (
20
  export const removeUselessFieldsFromValues = (values: any, prefix?: string) => {
21
  const nextValues: any = omit(values, [
22
  ...Object.keys(variableEnabledFieldMap),
23
- 'parameters',
24
  ...excludeUnEnabledVariables(values, prefix),
25
  ]);
26
 
 
20
  export const removeUselessFieldsFromValues = (values: any, prefix?: string) => {
21
  const nextValues: any = omit(values, [
22
  ...Object.keys(variableEnabledFieldMap),
23
+ 'parameter',
24
  ...excludeUnEnabledVariables(values, prefix),
25
  ]);
26