balibabu commited on
Commit
2bdad3e
·
1 Parent(s): 4496b5e

Feat: Add the iteration Node #4242 (#4247)

Browse files

### What problem does this PR solve?

Feat: Add the iteration Node #4242

### Type of change


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

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. web/src/assets/icon/Icon.tsx +38 -0
  2. web/src/components/delimiter.tsx +9 -2
  3. web/src/interfaces/database/flow.ts +1 -0
  4. web/src/less/mixins.less +20 -0
  5. web/src/locales/en.ts +16 -0
  6. web/src/locales/zh-traditional.ts +14 -0
  7. web/src/locales/zh.ts +14 -0
  8. web/src/pages/flow/canvas/edge/index.tsx +1 -0
  9. web/src/pages/flow/canvas/index.less +6 -0
  10. web/src/pages/flow/canvas/index.tsx +23 -82
  11. web/src/pages/flow/canvas/node/begin-node.tsx +3 -1
  12. web/src/pages/flow/canvas/node/dropdown.tsx +10 -2
  13. web/src/pages/flow/canvas/node/generate-node.tsx +1 -1
  14. web/src/pages/flow/canvas/node/index.less +22 -18
  15. web/src/pages/flow/canvas/node/iteration-node.tsx +118 -0
  16. web/src/pages/flow/canvas/node/node-header.tsx +16 -5
  17. web/src/pages/flow/canvas/node/popover.tsx +2 -1
  18. web/src/pages/flow/canvas/node/switch-node.tsx +1 -1
  19. web/src/pages/flow/canvas/node/template-node.tsx +2 -2
  20. web/src/pages/flow/constant.tsx +23 -0
  21. web/src/pages/flow/flow-drawer/index.tsx +13 -6
  22. web/src/pages/flow/form/akshare-form/index.tsx +1 -1
  23. web/src/pages/flow/form/arxiv-form/index.tsx +1 -1
  24. web/src/pages/flow/form/baidu-fanyi-form/index.tsx +1 -1
  25. web/src/pages/flow/form/baidu-form/index.tsx +1 -1
  26. web/src/pages/flow/form/bing-form/index.tsx +1 -1
  27. web/src/pages/flow/form/categorize-form/index.tsx +1 -1
  28. web/src/pages/flow/form/components/dynamic-input-variable.tsx +12 -7
  29. web/src/pages/flow/form/crawler-form/index.tsx +1 -1
  30. web/src/pages/flow/form/deepl-form/index.tsx +1 -1
  31. web/src/pages/flow/form/duckduckgo-form/index.tsx +1 -1
  32. web/src/pages/flow/form/email-form/index.tsx +1 -1
  33. web/src/pages/flow/form/exesql-form/index.tsx +1 -1
  34. web/src/pages/flow/form/generate-form/dynamic-parameters.tsx +8 -7
  35. web/src/pages/flow/form/generate-form/index.tsx +1 -1
  36. web/src/pages/flow/form/github-form/index.tsx +1 -1
  37. web/src/pages/flow/form/google-form/index.tsx +1 -1
  38. web/src/pages/flow/form/google-scholar-form/index.tsx +1 -1
  39. web/src/pages/flow/form/invoke-form/dynamic-variables.tsx +8 -6
  40. web/src/pages/flow/form/invoke-form/index.tsx +1 -1
  41. web/src/pages/flow/form/iteration-from/index.tsx +94 -0
  42. web/src/pages/flow/form/jin10-form/index.tsx +1 -1
  43. web/src/pages/flow/form/keyword-extract-form/index.tsx +1 -1
  44. web/src/pages/flow/form/pubmed-form/index.tsx +1 -1
  45. web/src/pages/flow/form/qweather-form/index.tsx +1 -1
  46. web/src/pages/flow/form/retrieval-form/index.tsx +1 -1
  47. web/src/pages/flow/form/switch-form/index.tsx +5 -2
  48. web/src/pages/flow/form/template-form/index.tsx +1 -1
  49. web/src/pages/flow/form/tushare-form/index.tsx +1 -1
  50. web/src/pages/flow/form/wencai-form/index.tsx +1 -1
web/src/assets/icon/Icon.tsx CHANGED
@@ -205,6 +205,36 @@ const QWeatherSvg = () => (
205
  </svg>
206
  );
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  export const ApiIcon = (props: Partial<IconComponentProps>) => (
209
  <Icon component={ApiSvg} {...props} />
210
  );
@@ -238,3 +268,11 @@ export const GitHubIcon = (props: Partial<IconComponentProps>) => (
238
  export const QWeatherIcon = (props: Partial<IconComponentProps>) => (
239
  <Icon component={QWeatherSvg} {...props} />
240
  );
 
 
 
 
 
 
 
 
 
205
  </svg>
206
  );
207
 
208
+ const SemicolonSvg = () => (
209
+ <svg
210
+ viewBox="0 0 1024 1024"
211
+ version="1.1"
212
+ xmlns="http://www.w3.org/2000/svg"
213
+ width="200"
214
+ height="200"
215
+ >
216
+ <path
217
+ d="M506.88 249.059556a89.884444 89.884444 0 0 0 60.074667-21.845334 87.950222 87.950222 0 0 0 23.210666-61.44 79.189333 79.189333 0 0 0-83.285333-83.285333c-24.576 0-45.056 6.826667-60.074667 23.210667-16.384 15.018667-23.210667 35.498667-23.210666 60.074666 0 24.576 6.826667 45.056 23.210666 61.44 15.018667 13.653333 35.498667 21.845333 60.074667 21.845334zM414.037333 967.224889a262.030222 262.030222 0 0 0 141.994667-88.746667c35.498667-46.421333 53.248-99.669333 53.248-159.744 0-39.594667-9.557333-70.997333-27.306667-95.573333a89.543111 89.543111 0 0 0-75.093333-38.229333c-27.306667 0-47.786667 6.826667-62.805333 23.210666-17.749333 15.018667-25.941333 35.498667-25.941334 61.44 0 23.210667 8.192 43.690667 24.576 60.074667a79.416889 79.416889 0 0 0 58.709334 24.576 78.506667 78.506667 0 0 0 30.037333-5.461333c0 32.768-9.557333 62.805333-30.037333 91.477333a190.008889 190.008889 0 0 1-87.381334 60.074667v66.901333z"
218
+ fill={currentColor}
219
+ ></path>
220
+ </svg>
221
+ );
222
+
223
+ const CommaSvg = () => (
224
+ <svg
225
+ viewBox="0 0 1024 1024"
226
+ version="1.1"
227
+ xmlns="http://www.w3.org/2000/svg"
228
+ width="200"
229
+ height="200"
230
+ >
231
+ <path
232
+ fill={currentColor}
233
+ d="M701.312 416.064C701.312 327.68 629.76 256 541.312 256c-88.32 0-160 71.68-160 160.064s71.68 160.064 160 160.064c10.368 0 20.352-1.216 30.144-3.072-27.136 78.592-88.32 99.392-166.4 120.32L434.688 736c228.288-40.576 269.184-268.48 266.688-266.496C707.328 452.672 701.312 434.88 701.312 416.064z"
234
+ ></path>
235
+ </svg>
236
+ );
237
+
238
  export const ApiIcon = (props: Partial<IconComponentProps>) => (
239
  <Icon component={ApiSvg} {...props} />
240
  );
 
268
  export const QWeatherIcon = (props: Partial<IconComponentProps>) => (
269
  <Icon component={QWeatherSvg} {...props} />
270
  );
271
+
272
+ export const SemicolonIcon = (props: Partial<IconComponentProps>) => (
273
+ <Icon component={SemicolonSvg} {...props} />
274
+ );
275
+
276
+ export const CommaIcon = (props: Partial<IconComponentProps>) => (
277
+ <Icon component={CommaSvg} {...props} />
278
+ );
web/src/components/delimiter.tsx CHANGED
@@ -4,16 +4,23 @@ import { useTranslation } from 'react-i18next';
4
  interface IProps {
5
  value?: string | undefined;
6
  onChange?: (val: string | undefined) => void;
 
7
  }
8
 
9
- const DelimiterInput = ({ value, onChange }: IProps) => {
10
  const nextValue = value?.replaceAll('\n', '\\n');
11
  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
12
  const val = e.target.value;
13
  const nextValue = val.replaceAll('\\n', '\n');
14
  onChange?.(nextValue);
15
  };
16
- return <Input value={nextValue} onChange={handleInputChange}></Input>;
 
 
 
 
 
 
17
  };
18
 
19
  const Delimiter = () => {
 
4
  interface IProps {
5
  value?: string | undefined;
6
  onChange?: (val: string | undefined) => void;
7
+ maxLength?: number;
8
  }
9
 
10
+ export const DelimiterInput = ({ value, onChange, maxLength }: IProps) => {
11
  const nextValue = value?.replaceAll('\n', '\\n');
12
  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
13
  const val = e.target.value;
14
  const nextValue = val.replaceAll('\\n', '\n');
15
  onChange?.(nextValue);
16
  };
17
+ return (
18
+ <Input
19
+ value={nextValue}
20
+ onChange={handleInputChange}
21
+ maxLength={maxLength}
22
+ ></Input>
23
+ );
24
  };
25
 
