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
- web/src/assets/icon/Icon.tsx +38 -0
- web/src/components/delimiter.tsx +9 -2
- web/src/interfaces/database/flow.ts +1 -0
- web/src/less/mixins.less +20 -0
- web/src/locales/en.ts +16 -0
- web/src/locales/zh-traditional.ts +14 -0
- web/src/locales/zh.ts +14 -0
- web/src/pages/flow/canvas/edge/index.tsx +1 -0
- web/src/pages/flow/canvas/index.less +6 -0
- web/src/pages/flow/canvas/index.tsx +23 -82
- web/src/pages/flow/canvas/node/begin-node.tsx +3 -1
- web/src/pages/flow/canvas/node/dropdown.tsx +10 -2
- web/src/pages/flow/canvas/node/generate-node.tsx +1 -1
- web/src/pages/flow/canvas/node/index.less +22 -18
- web/src/pages/flow/canvas/node/iteration-node.tsx +118 -0
- web/src/pages/flow/canvas/node/node-header.tsx +16 -5
- web/src/pages/flow/canvas/node/popover.tsx +2 -1
- web/src/pages/flow/canvas/node/switch-node.tsx +1 -1
- web/src/pages/flow/canvas/node/template-node.tsx +2 -2
- web/src/pages/flow/constant.tsx +23 -0
- web/src/pages/flow/flow-drawer/index.tsx +13 -6
- web/src/pages/flow/form/akshare-form/index.tsx +1 -1
- web/src/pages/flow/form/arxiv-form/index.tsx +1 -1
- web/src/pages/flow/form/baidu-fanyi-form/index.tsx +1 -1
- web/src/pages/flow/form/baidu-form/index.tsx +1 -1
- web/src/pages/flow/form/bing-form/index.tsx +1 -1
- web/src/pages/flow/form/categorize-form/index.tsx +1 -1
- web/src/pages/flow/form/components/dynamic-input-variable.tsx +12 -7
- web/src/pages/flow/form/crawler-form/index.tsx +1 -1
- web/src/pages/flow/form/deepl-form/index.tsx +1 -1
- web/src/pages/flow/form/duckduckgo-form/index.tsx +1 -1
- web/src/pages/flow/form/email-form/index.tsx +1 -1
- web/src/pages/flow/form/exesql-form/index.tsx +1 -1
- web/src/pages/flow/form/generate-form/dynamic-parameters.tsx +8 -7
- web/src/pages/flow/form/generate-form/index.tsx +1 -1
- web/src/pages/flow/form/github-form/index.tsx +1 -1
- web/src/pages/flow/form/google-form/index.tsx +1 -1
- web/src/pages/flow/form/google-scholar-form/index.tsx +1 -1
- web/src/pages/flow/form/invoke-form/dynamic-variables.tsx +8 -6
- web/src/pages/flow/form/invoke-form/index.tsx +1 -1
- web/src/pages/flow/form/iteration-from/index.tsx +94 -0
- web/src/pages/flow/form/jin10-form/index.tsx +1 -1
- web/src/pages/flow/form/keyword-extract-form/index.tsx +1 -1
- web/src/pages/flow/form/pubmed-form/index.tsx +1 -1
- web/src/pages/flow/form/qweather-form/index.tsx +1 -1
- web/src/pages/flow/form/retrieval-form/index.tsx +1 -1
- web/src/pages/flow/form/switch-form/index.tsx +5 -2
- web/src/pages/flow/form/template-form/index.tsx +1 -1
- web/src/pages/flow/form/tushare-form/index.tsx +1 -1
- 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
|
|
|
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
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 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
hideChatModal,
|
171 |
-
hideRunModal,
|
172 |
showChatModal,
|
173 |
-
|
174 |
drawerVisible,
|
175 |
-
|
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=
|
|
|
|
|
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 |
-
|
21 |
-
|
|
|
|
|
|
|
|
|
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 = ({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
return (
|
40 |
-
<section>
|
41 |
-
{label
|
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=
|
|
|
|
|
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 {
|
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 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
|
|
|
|
|
|
|
|
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
|
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
|
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
|
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
|
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
|
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
|
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 {
|
|
|
|
|
|
|
7 |
import styles from './index.less';
|
8 |
|
9 |
interface IProps {
|
10 |
-
|
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 = ({
|
22 |
const { t } = useTranslation();
|
23 |
-
const valueOptions = useBuildComponentIdSelectOptions(
|
|
|
|
|
|
|
24 |
const form = Form.useFormInstance();
|
25 |
|
26 |
const options = [
|
@@ -114,11 +119,11 @@ export function FormCollapse({
|
|
114 |
);
|
115 |
}
|
116 |
|
117 |
-
const DynamicInputVariable = ({
|
118 |
const { t } = useTranslation();
|
119 |
return (
|
120 |
<FormCollapse title={t('flow.input')}>
|
121 |
-
<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
|
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
|
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
|
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
|
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
|
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 {
|
6 |
-
|
7 |
-
import {
|
8 |
import { useHandleOperateParameters } from './hooks';
|
9 |
-
import styles from './index.less';
|
10 |
|
|
|
11 |
interface IProps {
|
12 |
-
|
13 |
}
|
14 |
|
15 |
const components = {
|
@@ -19,10 +19,11 @@ const components = {
|
|
19 |
},
|
20 |
};
|
21 |
|
22 |
-
const DynamicParameters = ({
|
|
|
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
|
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
|
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
|
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
|
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 {
|
6 |
-
import {
|
|
|
7 |
import { useHandleOperateParameters } from './hooks';
|
8 |
|
9 |
-
import {
|
10 |
import styles from './index.less';
|
11 |
|
12 |
interface IProps {
|
13 |
-
|
14 |
}
|
15 |
|
16 |
const components = {
|
@@ -20,10 +21,11 @@ const components = {
|
|
20 |
},
|
21 |
};
|
22 |
|
23 |
-
const DynamicVariablesForm = ({
|
|
|
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
|
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
|
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
|
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
|
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
|
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
|
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(
|
|
|
|
|
|
|
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
|
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
|
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
|
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>
|