26
  const Delimiter = () => {
web/src/interfaces/database/flow.ts CHANGED
@@ -17,6 +17,7 @@ export interface IOperator {
17
  obj: IOperatorNode;
18
  downstream: string[];
19
  upstream: string[];
 
20
  }
21
 
22
  export interface IOperatorNode {
 
17
  obj: IOperatorNode;
18
  downstream: string[];
19
  upstream: string[];
20
+ parent_id?: string;
21
  }
22
 
23
  export interface IOperatorNode {
web/src/less/mixins.less CHANGED
@@ -75,3 +75,23 @@
75
  background-color: #eff8ff;
76
  border: 1px;
77
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  background-color: #eff8ff;
76
  border: 1px;
77
  }
78
+
79
+ .commonNodeShadow() {
80
+ box-shadow:
81
+ -6px 0 12px 0 rgba(179, 177, 177, 0.08),
82
+ -3px 0 6px -4px rgba(0, 0, 0, 0.12),
83
+ -6px 0 16px 6px rgba(0, 0, 0, 0.05);
84
+ }
85
+
86
+ .commonNodeRadius() {
87
+ border-radius: 10px;
88
+ }
89
+
90
+ .commonNode() {
91
+ .commonNodeShadow();
92
+ .commonNodeRadius();
93
+
94
+ padding: 10px;
95
+ background: white;
96
+ width: 200px;
97
+ }
web/src/locales/en.ts CHANGED
@@ -1077,6 +1077,22 @@ The above is the content you need to summarize.`,
1077
  contentTip: 'content: Email content (Optional)',
1078
  jsonUploadTypeErrorMessage: 'Please upload json file',
1079
  jsonUploadContentErrorMessage: 'json file error',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1080
  },
1081
  footer: {
1082
  profile: 'All rights reserved @ React',
 
1077
  contentTip: 'content: Email content (Optional)',
1078
  jsonUploadTypeErrorMessage: 'Please upload json file',
1079
  jsonUploadContentErrorMessage: 'json file error',
1080
+ iteration: 'Iteration',
1081
+ iterationDescription: `This component firstly split the input into array by "delimiter".
1082
+ Perform the same operation steps on the elements in the array in sequence until all results are output, which can be understood as a task batch processor.
1083
+
1084
+ For example, within the long text translation iteration node, if all content is input to the LLM node, the single conversation limit may be reached. The upstream node can first split the long text into multiple fragments, and cooperate with the iterative node to perform batch translation on each fragment to avoid reaching the LLM message limit for a single conversation.`,
1085
+ delimiterTip: `
1086
+ This delimiter is used to split the input text into several text pieces echo of which will be performed as input item of each iteration.`,
1087
+ delimiterOptions: {
1088
+ comma: 'Comma',
1089
+ lineBreak: 'Line break',
1090
+ tab: 'Tab',
1091
+ underline: 'Underline',
1092
+ diagonal: 'Diagonal',
1093
+ minus: 'Minus',
1094
+ semicolon: 'Semicolon',
1095
+ },
1096
  },
1097
  footer: {
1098
  profile: 'All rights reserved @ React',
web/src/locales/zh-traditional.ts CHANGED
@@ -1016,6 +1016,20 @@ export default {
1016
  templateDescription: '此元件用於排版各種元件的輸出。 ',
1017
  jsonUploadTypeErrorMessage: '請上傳json檔',
1018
  jsonUploadContentErrorMessage: 'json 檔案錯誤',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1019
  },
1020
  footer: {
1021
  profile: '“保留所有權利 @ react”',
 
1016
  templateDescription: '此元件用於排版各種元件的輸出。 ',
1017
  jsonUploadTypeErrorMessage: '請上傳json檔',
1018
  jsonUploadContentErrorMessage: 'json 檔案錯誤',
1019
+ iterationDescription: `此元件首先透過「分隔符號」將輸入拆分為陣列。
1020
+ 對數組中的元素依序執行相同的操作步驟,直到輸出所有結果,可以理解為任務批次處理器。
1021
+
1022
+ 例如,在長文本翻譯迭代節點內,如果所有內容都輸入到LLM節點,則可能會達到單次對話限制。上游節點可以先將長文本拆分為多個分片,並配合迭代節點對每個分片進行批次翻譯,避免達到單次對話的LLM訊息限制。`,
1023
+ delimiterTip: `此分隔符號用於將輸入文字分割成多個文字片段,其中的回顯將作為每次迭代的輸入項執行。`,
1024
+ delimiterOptions: {
1025
+ comma: '逗號',
1026
+ lineBreak: '換行',
1027
+ tab: '製表符',
1028
+ underline: '底線',
1029
+ diagonal: '斜線',
1030
+ minus: '減號',
1031
+ semicolon: '分號',
1032
+ },
1033
  },
1034
  footer: {
1035
  profile: '“保留所有權利 @ react”',
web/src/locales/zh.ts CHANGED
@@ -1060,6 +1060,20 @@ export default {
1060
  contentTip: 'content: 邮件内容(可选)',
1061
  jsonUploadTypeErrorMessage: '请上传json文件',
1062
  jsonUploadContentErrorMessage: 'json 文件错误',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1063
  },
1064
  footer: {
1065
  profile: 'All rights reserved @ React',
 
1060
  contentTip: 'content: 邮件内容(可选)',
1061
  jsonUploadTypeErrorMessage: '请上传json文件',
1062
  jsonUploadContentErrorMessage: 'json 文件错误',
1063
+ iteration: '循环',
1064
+ iterationDescription: `该组件首先将输入以“分隔符”分割成数组,然后依次对数组中的元素执行相同的操作步骤,直到输出所有结果,可以理解为一个任务批处理器。
1065
+
1066
+ 例如在长文本翻译迭代节点中,如果所有内容都输入到LLM节点,可能会达到单次对话的限制,上游节点可以先将长文本分割成多个片段,配合迭代节点对每个片段进行批量翻译,避免达到单次对话的LLM消息限制。`,
1067
+ delimiterTip: `该分隔符用于将输入文本分割成几个文本片段,每个文本片段的回显将作为每次迭代的输入项。`,
1068
+ delimiterOptions: {
1069
+ comma: '逗号',
1070
+ lineBreak: '换行',
1071
+ tab: '制表符',
1072
+ underline: '下划线',
1073
+ diagonal: '斜线',
1074
+ minus: '减号',
1075
+ semicolon: '分号',
1076
+ },
1077
  },
1078
  footer: {
1079
  profile: 'All rights reserved @ React',
web/src/pages/flow/canvas/edge/index.tsx CHANGED
@@ -90,6 +90,7 @@ export function ButtonEdge({
90
  // everything inside EdgeLabelRenderer has no pointer events by default
91
  // if you have an interactive element, set pointer-events: all
92
  pointerEvents: 'all',
 
93
  }}
94
  className="nodrag nopan"
95
  >
 
90
  // everything inside EdgeLabelRenderer has no pointer events by default
91
  // if you have an interactive element, set pointer-events: all
92
  pointerEvents: 'all',
93
+ zIndex: 1001, // https://github.com/xyflow/xyflow/discussions/3498
94
  }}
95
  className="nodrag nopan"
96
  >
web/src/pages/flow/canvas/index.less CHANGED
@@ -1,4 +1,10 @@
1
  .canvasWrapper {
2
  position: relative;
3
  height: 100%;
 
 
 
 
 
 
4
  }
 
1
  .canvasWrapper {
2
  position: relative;
3
  height: 100%;
4
+ :global(.react-flow__node-group) {
5
+ .commonNode();
6
+ padding: 0;
7
+ border: 0;
8
+ background-color: transparent;
9
+ }
10
  }
web/src/pages/flow/canvas/index.tsx CHANGED
@@ -4,32 +4,24 @@ import {
4
  TooltipProvider,
5
  TooltipTrigger,
6
  } from '@/components/ui/tooltip';
7
- import { useSetModalState } from '@/hooks/common-hooks';
8
- import { get } from 'lodash';
9
  import { FolderInput, FolderOutput } from 'lucide-react';
10
- import { useCallback, useEffect } from 'react';
11
  import ReactFlow, {
12
  Background,
13
  ConnectionMode,
14
  ControlButton,
15
  Controls,
16
- NodeMouseHandler,
17
  } from 'reactflow';
18
  import 'reactflow/dist/style.css';
19
  import ChatDrawer from '../chat/drawer';
20
- import { Operator } from '../constant';
21
  import FormDrawer from '../flow-drawer';
22
  import {
23
- useGetBeginNodeDataQuery,
24
  useHandleDrop,
25
- useHandleExportOrImportJsonFile,
26
  useSelectCanvasData,
27
- useShowFormDrawer,
28
- useShowSingleDebugDrawer,
29
  useValidateConnection,
30
  useWatchNodeFormDataChange,
31
  } from '../hooks';
32
- import { BeginQuery } from '../interface';
 
33
  import JsonUploadModal from '../json-upload-modal';
34
  import RunDrawer from '../run-drawer';
35
  import { ButtonEdge } from './edge';
@@ -40,6 +32,7 @@ import { CategorizeNode } from './node/categorize-node';
40
  import { EmailNode } from './node/email-node';
41
  import { GenerateNode } from './node/generate-node';
42
  import { InvokeNode } from './node/invoke-node';
 
43
  import { KeywordNode } from './node/keyword-node';
44
  import { LogicNode } from './node/logic-node';
45
  import { MessageNode } from './node/message-node';
@@ -66,6 +59,8 @@ const nodeTypes = {
66
  invokeNode: InvokeNode,
67
  templateNode: TemplateNode,
68
  emailNode: EmailNode,
 
 
69
  };
70
 
71
  const edgeTypes = {
@@ -87,66 +82,11 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
87
  onSelectionChange,
88
  } = useSelectCanvasData();
89
  const isValidConnection = useValidateConnection();
90
- const {
91
- visible: runVisible,
92
- showModal: showRunModal,
93
- hideModal: hideRunModal,
94
- } = useSetModalState();
95
- const {
96
- visible: chatVisible,
97
- showModal: showChatModal,
98
- hideModal: hideChatModal,
99
- } = useSetModalState();
100
- const {
101
- singleDebugDrawerVisible,
102
- showSingleDebugDrawer,
103
- hideSingleDebugDrawer,
104
- } = useShowSingleDebugDrawer();
105
 
106
  const controlIconClassname = 'text-black';
107
 
108
- const { formDrawerVisible, hideFormDrawer, showFormDrawer, clickedNode } =
109
- useShowFormDrawer();
110
-
111
- const onPaneClick = useCallback(() => {
112
- hideFormDrawer();
113
- }, [hideFormDrawer]);
114
-
115
  const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop();
116
 
117
- useWatchNodeFormDataChange();
118
-
119
- const hideRunOrChatDrawer = useCallback(() => {
120
- hideChatModal();
121
- hideRunModal();
122
- hideDrawer();
123
- }, [hideChatModal, hideDrawer, hideRunModal]);
124
-
125
- const onNodeClick: NodeMouseHandler = useCallback(
126
- (e, node) => {
127
- if (node.data.label !== Operator.Note) {
128
- hideSingleDebugDrawer();
129
- hideRunOrChatDrawer();
130
- showFormDrawer(node);
131
- }
132
- // handle single debug icon click
133
- if (
134
- get(e.target, 'dataset.play') === 'true' ||
135
- get(e.target, 'parentNode.dataset.play') === 'true'
136
- ) {
137
- showSingleDebugDrawer();
138
- }
139
- },
140
- [
141
- hideRunOrChatDrawer,
142
- hideSingleDebugDrawer,
143
- showFormDrawer,
144
- showSingleDebugDrawer,
145
- ],
146
- );
147
-
148
- const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
149
-
150
  const {
151
  handleExportJson,
152
  handleImportJson,
@@ -155,25 +95,25 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
155
  hideFileUploadModal,
156
  } = useHandleExportOrImportJsonFile();
157
 
158
- useEffect(() => {
159
- if (drawerVisible) {
160
- const query: BeginQuery[] = getBeginNodeDataQuery();
161
- if (query.length > 0) {
162
- showRunModal();
163
- hideChatModal();
164
- } else {
165
- showChatModal();
166
- hideRunModal();
167
- }
168
- }
169
- }, [
170
- hideChatModal,
171
- hideRunModal,
172
  showChatModal,
173
- showRunModal,
174
  drawerVisible,
175
- getBeginNodeDataQuery,
176
- ]);
 
 
177
 
178
  return (
179
  <div className={styles.canvasWrapper}>
@@ -222,6 +162,7 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
222
  strokeWidth: 2,
223
  stroke: 'rgb(202 197 245)',
224
  },
 
225
  }}
226
  deleteKeyCode={['Delete', 'Backspace']}
227
  >
 
4
  TooltipProvider,
5
  TooltipTrigger,
6
  } from '@/components/ui/tooltip';
 
 
7
  import { FolderInput, FolderOutput } from 'lucide-react';
 
8
  import ReactFlow, {
9
  Background,
10
  ConnectionMode,
11
  ControlButton,
12
  Controls,
 
13
  } from 'reactflow';
14
  import 'reactflow/dist/style.css';
15
  import ChatDrawer from '../chat/drawer';
 
16
  import FormDrawer from '../flow-drawer';
17
  import {
 
18
  useHandleDrop,
 
19
  useSelectCanvasData,
 
 
20
  useValidateConnection,
21
  useWatchNodeFormDataChange,
22
  } from '../hooks';
23
+ import { useHandleExportOrImportJsonFile } from '../hooks/use-export-json';
24
+ import { useShowDrawer } from '../hooks/use-show-drawer';
25
  import JsonUploadModal from '../json-upload-modal';
26
  import RunDrawer from '../run-drawer';
27
  import { ButtonEdge } from './edge';
 
32
  import { EmailNode } from './node/email-node';
33
  import { GenerateNode } from './node/generate-node';
34
  import { InvokeNode } from './node/invoke-node';
35
+ import { IterationNode, IterationStartNode } from './node/iteration-node';
36
  import { KeywordNode } from './node/keyword-node';
37
  import { LogicNode } from './node/logic-node';
38
  import { MessageNode } from './node/message-node';
 
59
  invokeNode: InvokeNode,
60
  templateNode: TemplateNode,
61
  emailNode: EmailNode,
62
+ group: IterationNode,
63
+ iterationStartNode: IterationStartNode,
64
  };
65
 
66
  const edgeTypes = {
 
82
  onSelectionChange,
83
  } = useSelectCanvasData();
84
  const isValidConnection = useValidateConnection();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  const controlIconClassname = 'text-black';
87
 
 
 
 
 
 
 
 
88
  const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop();
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  const {
91
  handleExportJson,
92
  handleImportJson,
 
95
  hideFileUploadModal,
96
  } = useHandleExportOrImportJsonFile();
97
 
98
+ const {
99
+ onNodeClick,
100
+ onPaneClick,
101
+ clickedNode,
102
+ formDrawerVisible,
103
+ hideFormDrawer,
104
+ singleDebugDrawerVisible,
105
+ hideSingleDebugDrawer,
106
+ showSingleDebugDrawer,
107
+ chatVisible,
108
+ runVisible,
109
+ hideRunOrChatDrawer,
 
 
110
  showChatModal,
111
+ } = useShowDrawer({
112
  drawerVisible,
113
+ hideDrawer,
114
+ });
115
+
116
+ useWatchNodeFormDataChange();
117
 
118
  return (
119
  <div className={styles.canvasWrapper}>
 
162
  strokeWidth: 2,
163
  stroke: 'rgb(202 197 245)',
164
  },
165
+ zIndex: 1001, // https://github.com/xyflow/xyflow/discussions/3498
166
  }}
167
  deleteKeyCode={['Delete', 'Backspace']}
168
  >
web/src/pages/flow/canvas/node/begin-node.tsx CHANGED
@@ -44,7 +44,9 @@ export function BeginNode({ selected, data }: NodeProps<NodeData>) {
44
  fontSize={24}
45
  color={operatorMap[data.label as Operator].color}
46
  ></OperatorIcon>
47
- <div className={styles.nodeTitle}>{t(`flow.begin`)}</div>
 
 
48
  </Flex>
49
  <Flex gap={8} vertical className={styles.generateParameters}>
50
  {query.map((x, idx) => {
 
44
  fontSize={24}
45
  color={operatorMap[data.label as Operator].color}
46
  ></OperatorIcon>
47
+ <div className="truncate text-center font-semibold text-sm">
48
+ {t(`flow.begin`)}
49
+ </div>
50
  </Flex>
51
  <Flex gap={8} vertical className={styles.generateParameters}>
52
  {query.map((x, idx) => {
web/src/pages/flow/canvas/node/dropdown.tsx CHANGED
@@ -3,6 +3,7 @@ import { CopyOutlined } from '@ant-design/icons';
3
  import { Flex, MenuProps } from 'antd';
4
  import { useCallback } from 'react';
5
  import { useTranslation } from 'react-i18next';
 
6
  import { useDuplicateNode } from '../../hooks';
7
  import useGraphStore from '../../store';
8
 
@@ -15,10 +16,17 @@ interface IProps {
15
  const NodeDropdown = ({ id, iconFontColor, label }: IProps) => {
16
  const { t } = useTranslation();
17
  const deleteNodeById = useGraphStore((store) => store.deleteNodeById);
 
 
 
18
 
19
  const deleteNode = useCallback(() => {
20
- deleteNodeById(id);
21
- }, [id, deleteNodeById]);
 
 
 
 
22
 
23
  const duplicateNode = useDuplicateNode();
24
 
 
3
  import { Flex, MenuProps } from 'antd';
4
  import { useCallback } from 'react';
5
  import { useTranslation } from 'react-i18next';
6
+ import { Operator } from '../../constant';
7
  import { useDuplicateNode } from '../../hooks';
8
  import useGraphStore from '../../store';
9
 
 
16
  const NodeDropdown = ({ id, iconFontColor, label }: IProps) => {
17
  const { t } = useTranslation();
18
  const deleteNodeById = useGraphStore((store) => store.deleteNodeById);
19
+ const deleteIterationNodeById = useGraphStore(
20
+ (store) => store.deleteIterationNodeById,
21
+ );
22
 
23
  const deleteNode = useCallback(() => {
24
+ if (label === Operator.Iteration) {
25
+ deleteIterationNodeById(id);
26
+ } else {
27
+ deleteNodeById(id);
28
+ }
29
+ }, [label, deleteIterationNodeById, id, deleteNodeById]);
30
 
31
  const duplicateNode = useDuplicateNode();
32
 
web/src/pages/flow/canvas/node/generate-node.tsx CHANGED
@@ -4,7 +4,7 @@ import { Flex } from 'antd';
4
  import classNames from 'classnames';
5
  import { get } from 'lodash';
6
  import { Handle, NodeProps, Position } from 'reactflow';
7
- import { useGetComponentLabelByValue } from '../../hooks';
8
  import { IGenerateParameter, NodeData } from '../../interface';
9
  import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
10
  import styles from './index.less';
 
4
  import classNames from 'classnames';
5
  import { get } from 'lodash';
6
  import { Handle, NodeProps, Position } from 'reactflow';
7
+ import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
8
  import { IGenerateParameter, NodeData } from '../../interface';
9
  import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
10
  import styles from './index.less';
web/src/pages/flow/canvas/node/index.less CHANGED
@@ -1,15 +1,3 @@
1
- .commonNode() {
2
- box-shadow:
3
- -6px 0 12px 0 rgba(179, 177, 177, 0.08),
4
- -3px 0 6px -4px rgba(0, 0, 0, 0.12),
5
- -6px 0 16px 6px rgba(0, 0, 0, 0.05);
6
-
7
- padding: 10px;
8
- border-radius: 10px;
9
- background: white;
10
- width: 200px;
11
- }
12
-
13
  .dark {
14
  background: rgb(63, 63, 63) !important;
15
  }
@@ -43,6 +31,22 @@
43
  border: 1.5px solid rgb(59, 118, 244);
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  .handle {
47
  display: inline-flex;
48
  align-items: center;
@@ -133,6 +137,12 @@
133
  }
134
  }
135
 
 
 
 
 
 
 
136
  .nodeText {
137
  padding-inline: 0.4em;
138
  padding-block: 0.2em 0.1em;
@@ -142,12 +152,6 @@
142
  .textEllipsis();
143
  }
144
 
145
- .nodeTitle {
146
- font-weight: 600;
147
- text-align: center;
148
- .textEllipsis();
149
- }
150
-
151
  .nodeHeader {
152
  padding-bottom: 12px;
153
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  .dark {
2
  background: rgb(63, 63, 63) !important;
3
  }
 
31
  border: 1.5px solid rgb(59, 118, 244);
32
  }
33
 
34
+ .selectedIterationNode {
35
+ border-bottom: 1.5px solid rgb(59, 118, 244);
36
+ border-left: 1.5px solid rgb(59, 118, 244);
37
+ border-right: 1.5px solid rgb(59, 118, 244);
38
+ }
39
+
40
+ .iterationHeader {
41
+ .commonNodeShadow();
42
+ }
43
+
44
+ .selectedHeader {
45
+ border-top: 1.9px solid rgb(59, 118, 244);
46
+ border-left: 1.9px solid rgb(59, 118, 244);
47
+ border-right: 1.9px solid rgb(59, 118, 244);
48
+ }
49
+
50
  .handle {
51
  display: inline-flex;
52
  align-items: center;
 
137
  }
138
  }
139
 
140
+ .iterationNode {
141
+ .commonNodeShadow();
142
+ border-bottom-left-radius: 10px;
143
+ border-bottom-right-radius: 10px;
144
+ }
145
+
146
  .nodeText {
147
  padding-inline: 0.4em;
148
  padding-block: 0.2em 0.1em;
 
152
  .textEllipsis();
153
  }
154
 
 
 
 
 
 
 
155
  .nodeHeader {
156
  padding-bottom: 12px;
157
  }
web/src/pages/flow/canvas/node/iteration-node.tsx ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTheme } from '@/components/theme-provider';
2
+ import { cn } from '@/lib/utils';
3
+ import { ListRestart } from 'lucide-react';
4
+ import { Handle, NodeProps, NodeResizeControl, Position } from 'reactflow';
5
+ import { NodeData } from '../../interface';
6
+ import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
7
+ import styles from './index.less';
8
+ import NodeHeader from './node-header';
9
+
10
+ function ResizeIcon() {
11
+ return (
12
+ <svg
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ width="20"
15
+ height="20"
16
+ viewBox="0 0 24 24"
17
+ strokeWidth="2"
18
+ stroke="#5025f9"
19
+ fill="none"
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ style={{ position: 'absolute', right: 5, bottom: 5 }}
23
+ >
24
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
25
+ <polyline points="16 20 20 20 20 16" />
26
+ <line x1="14" y1="14" x2="20" y2="20" />
27
+ <polyline points="8 4 4 4 4 8" />
28
+ <line x1="4" y1="4" x2="10" y2="10" />
29
+ </svg>
30
+ );
31
+ }
32
+
33
+ const controlStyle = {
34
+ background: 'transparent',
35
+ border: 'none',
36
+ };
37
+
38
+ export function IterationNode({
39
+ id,
40
+ data,
41
+ isConnectable = true,
42
+ selected,
43
+ }: NodeProps<NodeData>) {
44
+ const { theme } = useTheme();
45
+
46
+ return (
47
+ <section
48
+ className={cn(
49
+ 'w-full h-full bg-zinc-200 opacity-70',
50
+ styles.iterationNode,
51
+ {
52
+ ['bg-gray-800']: theme === 'dark',
53
+ [styles.selectedIterationNode]: selected,
54
+ },
55
+ )}
56
+ >
57
+ <NodeResizeControl style={controlStyle} minWidth={100} minHeight={50}>
58
+ <ResizeIcon />
59
+ </NodeResizeControl>
60
+ <Handle
61
+ id="c"
62
+ type="source"
63
+ position={Position.Left}
64
+ isConnectable={isConnectable}
65
+ className={styles.handle}
66
+ style={LeftHandleStyle}
67
+ ></Handle>
68
+ <Handle
69
+ type="source"
70
+ position={Position.Right}
71
+ isConnectable={isConnectable}
72
+ className={styles.handle}
73
+ id="b"
74
+ style={RightHandleStyle}
75
+ ></Handle>
76
+ <NodeHeader
77
+ id={id}
78
+ name={data.name}
79
+ label={data.label}
80
+ wrapperClassName={cn(
81
+ 'p-2 bg-white rounded-t-[10px] absolute w-full top-[-60px] left-[-0.3px]',
82
+ styles.iterationHeader,
83
+ {
84
+ [`${styles.dark} text-white`]: theme === 'dark',
85
+ [styles.selectedHeader]: selected,
86
+ },
87
+ )}
88
+ ></NodeHeader>
89
+ </section>
90
+ );
91
+ }
92
+
93
+ export function IterationStartNode({
94
+ isConnectable = true,
95
+ selected,
96
+ }: NodeProps<NodeData>) {
97
+ const { theme } = useTheme();
98
+
99
+ return (
100
+ <section
101
+ className={cn('bg-white p-2 rounded-xl', {
102
+ [styles.dark]: theme === 'dark',
103
+ [styles.selectedNode]: selected,
104
+ })}
105
+ >
106
+ <Handle
107
+ type="source"
108
+ position={Position.Right}
109
+ isConnectable={isConnectable}
110
+ className={styles.handle}
111
+ style={RightHandleStyle}
112
+ ></Handle>
113
+ <div>
114
+ <ListRestart className="size-7" />
115
+ </div>
116
+ </section>
117
+ );
118
+ }
web/src/pages/flow/canvas/node/node-header.tsx CHANGED
@@ -8,15 +8,17 @@ import NodeDropdown from './dropdown';
8
  import { NextNodePopover } from './popover';
9
 
10
  import { RunTooltip } from '../../flow-tooltip';
11
- import styles from './index.less';
12
  interface IProps {
13
  id: string;
14
  label: string;
15
  name: string;
16
  gap?: number;
17
  className?: string;
 
18
  }
19
 
 
 
20
  export function RunStatus({ id, name, label }: IProps) {
21
  const { t } = useTranslate('flow');
22
  return (
@@ -35,10 +37,17 @@ export function RunStatus({ id, name, label }: IProps) {
35
  );
36
  }
37
 
38
- const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => {
 
 
 
 
 
 
 
39
  return (
40
- <section>
41
- {label !== Operator.Answer && (
42
  <RunStatus id={id} name={name} label={label}></RunStatus>
43
  )}
44
  <Flex
@@ -52,7 +61,9 @@ const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => {
52
  name={label as Operator}
53
  color={operatorMap[label as Operator].color}
54
  ></OperatorIcon>
55
- <span className={styles.nodeTitle}>{name}</span>
 
 
56
  <NodeDropdown id={id} label={label}></NodeDropdown>
57
  </Flex>
58
  </section>
 
8
  import { NextNodePopover } from './popover';
9
 
10
  import { RunTooltip } from '../../flow-tooltip';
 
11
  interface IProps {
12
  id: string;
13
  label: string;
14
  name: string;
15
  gap?: number;
16
  className?: string;
17
+ wrapperClassName?: string;
18
  }
19
 
20
+ const ExcludedRunStateOperators = [Operator.Answer];
21
+
22
  export function RunStatus({ id, name, label }: IProps) {
23
  const { t } = useTranslate('flow');
24
  return (
 
37
  );
38
  }
39
 
40
+ const NodeHeader = ({
41
+ label,
42
+ id,
43
+ name,
44
+ gap = 4,
45
+ className,
46
+ wrapperClassName,
47
+ }: IProps) => {
48
  return (
49
+ <section className={wrapperClassName}>
50
+ {!ExcludedRunStateOperators.includes(label as Operator) && (
51
  <RunStatus id={id} name={name} label={label}></RunStatus>
52
  )}
53
  <Flex
 
61
  name={label as Operator}
62
  color={operatorMap[label as Operator].color}
63
  ></OperatorIcon>
64
+ <span className="truncate text-center font-semibold text-sm">
65
+ {name}
66
+ </span>
67
  <NodeDropdown id={id} label={label}></NodeDropdown>
68
  </Flex>
69
  </section>
web/src/pages/flow/canvas/node/popover.tsx CHANGED
@@ -3,7 +3,7 @@ import get from 'lodash/get';
3
  import React, { MouseEventHandler, useCallback, useMemo } from 'react';
4
  import JsonView from 'react18-json-view';
5
  import 'react18-json-view/src/style.css';
6
- import { useGetComponentLabelByValue, useReplaceIdWithText } from '../../hooks';
7
 
8
  import { useTheme } from '@/components/theme-provider';
9
  import {
@@ -20,6 +20,7 @@ import {
20
  TableRow,
21
  } from '@/components/ui/table';
22
  import { useTranslate } from '@/hooks/common-hooks';
 
23
 
24
  interface IProps extends React.PropsWithChildren {
25
  nodeId: string;
 
3
  import React, { MouseEventHandler, useCallback, useMemo } from 'react';
4
  import JsonView from 'react18-json-view';
5
  import 'react18-json-view/src/style.css';
6
+ import { useReplaceIdWithText } from '../../hooks';
7
 
8
  import { useTheme } from '@/components/theme-provider';
9
  import {
 
20
  TableRow,
21
  } from '@/components/ui/table';
22
  import { useTranslate } from '@/hooks/common-hooks';
23
+ import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
24
 
25
  interface IProps extends React.PropsWithChildren {
26
  nodeId: string;
web/src/pages/flow/canvas/node/switch-node.tsx CHANGED
@@ -2,7 +2,7 @@ import { useTheme } from '@/components/theme-provider';
2
  import { Divider, Flex } from 'antd';
3
  import classNames from 'classnames';
4
  import { Handle, NodeProps, Position } from 'reactflow';
5
- import { useGetComponentLabelByValue } from '../../hooks';
6
  import { ISwitchCondition, NodeData } from '../../interface';
7
  import { RightHandleStyle } from './handle-icon';
8
  import { useBuildSwitchHandlePositions } from './hooks';
 
2
  import { Divider, Flex } from 'antd';
3
  import classNames from 'classnames';
4
  import { Handle, NodeProps, Position } from 'reactflow';
5
+ import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
6
  import { ISwitchCondition, NodeData } from '../../interface';
7
  import { RightHandleStyle } from './handle-icon';
8
  import { useBuildSwitchHandlePositions } from './hooks';
web/src/pages/flow/canvas/node/template-node.tsx CHANGED
@@ -1,13 +1,13 @@
 
1
  import { Flex } from 'antd';
2
  import classNames from 'classnames';
3
  import { get } from 'lodash';
4
  import { Handle, NodeProps, Position } from 'reactflow';
5
- import { useGetComponentLabelByValue } from '../../hooks';
6
  import { IGenerateParameter, NodeData } from '../../interface';
7
  import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
8
  import NodeHeader from './node-header';
9
 
10
- import { useTheme } from '@/components/theme-provider';
11
  import styles from './index.less';
12
 
13
  export function TemplateNode({
 
1
+ import { useTheme } from '@/components/theme-provider';
2
  import { Flex } from 'antd';
3
  import classNames from 'classnames';
4
  import { get } from 'lodash';
5
  import { Handle, NodeProps, Position } from 'reactflow';
6
+ import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
7
  import { IGenerateParameter, NodeData } from '../../interface';
8
  import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
9
  import NodeHeader from './node-header';
10
 
 
11
  import styles from './index.less';
12
 
13
  export function TemplateNode({
web/src/pages/flow/constant.tsx CHANGED
@@ -50,7 +50,9 @@ import {
50
  } from '@ant-design/icons';
51
  import upperFirst from 'lodash/upperFirst';
52
  import {
 
53
  CloudUpload,
 
54
  ListOrdered,
55
  OptionIcon,
56
  TextCursorInput,
@@ -58,6 +60,8 @@ import {
58
  WrapText,
59
  } from 'lucide-react';
60
 
 
 
61
  export enum Operator {
62
  Begin = 'Begin',
63
  Retrieval = 'Retrieval',
@@ -93,6 +97,8 @@ export enum Operator {
93
  Invoke = 'Invoke',
94
  Template = 'Template',
95
  Email = 'Email',
 
 
96
  }
97
 
98
  export const CommonOperatorList = Object.values(Operator).filter(
@@ -134,6 +140,8 @@ export const operatorIconMap = {
134
  [Operator.Invoke]: InvokeIcon,
135
  [Operator.Template]: TemplateIcon,
136
  [Operator.Email]: EmailIcon,
 
 
137
  };
138
 
139
  export const operatorMap: Record<
@@ -270,6 +278,8 @@ export const operatorMap: Record<
270
  backgroundColor: '#dee0e2',
271
  },
272
  [Operator.Email]: { backgroundColor: '#e6f7ff' },
 
 
273
  };
274
 
275
  export const componentMenuList = [
@@ -306,6 +316,9 @@ export const componentMenuList = [
306
  {
307
  name: Operator.Template,
308
  },
 
 
 
309
  {
310
  name: Operator.Note,
311
  },
@@ -606,6 +619,11 @@ export const initialEmailValues = {
606
  content: '',
607
  };
608
 
 
 
 
 
 
609
  export const CategorizeAnchorPointPositions = [
610
  { top: 1, right: 34 },
611
  { top: 8, right: 18 },
@@ -687,6 +705,8 @@ export const RestrictedUpstreamMap = {
687
  [Operator.Invoke]: [Operator.Begin],
688
  [Operator.Template]: [Operator.Begin, Operator.Relevant],
689
  [Operator.Email]: [Operator.Begin],
 
 
690
  };
691
 
692
  export const NodeMap = {
@@ -724,6 +744,8 @@ export const NodeMap = {
724
  [Operator.Invoke]: 'invokeNode',
725
  [Operator.Template]: 'templateNode',
726
  [Operator.Email]: 'emailNode',
 
 
727
  };
728
 
729
  export const LanguageOptions = [
@@ -2940,4 +2962,5 @@ export const NoDebugOperatorsList = [
2940
  Operator.Message,
2941
  Operator.RewriteQuestion,
2942
  Operator.Switch,
 
2943
  ];
 
50
  } from '@ant-design/icons';
51
  import upperFirst from 'lodash/upperFirst';
52
  import {
53
+ CirclePower,
54
  CloudUpload,
55
+ IterationCcw,
56
  ListOrdered,
57
  OptionIcon,
58
  TextCursorInput,
 
60
  WrapText,
61
  } from 'lucide-react';
62
 
63
+ export const BeginId = 'begin';
64
+
65
  export enum Operator {
66
  Begin = 'Begin',
67
  Retrieval = 'Retrieval',
 
97
  Invoke = 'Invoke',
98
  Template = 'Template',
99
  Email = 'Email',
100
+ Iteration = 'Iteration',
101
+ IterationStart = 'IterationItem',
102
  }
103
 
104
  export const CommonOperatorList = Object.values(Operator).filter(
 
140
  [Operator.Invoke]: InvokeIcon,
141
  [Operator.Template]: TemplateIcon,
142
  [Operator.Email]: EmailIcon,
143
+ [Operator.Iteration]: IterationCcw,
144
+ [Operator.IterationStart]: CirclePower,
145
  };
146
 
147
  export const operatorMap: Record<
 
278
  backgroundColor: '#dee0e2',
279
  },
280
  [Operator.Email]: { backgroundColor: '#e6f7ff' },
281
+ [Operator.Iteration]: { backgroundColor: '#e6f7ff' },
282
+ [Operator.IterationStart]: { backgroundColor: '#e6f7ff' },
283
  };
284
 
285
  export const componentMenuList = [
 
316
  {
317
  name: Operator.Template,
318
  },
319
+ {
320
+ name: Operator.Iteration,
321
+ },
322
  {
323
  name: Operator.Note,
324
  },
 
619
  content: '',
620
  };
621
 
622
+ export const initialIterationValues = {
623
+ delimiter: ',',
624
+ };
625
+ export const initialIterationStartValues = {};
626
+
627
  export const CategorizeAnchorPointPositions = [
628
  { top: 1, right: 34 },
629
  { top: 8, right: 18 },
 
705
  [Operator.Invoke]: [Operator.Begin],
706
  [Operator.Template]: [Operator.Begin, Operator.Relevant],
707
  [Operator.Email]: [Operator.Begin],
708
+ [Operator.Iteration]: [Operator.Begin],
709
+ [Operator.IterationStart]: [Operator.Begin],
710
  };
711
 
712
  export const NodeMap = {
 
744
  [Operator.Invoke]: 'invokeNode',
745
  [Operator.Template]: 'templateNode',
746
  [Operator.Email]: 'emailNode',
747
+ [Operator.Iteration]: 'group',
748
+ [Operator.IterationStart]: 'iterationStartNode',
749
  };
750
 
751
  export const LanguageOptions = [
 
2962
  Operator.Message,
2963
  Operator.RewriteQuestion,
2964
  Operator.Switch,
2965
+ Operator.Iteration,
2966
  ];
web/src/pages/flow/flow-drawer/index.tsx CHANGED
@@ -6,7 +6,7 @@ import { lowerFirst } from 'lodash';
6
  import { Play } from 'lucide-react';
7
  import { useEffect, useRef } from 'react';
8
  import { Node } from 'reactflow';
9
- import { Operator, operatorMap } from '../constant';
10
  import AkShareForm from '../form/akshare-form';
11
  import AnswerForm from '../form/answer-form';
12
  import ArXivForm from '../form/arxiv-form';
@@ -45,6 +45,7 @@ import { getDrawerWidth, needsSingleStepDebugging } from '../utils';
45
  import SingleDebugDrawer from './single-debug-drawer';
46
 
47
  import { RunTooltip } from '../flow-tooltip';
 
48
  import styles from './index.less';
49
 
50
  interface IProps {
@@ -89,6 +90,8 @@ const FormMap = {
89
  [Operator.Note]: () => <></>,
90
  [Operator.Template]: TemplateForm,
91
  [Operator.Email]: EmailForm,
 
 
92
  };
93
 
94
  const EmptyContent = () => <div></div>;
@@ -137,11 +140,15 @@ const FormDrawer = ({
137
  <label htmlFor="" className={styles.title}>
138
  {t('title')}
139
  </label>
140
- <Input
141
- value={name}
142
- onBlur={handleNameBlur}
143
- onChange={handleNameChange}
144
- ></Input>
 
 
 
 
145
  </Flex>
146
  {needsSingleStepDebugging(operatorName) && (
147
  <RunTooltip>
 
6
  import { Play } from 'lucide-react';
7
  import { useEffect, useRef } from 'react';
8
  import { Node } from 'reactflow';
9
+ import { BeginId, Operator, operatorMap } from '../constant';
10
  import AkShareForm from '../form/akshare-form';
11
  import AnswerForm from '../form/answer-form';
12
  import ArXivForm from '../form/arxiv-form';
 
45
  import SingleDebugDrawer from './single-debug-drawer';
46
 
47
  import { RunTooltip } from '../flow-tooltip';
48
+ import IterationForm from '../form/iteration-from';
49
  import styles from './index.less';
50
 
51
  interface IProps {
 
90
  [Operator.Note]: () => <></>,
91
  [Operator.Template]: TemplateForm,
92
  [Operator.Email]: EmailForm,
93
+ [Operator.Iteration]: IterationForm,
94
+ [Operator.IterationStart]: () => <></>,
95
  };
96
 
97
  const EmptyContent = () => <div></div>;
 
140
  <label htmlFor="" className={styles.title}>
141
  {t('title')}
142
  </label>
143
+ {node?.id === BeginId ? (
144
+ <span>{t(BeginId)}</span>
145
+ ) : (
146
+ <Input
147
+ value={name}
148
+ onBlur={handleNameBlur}
149
+ onChange={handleNameChange}
150
+ ></Input>
151
+ )}
152
  </Flex>
153
  {needsSingleStepDebugging(operatorName) && (
154
  <RunTooltip>
web/src/pages/flow/form/akshare-form/index.tsx CHANGED
@@ -12,7 +12,7 @@ const AkShareForm = ({ onValuesChange, form, node }: IOperatorForm) => {
12
  onValuesChange={onValuesChange}
13
  layout={'vertical'}
14
  >
15
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
16
  <TopNItem initialValue={10} max={99}></TopNItem>
17
  </Form>
18
  );
 
12
  onValuesChange={onValuesChange}
13
  layout={'vertical'}
14
  >
15
+ <DynamicInputVariable node={node}></DynamicInputVariable>
16
  <TopNItem initialValue={10} max={99}></TopNItem>
17
  </Form>
18
  );
web/src/pages/flow/form/arxiv-form/index.tsx CHANGED
@@ -23,7 +23,7 @@ const ArXivForm = ({ onValuesChange, form, node }: IOperatorForm) => {
23
  onValuesChange={onValuesChange}
24
  layout={'vertical'}
25
  >
26
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
27
 
28
  <TopNItem initialValue={10}></TopNItem>
29
  <Form.Item label={t('sortBy')} name={'sort_by'}>
 
23
  onValuesChange={onValuesChange}
24
  layout={'vertical'}
25
  >
26
+ <DynamicInputVariable node={node}></DynamicInputVariable>
27
 
28
  <TopNItem initialValue={10}></TopNItem>
29
  <Form.Item label={t('sortBy')} name={'sort_by'}>
web/src/pages/flow/form/baidu-fanyi-form/index.tsx CHANGED
@@ -39,7 +39,7 @@ const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => {
39
  onValuesChange={onValuesChange}
40
  layout={'vertical'}
41
  >
42
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
43
  <Form.Item label={t('appid')} name={'appid'}>
44
  <Input></Input>
45
  </Form.Item>
 
39
  onValuesChange={onValuesChange}
40
  layout={'vertical'}
41
  >
42
+ <DynamicInputVariable node={node}></DynamicInputVariable>
43
  <Form.Item label={t('appid')} name={'appid'}>
44
  <Input></Input>
45
  </Form.Item>
web/src/pages/flow/form/baidu-form/index.tsx CHANGED
@@ -12,7 +12,7 @@ const BaiduForm = ({ onValuesChange, form, node }: IOperatorForm) => {
12
  onValuesChange={onValuesChange}
13
  layout={'vertical'}
14
  >
15
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
16
  <TopNItem initialValue={10}></TopNItem>
17
  </Form>
18
  );
 
12
  onValuesChange={onValuesChange}
13
  layout={'vertical'}
14
  >
15
+ <DynamicInputVariable node={node}></DynamicInputVariable>
16
  <TopNItem initialValue={10}></TopNItem>
17
  </Form>
18
  );
web/src/pages/flow/form/bing-form/index.tsx CHANGED
@@ -21,7 +21,7 @@ const BingForm = ({ onValuesChange, form, node }: IOperatorForm) => {
21
  onValuesChange={onValuesChange}
22
  layout={'vertical'}
23
  >
24
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
25
  <TopNItem initialValue={10}></TopNItem>
26
  <Form.Item label={t('channel')} name={'channel'}>
27
  <Select options={options}></Select>
 
21
  onValuesChange={onValuesChange}
22
  layout={'vertical'}
23
  >
24
+ <DynamicInputVariable node={node}></DynamicInputVariable>
25
  <TopNItem initialValue={10}></TopNItem>
26
  <Form.Item label={t('channel')} name={'channel'}>
27
  <Select options={options}></Select>
web/src/pages/flow/form/categorize-form/index.tsx CHANGED
@@ -24,7 +24,7 @@ const CategorizeForm = ({ form, onValuesChange, node }: IOperatorForm) => {
24
  initialValues={{ items: [{}] }}
25
  layout={'vertical'}
26
  >
27
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
28
  <Form.Item
29
  name={'llm_id'}
30
  label={t('model', { keyPrefix: 'chat' })}
 
24
  initialValues={{ items: [{}] }}
25
  layout={'vertical'}
26
  >
27
+ <DynamicInputVariable node={node}></DynamicInputVariable>
28
  <Form.Item
29
  name={'llm_id'}
30
  label={t('model', { keyPrefix: 'chat' })}
web/src/pages/flow/form/components/dynamic-input-variable.tsx CHANGED
@@ -1,13 +1,15 @@
1
  import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
2
  import { Button, Collapse, Flex, Form, Input, Select } from 'antd';
3
-
4
  import { PropsWithChildren, useCallback } from 'react';
5
  import { useTranslation } from 'react-i18next';
6
- import { useBuildComponentIdSelectOptions } from '../../hooks';
 
 
 
7
  import styles from './index.less';
8
 
9
  interface IProps {
10
- nodeId?: string;
11
  }
12
 
13
  enum VariableType {
@@ -18,9 +20,12 @@ enum VariableType {
18
  const getVariableName = (type: string) =>
19
  type === VariableType.Reference ? 'component_id' : 'value';
20
 
21
- const DynamicVariableForm = ({ nodeId }: IProps) => {
22
  const { t } = useTranslation();
23
- const valueOptions = useBuildComponentIdSelectOptions(nodeId);
 
 
 
24
  const form = Form.useFormInstance();
25
 
26
  const options = [
@@ -114,11 +119,11 @@ export function FormCollapse({
114
  );
115
  }
116
 
117
- const DynamicInputVariable = ({ nodeId }: IProps) => {
118
  const { t } = useTranslation();
119
  return (
120
  <FormCollapse title={t('flow.input')}>
121
- <DynamicVariableForm nodeId={nodeId}></DynamicVariableForm>
122
  </FormCollapse>
123
  );
124
  };
 
1
  import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
2
  import { Button, Collapse, Flex, Form, Input, Select } from 'antd';
 
3
  import { PropsWithChildren, useCallback } from 'react';
4
  import { useTranslation } from 'react-i18next';
5
+ import { Node } from 'reactflow';
6
+ import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
7
+ import { NodeData } from '../../interface';
8
+
9
  import styles from './index.less';
10
 
11
  interface IProps {
12
+ node?: Node<NodeData>;
13
  }
14
 
15
  enum VariableType {
 
20
  const getVariableName = (type: string) =>
21
  type === VariableType.Reference ? 'component_id' : 'value';
22
 
23
+ const DynamicVariableForm = ({ node }: IProps) => {
24
  const { t } = useTranslation();
25
+ const valueOptions = useBuildComponentIdSelectOptions(
26
+ node?.id,
27
+ node?.parentId,
28
+ );
29
  const form = Form.useFormInstance();
30
 
31
  const options = [
 
119
  );
120
  }
121
 
122
+ const DynamicInputVariable = ({ node }: IProps) => {
123
  const { t } = useTranslation();
124
  return (
125
  <FormCollapse title={t('flow.input')}>
126
+ <DynamicVariableForm node={node}></DynamicVariableForm>
127
  </FormCollapse>
128
  );
129
  };
web/src/pages/flow/form/crawler-form/index.tsx CHANGED
@@ -20,7 +20,7 @@ const CrawlerForm = ({ onValuesChange, form, node }: IOperatorForm) => {
20
  onValuesChange={onValuesChange}
21
  layout={'vertical'}
22
  >
23
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
24
  <Form.Item label={t('proxy')} name={'proxy'}>
25
  <Input placeholder="like: http://127.0.0.1:8888"></Input>
26
  </Form.Item>
 
20
  onValuesChange={onValuesChange}
21
  layout={'vertical'}
22
  >
23
+ <DynamicInputVariable node={node}></DynamicInputVariable>
24
  <Form.Item label={t('proxy')} name={'proxy'}>
25
  <Input placeholder="like: http://127.0.0.1:8888"></Input>
26
  </Form.Item>
web/src/pages/flow/form/deepl-form/index.tsx CHANGED
@@ -18,7 +18,7 @@ const DeepLForm = ({ onValuesChange, form, node }: IOperatorForm) => {
18
  onValuesChange={onValuesChange}
19
  layout={'vertical'}
20
  >
21
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
22
  <TopNItem initialValue={5}></TopNItem>
23
  <Form.Item label={t('authKey')} name={'auth_key'}>
24
  <Select options={options}></Select>
 
18
  onValuesChange={onValuesChange}
19
  layout={'vertical'}
20
  >
21
+ <DynamicInputVariable node={node}></DynamicInputVariable>
22
  <TopNItem initialValue={5}></TopNItem>
23
  <Form.Item label={t('authKey')} name={'auth_key'}>
24
  <Select options={options}></Select>
web/src/pages/flow/form/duckduckgo-form/index.tsx CHANGED
@@ -21,7 +21,7 @@ const DuckDuckGoForm = ({ onValuesChange, form, node }: IOperatorForm) => {
21
  onValuesChange={onValuesChange}
22
  layout={'vertical'}
23
  >
24
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
25
  <TopNItem initialValue={10}></TopNItem>
26
  <Form.Item
27
  label={t('channel')}
 
21
  onValuesChange={onValuesChange}
22
  layout={'vertical'}
23
  >
24
+ <DynamicInputVariable node={node}></DynamicInputVariable>
25
  <TopNItem initialValue={10}></TopNItem>
26
  <Form.Item
27
  label={t('channel')}
web/src/pages/flow/form/email-form/index.tsx CHANGED
@@ -14,7 +14,7 @@ const EmailForm = ({ onValuesChange, form, node }: IOperatorForm) => {
14
  onValuesChange={onValuesChange}
15
  layout={'vertical'}
16
  >
17
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
18
 
19
  {/* SMTP服务器配置 */}
20
  <Form.Item label={t('smtpServer')} name={'smtp_server'}>
 
14
  onValuesChange={onValuesChange}
15
  layout={'vertical'}
16
  >
17
+ <DynamicInputVariable node={node}></DynamicInputVariable>
18
 
19
  {/* SMTP服务器配置 */}
20
  <Form.Item label={t('smtpServer')} name={'smtp_server'}>
web/src/pages/flow/form/exesql-form/index.tsx CHANGED
@@ -24,7 +24,7 @@ const ExeSQLForm = ({ onValuesChange, form, node }: IOperatorForm) => {
24
  onValuesChange={onValuesChange}
25
  layout={'vertical'}
26
  >
27
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
28
  <Form.Item
29
  label={t('dbType')}
30
  name={'db_type'}
 
24
  onValuesChange={onValuesChange}
25
  layout={'vertical'}
26
  >
27
+ <DynamicInputVariable node={node}></DynamicInputVariable>
28
  <Form.Item
29
  label={t('dbType')}
30
  name={'db_type'}
web/src/pages/flow/form/generate-form/dynamic-parameters.tsx CHANGED
@@ -2,14 +2,14 @@ import { EditableCell, EditableRow } from '@/components/editable-cell';
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import { DeleteOutlined } from '@ant-design/icons';
4
  import { Button, Flex, Select, Table, TableProps } from 'antd';
5
- import { IGenerateParameter } from '../../interface';
6
-
7
- import { useBuildComponentIdSelectOptions } from '../../hooks';
8
  import { useHandleOperateParameters } from './hooks';
9
- import styles from './index.less';
10
 
 
11
  interface IProps {
12
- nodeId?: string;
13
  }
14
 
15
  const components = {
@@ -19,10 +19,11 @@ const components = {
19
  },
20
  };
21
 
22
- const DynamicParameters = ({ nodeId }: IProps) => {
 
23
  const { t } = useTranslate('flow');
24
 
25
- const options = useBuildComponentIdSelectOptions(nodeId);
26
  const {
27
  dataSource,
28
  handleAdd,
 
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import { DeleteOutlined } from '@ant-design/icons';
4
  import { Button, Flex, Select, Table, TableProps } from 'antd';
5
+ import { Node } from 'reactflow';
6
+ import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
7
+ import { IGenerateParameter, NodeData } from '../../interface';
8
  import { useHandleOperateParameters } from './hooks';
 
9
 
10
+ import styles from './index.less';
11
  interface IProps {
12
+ node?: Node<NodeData>;
13
  }
14
 
15
  const components = {
 
19
  },
20
  };
21
 
22
+ const DynamicParameters = ({ node }: IProps) => {
23
+ const nodeId = node?.id;
24
  const { t } = useTranslate('flow');
25
 
26
+ const options = useBuildComponentIdSelectOptions(nodeId, node?.parentId);
27
  const {
28
  dataSource,
29
  handleAdd,
web/src/pages/flow/form/generate-form/index.tsx CHANGED
@@ -49,7 +49,7 @@ const GenerateForm = ({ onValuesChange, form, node }: IOperatorForm) => {
49
  <MessageHistoryWindowSizeItem
50
  initialValue={12}
51
  ></MessageHistoryWindowSizeItem>
52
- <DynamicParameters nodeId={node?.id}></DynamicParameters>
53
  </Form>
54
  );
55
  };
 
49
  <MessageHistoryWindowSizeItem
50
  initialValue={12}
51
  ></MessageHistoryWindowSizeItem>
52
+ <DynamicParameters node={node}></DynamicParameters>
53
  </Form>
54
  );
55
  };
web/src/pages/flow/form/github-form/index.tsx CHANGED
@@ -12,7 +12,7 @@ const GithubForm = ({ onValuesChange, form, node }: IOperatorForm) => {
12
  onValuesChange={onValuesChange}
13
  layout={'vertical'}
14
  >
15
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
16
  <TopNItem initialValue={5}></TopNItem>
17
  </Form>
18
  );
 
12
  onValuesChange={onValuesChange}
13
  layout={'vertical'}
14
  >
15
+ <DynamicInputVariable node={node}></DynamicInputVariable>
16
  <TopNItem initialValue={5}></TopNItem>
17
  </Form>
18
  );
web/src/pages/flow/form/google-form/index.tsx CHANGED
@@ -16,7 +16,7 @@ const GoogleForm = ({ onValuesChange, form, node }: IOperatorForm) => {
16
  onValuesChange={onValuesChange}
17
  layout={'vertical'}
18
  >
19
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
20
  <TopNItem initialValue={10}></TopNItem>
21
  <Form.Item label={t('apiKey')} name={'api_key'}>
22
  <Input></Input>
 
16
  onValuesChange={onValuesChange}
17
  layout={'vertical'}
18
  >
19
+ <DynamicInputVariable node={node}></DynamicInputVariable>
20
  <TopNItem initialValue={10}></TopNItem>
21
  <Form.Item label={t('apiKey')} name={'api_key'}>
22
  <Input></Input>
web/src/pages/flow/form/google-scholar-form/index.tsx CHANGED
@@ -45,7 +45,7 @@ const GoogleScholarForm = ({ onValuesChange, form, node }: IOperatorForm) => {
45
  onValuesChange={onValuesChange}
46
  layout={'vertical'}
47
  >
48
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
49
  <TopNItem initialValue={5}></TopNItem>
50
  <Form.Item
51
  label={t('sortBy')}
 
45
  onValuesChange={onValuesChange}
46
  layout={'vertical'}
47
  >
48
+ <DynamicInputVariable node={node}></DynamicInputVariable>
49
  <TopNItem initialValue={5}></TopNItem>
50
  <Form.Item
51
  label={t('sortBy')}
web/src/pages/flow/form/invoke-form/dynamic-variables.tsx CHANGED
@@ -2,15 +2,16 @@ import { EditableCell, EditableRow } from '@/components/editable-cell';
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import { DeleteOutlined } from '@ant-design/icons';
4
  import { Button, Collapse, Flex, Input, Select, Table, TableProps } from 'antd';
5
- import { useBuildComponentIdSelectOptions } from '../../hooks';
6
- import { IInvokeVariable } from '../../interface';
 
7
  import { useHandleOperateParameters } from './hooks';
8
 
9
- import { trim } from 'lodash';
10
  import styles from './index.less';
11
 
12
  interface IProps {
13
- nodeId?: string;
14
  }
15
 
16
  const components = {
@@ -20,10 +21,11 @@ const components = {
20
  },
21
  };
22
 
23
- const DynamicVariablesForm = ({ nodeId }: IProps) => {
 
24
  const { t } = useTranslate('flow');
25
 
26
- const options = useBuildComponentIdSelectOptions(nodeId);
27
  const {
28
  dataSource,
29
  handleAdd,
 
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import { DeleteOutlined } from '@ant-design/icons';
4
  import { Button, Collapse, Flex, Input, Select, Table, TableProps } from 'antd';
5
+ import { trim } from 'lodash';
6
+ import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
7
+ import { IInvokeVariable, NodeData } from '../../interface';
8
  import { useHandleOperateParameters } from './hooks';
9
 
10
+ import { Node } from 'reactflow';
11
  import styles from './index.less';
12
 
13
  interface IProps {
14
+ node?: Node<NodeData>;
15
  }
16
 
17
  const components = {
 
21
  },
22
  };
23
 
24
+ const DynamicVariablesForm = ({ node }: IProps) => {
25
+ const nodeId = node?.id;
26
  const { t } = useTranslate('flow');
27
 
28
+ const options = useBuildComponentIdSelectOptions(nodeId, node?.parentId);
29
  const {
30
  dataSource,
31
  handleAdd,
web/src/pages/flow/form/invoke-form/index.tsx CHANGED
@@ -69,7 +69,7 @@ const InvokeForm = ({ onValuesChange, form, node }: IOperatorForm) => {
69
  >
70
  <Switch />
71
  </Form.Item>
72
- <DynamicVariablesForm nodeId={node?.id}></DynamicVariablesForm>
73
  </Form>
74
  </>
75
  );
 
69
  >
70
  <Switch />
71
  </Form.Item>
72
+ <DynamicVariablesForm node={node}></DynamicVariablesForm>
73
  </Form>
74
  </>
75
  );
web/src/pages/flow/form/iteration-from/index.tsx ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { CommaIcon, SemicolonIcon } from '@/assets/icon/Icon';
2
+ import { Form, Select } from 'antd';
3
+ import {
4
+ CornerDownLeft,
5
+ IndentIncrease,
6
+ Minus,
7
+ Slash,
8
+ Underline,
9
+ } from 'lucide-react';
10
+ import { useMemo } from 'react';
11
+ import { useTranslation } from 'react-i18next';
12
+ import { IOperatorForm } from '../../interface';
13
+ import DynamicInputVariable from '../components/dynamic-input-variable';
14
+
15
+ const optionList = [
16
+ {
17
+ value: ',',
18
+ icon: CommaIcon,
19
+ text: 'comma',
20
+ },
21
+ {
22
+ value: '\n',
23
+ icon: CornerDownLeft,
24
+ text: 'lineBreak',
25
+ },
26
+ {
27
+ value: 'tab',
28
+ icon: IndentIncrease,
29
+ text: 'tab',
30
+ },
31
+ {
32
+ value: '_',
33
+ icon: Underline,
34
+ text: 'underline',
35
+ },
36
+ {
37
+ value: '/',
38
+ icon: Slash,
39
+ text: 'diagonal',
40
+ },
41
+ {
42
+ value: '-',
43
+ icon: Minus,
44
+ text: 'minus',
45
+ },
46
+ {
47
+ value: ';',
48
+ icon: SemicolonIcon,
49
+ text: 'semicolon',
50
+ },
51
+ ];
52
+
53
+ const IterationForm = ({ onValuesChange, form, node }: IOperatorForm) => {
54
+ const { t } = useTranslation();
55
+
56
+ const options = useMemo(() => {
57
+ return optionList.map((x) => {
58
+ let Icon = x.icon;
59
+
60
+ return {
61
+ value: x.value,
62
+ label: (
63
+ <div className="flex items-center gap-2">
64
+ <Icon className={'size-4'}></Icon>
65
+ {t(`flow.delimiterOptions.${x.text}`)}
66
+ </div>
67
+ ),
68
+ };
69
+ });
70
+ }, [t]);
71
+
72
+ return (
73
+ <Form
74
+ name="basic"
75
+ autoComplete="off"
76
+ form={form}
77
+ onValuesChange={onValuesChange}
78
+ layout={'vertical'}
79
+ >
80
+ <DynamicInputVariable node={node}></DynamicInputVariable>
81
+ <Form.Item
82
+ name={['delimiter']}
83
+ label={t('knowledgeDetails.delimiter')}
84
+ initialValue={`\\n!?;。;!?`}
85
+ rules={[{ required: true }]}
86
+ tooltip={t('flow.delimiterTip')}
87
+ >
88
+ <Select options={options}></Select>
89
+ </Form.Item>
90
+ </Form>
91
+ );
92
+ };
93
+
94
+ export default IterationForm;
web/src/pages/flow/form/jin10-form/index.tsx CHANGED
@@ -65,7 +65,7 @@ const Jin10Form = ({ onValuesChange, form, node }: IOperatorForm) => {
65
  onValuesChange={onValuesChange}
66
  layout={'vertical'}
67
  >
68
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
69
  <Form.Item label={t('type')} name={'type'} initialValue={'flash'}>
70
  <Select options={jin10TypeOptions}></Select>
71
  </Form.Item>
 
65
  onValuesChange={onValuesChange}
66
  layout={'vertical'}
67
  >
68
+ <DynamicInputVariable node={node}></DynamicInputVariable>
69
  <Form.Item label={t('type')} name={'type'} initialValue={'flash'}>
70
  <Select options={jin10TypeOptions}></Select>
71
  </Form.Item>
web/src/pages/flow/form/keyword-extract-form/index.tsx CHANGED
@@ -16,7 +16,7 @@ const KeywordExtractForm = ({ onValuesChange, form, node }: IOperatorForm) => {
16
  onValuesChange={onValuesChange}
17
  layout={'vertical'}
18
  >
19
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
20
  <Form.Item
21
  name={'llm_id'}
22
  label={t('model', { keyPrefix: 'chat' })}
 
16
  onValuesChange={onValuesChange}
17
  layout={'vertical'}
18
  >
19
+ <DynamicInputVariable node={node}></DynamicInputVariable>
20
  <Form.Item
21
  name={'llm_id'}
22
  label={t('model', { keyPrefix: 'chat' })}
web/src/pages/flow/form/pubmed-form/index.tsx CHANGED
@@ -15,7 +15,7 @@ const PubMedForm = ({ onValuesChange, form, node }: IOperatorForm) => {
15
  onValuesChange={onValuesChange}
16
  layout={'vertical'}
17
  >
18
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
19
  <TopNItem initialValue={10}></TopNItem>
20
  <Form.Item
21
  label={t('email')}
 
15
  onValuesChange={onValuesChange}
16
  layout={'vertical'}
17
  >
18
+ <DynamicInputVariable node={node}></DynamicInputVariable>
19
  <TopNItem initialValue={10}></TopNItem>
20
  <Form.Item
21
  label={t('email')}
web/src/pages/flow/form/qweather-form/index.tsx CHANGED
@@ -55,7 +55,7 @@ const QWeatherForm = ({ onValuesChange, form, node }: IOperatorForm) => {
55
  onValuesChange={onValuesChange}
56
  layout={'vertical'}
57
  >
58
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
59
  <Form.Item label={t('webApiKey')} name={'web_apikey'}>
60
  <Input></Input>
61
  </Form.Item>
 
55
  onValuesChange={onValuesChange}
56
  layout={'vertical'}
57
  >
58
+ <DynamicInputVariable node={node}></DynamicInputVariable>
59
  <Form.Item label={t('webApiKey')} name={'web_apikey'}>
60
  <Input></Input>
61
  </Form.Item>
web/src/pages/flow/form/retrieval-form/index.tsx CHANGED
@@ -32,7 +32,7 @@ const RetrievalForm = ({ onValuesChange, form, node }: IOperatorForm) => {
32
  form={form}
33
  layout={'vertical'}
34
  >
35
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
36
  <SimilaritySlider
37
  isTooltipShown
38
  vectorSimilarityWeightName="keywords_similarity_weight"
 
32
  form={form}
33
  layout={'vertical'}
34
  >
35
+ <DynamicInputVariable node={node}></DynamicInputVariable>
36
  <SimilaritySlider
37
  isTooltipShown
38
  vectorSimilarityWeightName="keywords_similarity_weight"
web/src/pages/flow/form/switch-form/index.tsx CHANGED
@@ -9,7 +9,7 @@ import {
9
  SwitchOperatorOptions,
10
  } from '../../constant';
11
  import { useBuildFormSelectOptions } from '../../form-hooks';
12
- import { useBuildComponentIdSelectOptions } from '../../hooks';
13
  import { IOperatorForm, ISwitchForm } from '../../interface';
14
  import { getOtherFieldValues } from '../../utils';
15
 
@@ -43,7 +43,10 @@ const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
43
  }));
44
  }, [t]);
45
 
46
- const componentIdOptions = useBuildComponentIdSelectOptions(node?.id);
 
 
 
47
 
48
  return (
49
  <Form
 
9
  SwitchOperatorOptions,
10
  } from '../../constant';
11
  import { useBuildFormSelectOptions } from '../../form-hooks';
12
+ import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
13
  import { IOperatorForm, ISwitchForm } from '../../interface';
14
  import { getOtherFieldValues } from '../../utils';
15
 
 
43
  }));
44
  }, [t]);
45
 
46
+ const componentIdOptions = useBuildComponentIdSelectOptions(
47
+ node?.id,
48
+ node?.parentId,
49
+ );
50
 
51
  return (
52
  <Form
web/src/pages/flow/form/template-form/index.tsx CHANGED
@@ -18,7 +18,7 @@ const TemplateForm = ({ onValuesChange, form, node }: IOperatorForm) => {
18
  <Input.TextArea rows={8} placeholder={t('flow.blank')} />
19
  </Form.Item>
20
 
21
- <DynamicParameters nodeId={node?.id}></DynamicParameters>
22
  </Form>
23
  );
24
  };
 
18
  <Input.TextArea rows={8} placeholder={t('flow.blank')} />
19
  </Form.Item>
20
 
21
+ <DynamicParameters node={node}></DynamicParameters>
22
  </Form>
23
  );
24
  };
web/src/pages/flow/form/tushare-form/index.tsx CHANGED
@@ -56,7 +56,7 @@ const TuShareForm = ({ onValuesChange, form, node }: IOperatorForm) => {
56
  onValuesChange={onValuesChange}
57
  layout={'vertical'}
58
  >
59
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
60
  <Form.Item
61
  label={t('token')}
62
  name={'token'}
 
56
  onValuesChange={onValuesChange}
57
  layout={'vertical'}
58
  >
59
+ <DynamicInputVariable node={node}></DynamicInputVariable>
60
  <Form.Item
61
  label={t('token')}
62
  name={'token'}
web/src/pages/flow/form/wencai-form/index.tsx CHANGED
@@ -24,7 +24,7 @@ const WenCaiForm = ({ onValuesChange, form, node }: IOperatorForm) => {
24
  onValuesChange={onValuesChange}
25
  layout={'vertical'}
26
  >
27
- <DynamicInputVariable nodeId={node?.id}></DynamicInputVariable>
28
  <TopNItem initialValue={20} max={99}></TopNItem>
29
  <Form.Item label={t('queryType')} name={'query_type'}>
30
  <Select options={wenCaiQueryTypeOptions}></Select>
 
24
  onValuesChange={onValuesChange}
25
  layout={'vertical'}
26
  >
27
+ <DynamicInputVariable node={node}></DynamicInputVariable>
28
  <TopNItem initialValue={20} max={99}></TopNItem>
29
  <Form.Item label={t('queryType')} name={'query_type'}>
30
  <Select options={wenCaiQueryTypeOptions}></Select>