balibabu
commited on
Commit
·
b9c5b85
1
Parent(s):
de65b37
Feat: Supports to debug single component in Agent. #3993 (#4007)
Browse files### What problem does this PR solve?
Feat: Supports to debug single component in Agent. #3993
Fix: The github button on the login page is displayed incorrectly #4002
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/hooks/flow-hooks.ts +48 -0
- web/src/interfaces/request/flow.ts +4 -0
- web/src/locales/en.ts +5 -8
- web/src/locales/zh-traditional.ts +1 -1
- web/src/locales/zh.ts +2 -2
- web/src/pages/flow/canvas/index.tsx +26 -9
- web/src/pages/flow/canvas/node/node-header.tsx +17 -8
- web/src/pages/flow/constant.tsx +10 -0
- web/src/pages/flow/debug-content/index.less +5 -0
- web/src/pages/flow/debug-content/index.tsx +238 -0
- web/src/pages/flow/debug-content/popover-form.tsx +74 -0
- web/src/pages/flow/flow-drawer/index.less +6 -0
- web/src/pages/flow/flow-drawer/index.tsx +32 -5
- web/src/pages/flow/flow-drawer/single-debug-drawer/index.tsx +81 -0
- web/src/pages/flow/flow-tooltip.tsx +22 -0
- web/src/pages/flow/form/invoke-form/index.tsx +3 -1
- web/src/pages/flow/hooks.tsx +18 -1
- web/src/pages/flow/run-drawer/index.tsx +16 -205
- web/src/pages/flow/utils.ts +11 -2
- web/src/pages/login/index.tsx +1 -1
- web/src/services/flow-service.ts +10 -0
- web/src/utils/api.ts +2 -0
- web/src/utils/request.ts +13 -19
web/src/hooks/flow-hooks.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import { ResponseType } from '@/interfaces/database/base';
|
2 |
import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
|
|
|
3 |
import i18n from '@/locales/config';
|
4 |
import flowService from '@/services/flow-service';
|
5 |
import { buildMessageListWithUuid } from '@/utils/chat';
|
@@ -220,3 +221,50 @@ export const useTestDbConnect = () => {
|
|
220 |
|
221 |
return { data, loading, testDbConnect: mutateAsync };
|
222 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import { ResponseType } from '@/interfaces/database/base';
|
2 |
import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
|
3 |
+
import { IDebugSingleRequestBody } from '@/interfaces/request/flow';
|
4 |
import i18n from '@/locales/config';
|
5 |
import flowService from '@/services/flow-service';
|
6 |
import { buildMessageListWithUuid } from '@/utils/chat';
|
|
|
221 |
|
222 |
return { data, loading, testDbConnect: mutateAsync };
|
223 |
};
|
224 |
+
|
225 |
+
export const useFetchInputElements = (componentId?: string) => {
|
226 |
+
const { id } = useParams();
|
227 |
+
|
228 |
+
const { data, isPending: loading } = useQuery({
|
229 |
+
queryKey: ['fetchInputElements', id, componentId],
|
230 |
+
initialData: [],
|
231 |
+
enabled: !!id && !!componentId,
|
232 |
+
retryOnMount: false,
|
233 |
+
refetchOnWindowFocus: false,
|
234 |
+
refetchOnReconnect: false,
|
235 |
+
gcTime: 0,
|
236 |
+
queryFn: async () => {
|
237 |
+
try {
|
238 |
+
const { data } = await flowService.getInputElements({
|
239 |
+
id,
|
240 |
+
component_id: componentId,
|
241 |
+
});
|
242 |
+
return data?.data ?? [];
|
243 |
+
} catch (error) {
|
244 |
+
console.log('🚀 ~ queryFn: ~ error:', error);
|
245 |
+
}
|
246 |
+
},
|
247 |
+
});
|
248 |
+
|
249 |
+
return { data, loading };
|
250 |
+
};
|
251 |
+
|
252 |
+
export const useDebugSingle = () => {
|
253 |
+
const { id } = useParams();
|
254 |
+
const {
|
255 |
+
data,
|
256 |
+
isPending: loading,
|
257 |
+
mutateAsync,
|
258 |
+
} = useMutation({
|
259 |
+
mutationKey: ['debugSingle'],
|
260 |
+
mutationFn: async (params: IDebugSingleRequestBody) => {
|
261 |
+
const ret = await flowService.debugSingle({ id, ...params });
|
262 |
+
if (ret?.data?.code !== 0) {
|
263 |
+
message.error(ret?.data?.message);
|
264 |
+
}
|
265 |
+
return ret?.data?.data;
|
266 |
+
},
|
267 |
+
});
|
268 |
+
|
269 |
+
return { data, loading, debugSingle: mutateAsync };
|
270 |
+
};
|
web/src/interfaces/request/flow.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface IDebugSingleRequestBody {
|
2 |
+
component_id: string;
|
3 |
+
params: any[];
|
4 |
+
}
|
web/src/locales/en.ts
CHANGED
@@ -381,8 +381,7 @@ The above is the content you need to summarize.`,
|
|
381 |
freedomTip: `Set the freedom level to 'Precise' to strictly confine the LLM's response to your selected knowledge base(s). Choose 'Improvise' to grant the LLM greater freedom in its responses, which may lead to hallucinations. 'Balance' is an intermediate level; choose 'Balance' for more balanced responses.`,
|
382 |
temperature: 'Temperature',
|
383 |
temperatureMessage: 'Temperature is required',
|
384 |
-
temperatureTip:
|
385 |
-
`This parameter controls the randomness of the model's predictions. A lower temperature results in more conservative responses, while a higher temperature yields more creative and diverse responses.`,
|
386 |
topP: 'Top P',
|
387 |
topPMessage: 'Top P is required',
|
388 |
topPTip:
|
@@ -397,8 +396,7 @@ The above is the content you need to summarize.`,
|
|
397 |
'Similar to the presence penalty, this reduces the model’s tendency to repeat the same words frequently.',
|
398 |
maxTokens: 'Max tokens',
|
399 |
maxTokensMessage: 'Max tokens is required',
|
400 |
-
maxTokensTip:
|
401 |
-
`This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). If disabled, you lift the maximum token limit, allowing the model to determine the number of tokens in its responses. Defaults to 512.`,
|
402 |
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
|
403 |
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
|
404 |
quote: 'Show quote',
|
@@ -430,7 +428,7 @@ The above is the content you need to summarize.`,
|
|
430 |
partialTitle: 'Partial Embed',
|
431 |
extensionTitle: 'Chrome Extension',
|
432 |
tokenError: 'Please create API Token first!',
|
433 |
-
betaError: '
|
434 |
searching: 'Searching...',
|
435 |
parsing: 'Parsing',
|
436 |
uploading: 'Uploading',
|
@@ -453,8 +451,7 @@ The above is the content you need to summarize.`,
|
|
453 |
profileDescription: 'Update your photo and personal details here.',
|
454 |
maxTokens: 'Max Tokens',
|
455 |
maxTokensMessage: 'Max Tokens is required',
|
456 |
-
maxTokensTip:
|
457 |
-
`This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). If disabled, you lift the maximum token limit, allowing the model to determine the number of tokens in its responses. Defaults to 512.`,
|
458 |
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
|
459 |
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
|
460 |
password: 'Password',
|
@@ -774,7 +771,7 @@ The above is the content you need to summarize.`,
|
|
774 |
sourceLang: 'Source language',
|
775 |
targetLang: 'Target language',
|
776 |
gitHub: 'GitHub',
|
777 |
-
|
778 |
'This component is used to search the repository from https://github.com/. Top N specifies the number of search results to be adjusted.',
|
779 |
baiduFanyi: 'BaiduFanyi',
|
780 |
baiduFanyiDescription:
|
|
|
381 |
freedomTip: `Set the freedom level to 'Precise' to strictly confine the LLM's response to your selected knowledge base(s). Choose 'Improvise' to grant the LLM greater freedom in its responses, which may lead to hallucinations. 'Balance' is an intermediate level; choose 'Balance' for more balanced responses.`,
|
382 |
temperature: 'Temperature',
|
383 |
temperatureMessage: 'Temperature is required',
|
384 |
+
temperatureTip: `This parameter controls the randomness of the model's predictions. A lower temperature results in more conservative responses, while a higher temperature yields more creative and diverse responses.`,
|
|
|
385 |
topP: 'Top P',
|
386 |
topPMessage: 'Top P is required',
|
387 |
topPTip:
|
|
|
396 |
'Similar to the presence penalty, this reduces the model’s tendency to repeat the same words frequently.',
|
397 |
maxTokens: 'Max tokens',
|
398 |
maxTokensMessage: 'Max tokens is required',
|
399 |
+
maxTokensTip: `This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). If disabled, you lift the maximum token limit, allowing the model to determine the number of tokens in its responses. Defaults to 512.`,
|
|
|
400 |
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
|
401 |
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
|
402 |
quote: 'Show quote',
|
|
|
428 |
partialTitle: 'Partial Embed',
|
429 |
extensionTitle: 'Chrome Extension',
|
430 |
tokenError: 'Please create API Token first!',
|
431 |
+
betaError: 'Please apply an API key in system setting firstly.',
|
432 |
searching: 'Searching...',
|
433 |
parsing: 'Parsing',
|
434 |
uploading: 'Uploading',
|
|
|
451 |
profileDescription: 'Update your photo and personal details here.',
|
452 |
maxTokens: 'Max Tokens',
|
453 |
maxTokensMessage: 'Max Tokens is required',
|
454 |
+
maxTokensTip: `This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). If disabled, you lift the maximum token limit, allowing the model to determine the number of tokens in its responses. Defaults to 512.`,
|
|
|
455 |
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
|
456 |
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
|
457 |
password: 'Password',
|
|
|
771 |
sourceLang: 'Source language',
|
772 |
targetLang: 'Target language',
|
773 |
gitHub: 'GitHub',
|
774 |
+
gitHubDescription:
|
775 |
'This component is used to search the repository from https://github.com/. Top N specifies the number of search results to be adjusted.',
|
776 |
baiduFanyi: 'BaiduFanyi',
|
777 |
baiduFanyiDescription:
|
web/src/locales/zh-traditional.ts
CHANGED
@@ -414,7 +414,7 @@ export default {
|
|
414 |
partialTitle: '部分嵌入',
|
415 |
extensionTitle: 'Chrome 插件',
|
416 |
tokenError: '請先創建 API Token!',
|
417 |
-
betaError: 'API
|
418 |
searching: '搜索中',
|
419 |
parsing: '解析中',
|
420 |
uploading: '上傳中',
|
|
|
414 |
partialTitle: '部分嵌入',
|
415 |
extensionTitle: 'Chrome 插件',
|
416 |
tokenError: '請先創建 API Token!',
|
417 |
+
betaError: '請先在系統設定中申請API密鑰。',
|
418 |
searching: '搜索中',
|
419 |
parsing: '解析中',
|
420 |
uploading: '上傳中',
|
web/src/locales/zh.ts
CHANGED
@@ -431,7 +431,7 @@ export default {
|
|
431 |
partialTitle: '部分嵌入',
|
432 |
extensionTitle: 'Chrome 插件',
|
433 |
tokenError: '请先创建 API Token!',
|
434 |
-
betaError: 'API
|
435 |
searching: '搜索中',
|
436 |
parsing: '解析中',
|
437 |
uploading: '上传中',
|
@@ -760,7 +760,7 @@ export default {
|
|
760 |
sourceLang: '源语言',
|
761 |
targetLang: '目标语言',
|
762 |
gitHub: 'GitHub',
|
763 |
-
|
764 |
'该组件用于从 https://github.com/ 搜索仓库。Top N 指定需要调整的搜索结果数量。',
|
765 |
baiduFanyi: '百度翻译',
|
766 |
baiduFanyiDescription:
|
|
|
431 |
partialTitle: '部分嵌入',
|
432 |
extensionTitle: 'Chrome 插件',
|
433 |
tokenError: '请先创建 API Token!',
|
434 |
+
betaError: '请先在系统设置中申请API密钥。',
|
435 |
searching: '搜索中',
|
436 |
parsing: '解析中',
|
437 |
uploading: '上传中',
|
|
|
760 |
sourceLang: '源语言',
|
761 |
targetLang: '目标语言',
|
762 |
gitHub: 'GitHub',
|
763 |
+
gitHubDescription:
|
764 |
'该组件用于从 https://github.com/ 搜索仓库。Top N 指定需要调整的搜索结果数量。',
|
765 |
baiduFanyi: '百度翻译',
|
766 |
baiduFanyiDescription:
|
web/src/pages/flow/canvas/index.tsx
CHANGED
@@ -5,6 +5,7 @@ import {
|
|
5 |
TooltipTrigger,
|
6 |
} from '@/components/ui/tooltip';
|
7 |
import { useSetModalState } from '@/hooks/common-hooks';
|
|
|
8 |
import { FolderInput, FolderOutput } from 'lucide-react';
|
9 |
import { useCallback, useEffect } from 'react';
|
10 |
import ReactFlow, {
|
@@ -24,6 +25,7 @@ import {
|
|
24 |
useHandleExportOrImportJsonFile,
|
25 |
useSelectCanvasData,
|
26 |
useShowFormDrawer,
|
|
|
27 |
useValidateConnection,
|
28 |
useWatchNodeFormDataChange,
|
29 |
} from '../hooks';
|
@@ -95,6 +97,11 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
95 |
showModal: showChatModal,
|
96 |
hideModal: hideChatModal,
|
97 |
} = useSetModalState();
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
const { formDrawerVisible, hideFormDrawer, showFormDrawer, clickedNode } =
|
100 |
useShowFormDrawer();
|
@@ -116,11 +123,24 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
116 |
const onNodeClick: NodeMouseHandler = useCallback(
|
117 |
(e, node) => {
|
118 |
if (node.data.label !== Operator.Note) {
|
|
|
119 |
hideRunOrChatDrawer();
|
120 |
showFormDrawer(node);
|
121 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
},
|
123 |
-
[
|
|
|
|
|
|
|
|
|
|
|
124 |
);
|
125 |
|
126 |
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
@@ -193,12 +213,6 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
193 |
onSelectionChange={onSelectionChange}
|
194 |
nodeOrigin={[0.5, 0]}
|
195 |
isValidConnection={isValidConnection}
|
196 |
-
onChangeCapture={(...params) => {
|
197 |
-
console.info('onChangeCapture:', ...params);
|
198 |
-
}}
|
199 |
-
onChange={(...params) => {
|
200 |
-
console.info('params:', ...params);
|
201 |
-
}}
|
202 |
defaultEdgeOptions={{
|
203 |
type: 'buttonEdge',
|
204 |
markerEnd: 'logo',
|
@@ -214,7 +228,7 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
214 |
<ControlButton onClick={handleImportJson}>
|
215 |
<TooltipProvider>
|
216 |
<Tooltip>
|
217 |
-
<TooltipTrigger>
|
218 |
<FolderInput />
|
219 |
</TooltipTrigger>
|
220 |
<TooltipContent>Import</TooltipContent>
|
@@ -224,7 +238,7 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
224 |
<ControlButton onClick={handleExportJson}>
|
225 |
<TooltipProvider>
|
226 |
<Tooltip>
|
227 |
-
<TooltipTrigger>
|
228 |
<FolderOutput />
|
229 |
</TooltipTrigger>
|
230 |
<TooltipContent>Export</TooltipContent>
|
@@ -238,6 +252,9 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
238 |
node={clickedNode}
|
239 |
visible={formDrawerVisible}
|
240 |
hideModal={hideFormDrawer}
|
|
|
|
|
|
|
241 |
></FormDrawer>
|
242 |
)}
|
243 |
{chatVisible && (
|
|
|
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, {
|
|
|
25 |
useHandleExportOrImportJsonFile,
|
26 |
useSelectCanvasData,
|
27 |
useShowFormDrawer,
|
28 |
+
useShowSingleDebugDrawer,
|
29 |
useValidateConnection,
|
30 |
useWatchNodeFormDataChange,
|
31 |
} from '../hooks';
|
|
|
97 |
showModal: showChatModal,
|
98 |
hideModal: hideChatModal,
|
99 |
} = useSetModalState();
|
100 |
+
const {
|
101 |
+
singleDebugDrawerVisible,
|
102 |
+
showSingleDebugDrawer,
|
103 |
+
hideSingleDebugDrawer,
|
104 |
+
} = useShowSingleDebugDrawer();
|
105 |
|
106 |
const { formDrawerVisible, hideFormDrawer, showFormDrawer, clickedNode } =
|
107 |
useShowFormDrawer();
|
|
|
123 |
const onNodeClick: NodeMouseHandler = useCallback(
|
124 |
(e, node) => {
|
125 |
if (node.data.label !== Operator.Note) {
|
126 |
+
hideSingleDebugDrawer();
|
127 |
hideRunOrChatDrawer();
|
128 |
showFormDrawer(node);
|
129 |
}
|
130 |
+
// handle single debug icon click
|
131 |
+
if (
|
132 |
+
get(e.target, 'dataset.play') === 'true' ||
|
133 |
+
get(e.target, 'parentNode.dataset.play') === 'true'
|
134 |
+
) {
|
135 |
+
showSingleDebugDrawer();
|
136 |
+
}
|
137 |
},
|
138 |
+
[
|
139 |
+
hideRunOrChatDrawer,
|
140 |
+
hideSingleDebugDrawer,
|
141 |
+
showFormDrawer,
|
142 |
+
showSingleDebugDrawer,
|
143 |
+
],
|
144 |
);
|
145 |
|
146 |
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
|
|
213 |
onSelectionChange={onSelectionChange}
|
214 |
nodeOrigin={[0.5, 0]}
|
215 |
isValidConnection={isValidConnection}
|
|
|
|
|
|
|
|
|
|
|
|
|
216 |
defaultEdgeOptions={{
|
217 |
type: 'buttonEdge',
|
218 |
markerEnd: 'logo',
|
|
|
228 |
<ControlButton onClick={handleImportJson}>
|
229 |
<TooltipProvider>
|
230 |
<Tooltip>
|
231 |
+
<TooltipTrigger asChild>
|
232 |
<FolderInput />
|
233 |
</TooltipTrigger>
|
234 |
<TooltipContent>Import</TooltipContent>
|
|
|
238 |
<ControlButton onClick={handleExportJson}>
|
239 |
<TooltipProvider>
|
240 |
<Tooltip>
|
241 |
+
<TooltipTrigger asChild>
|
242 |
<FolderOutput />
|
243 |
</TooltipTrigger>
|
244 |
<TooltipContent>Export</TooltipContent>
|
|
|
252 |
node={clickedNode}
|
253 |
visible={formDrawerVisible}
|
254 |
hideModal={hideFormDrawer}
|
255 |
+
singleDebugDrawerVisible={singleDebugDrawerVisible}
|
256 |
+
hideSingleDebugDrawer={hideSingleDebugDrawer}
|
257 |
+
showSingleDebugDrawer={showSingleDebugDrawer}
|
258 |
></FormDrawer>
|
259 |
)}
|
260 |
{chatVisible && (
|
web/src/pages/flow/canvas/node/node-header.tsx
CHANGED
@@ -1,12 +1,14 @@
|
|
|
|
1 |
import { Flex } from 'antd';
|
|
|
2 |
import { Operator, operatorMap } from '../../constant';
|
3 |
import OperatorIcon from '../../operator-icon';
|
|
|
4 |
import NodeDropdown from './dropdown';
|
5 |
-
|
6 |
-
import { useTranslate } from '@/hooks/common-hooks';
|
7 |
-
import styles from './index.less';
|
8 |
import { NextNodePopover } from './popover';
|
9 |
|
|
|
|
|
10 |
interface IProps {
|
11 |
id: string;
|
12 |
label: string;
|
@@ -15,12 +17,17 @@ interface IProps {
|
|
15 |
className?: string;
|
16 |
}
|
17 |
|
18 |
-
export function RunStatus({ id, name }:
|
19 |
const { t } = useTranslate('flow');
|
20 |
return (
|
21 |
-
<section className="flex
|
|
|
|
|
|
|
|
|
|
|
22 |
<NextNodePopover nodeId={id} name={name}>
|
23 |
-
<span className="
|
24 |
{t('operationResults')}
|
25 |
</span>
|
26 |
</NextNodePopover>
|
@@ -30,8 +37,10 @@ export function RunStatus({ id, name }: Omit<IProps, 'label'>) {
|
|
30 |
|
31 |
const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => {
|
32 |
return (
|
33 |
-
<section
|
34 |
-
{label !== Operator.Answer &&
|
|
|
|
|
35 |
<Flex
|
36 |
flex={1}
|
37 |
align="center"
|
|
|
1 |
+
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { Flex } from 'antd';
|
3 |
+
import { Play } from 'lucide-react';
|
4 |
import { Operator, operatorMap } from '../../constant';
|
5 |
import OperatorIcon from '../../operator-icon';
|
6 |
+
import { needsSingleStepDebugging } from '../../utils';
|
7 |
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;
|
|
|
17 |
className?: string;
|
18 |
}
|
19 |
|
20 |
+
export function RunStatus({ id, name, label }: IProps) {
|
21 |
const { t } = useTranslate('flow');
|
22 |
return (
|
23 |
+
<section className="flex justify-end items-center pb-1 gap-2 text-blue-600">
|
24 |
+
{needsSingleStepDebugging(label) && (
|
25 |
+
<RunTooltip>
|
26 |
+
<Play className="size-3 cursor-pointer" data-play />
|
27 |
+
</RunTooltip> // data-play is used to trigger single step debugging
|
28 |
+
)}
|
29 |
<NextNodePopover nodeId={id} name={name}>
|
30 |
+
<span className="cursor-pointer text-[10px]">
|
31 |
{t('operationResults')}
|
32 |
</span>
|
33 |
</NextNodePopover>
|
|
|
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
|
45 |
flex={1}
|
46 |
align="center"
|
web/src/pages/flow/constant.tsx
CHANGED
@@ -2931,3 +2931,13 @@ export const BeginQueryTypeIconMap = {
|
|
2931 |
[BeginQueryType.Integer]: ListOrdered,
|
2932 |
[BeginQueryType.Boolean]: ToggleLeft,
|
2933 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2931 |
[BeginQueryType.Integer]: ListOrdered,
|
2932 |
[BeginQueryType.Boolean]: ToggleLeft,
|
2933 |
};
|
2934 |
+
|
2935 |
+
export const NoDebugOperatorsList = [
|
2936 |
+
Operator.Begin,
|
2937 |
+
Operator.Answer,
|
2938 |
+
Operator.Concentrator,
|
2939 |
+
Operator.Template,
|
2940 |
+
Operator.Message,
|
2941 |
+
Operator.RewriteQuestion,
|
2942 |
+
Operator.Switch,
|
2943 |
+
];
|
web/src/pages/flow/debug-content/index.less
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.formWrapper {
|
2 |
+
:global(.ant-form-item-label) {
|
3 |
+
font-weight: 600 !important;
|
4 |
+
}
|
5 |
+
}
|
web/src/pages/flow/debug-content/index.tsx
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Authorization } from '@/constants/authorization';
|
2 |
+
import { useSetModalState } from '@/hooks/common-hooks';
|
3 |
+
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
4 |
+
import { useHandleSubmittable } from '@/hooks/login-hooks';
|
5 |
+
import api from '@/utils/api';
|
6 |
+
import { getAuthorization } from '@/utils/authorization-util';
|
7 |
+
import { UploadOutlined } from '@ant-design/icons';
|
8 |
+
import {
|
9 |
+
Button,
|
10 |
+
Form,
|
11 |
+
FormItemProps,
|
12 |
+
Input,
|
13 |
+
InputNumber,
|
14 |
+
Select,
|
15 |
+
Switch,
|
16 |
+
Upload,
|
17 |
+
} from 'antd';
|
18 |
+
import { UploadChangeParam, UploadFile } from 'antd/es/upload';
|
19 |
+
import { pick } from 'lodash';
|
20 |
+
import { Link } from 'lucide-react';
|
21 |
+
import React, { useCallback, useState } from 'react';
|
22 |
+
import { useTranslation } from 'react-i18next';
|
23 |
+
import { BeginQueryType } from '../constant';
|
24 |
+
import { BeginQuery } from '../interface';
|
25 |
+
import { PopoverForm } from './popover-form';
|
26 |
+
|
27 |
+
import styles from './index.less';
|
28 |
+
|
29 |
+
interface IProps {
|
30 |
+
parameters: BeginQuery[];
|
31 |
+
ok(parameters: any[]): void;
|
32 |
+
isNext?: boolean;
|
33 |
+
loading?: boolean;
|
34 |
+
submitButtonDisabled?: boolean;
|
35 |
+
}
|
36 |
+
|
37 |
+
const DebugContent = ({
|
38 |
+
parameters,
|
39 |
+
ok,
|
40 |
+
isNext = true,
|
41 |
+
loading = false,
|
42 |
+
submitButtonDisabled = false,
|
43 |
+
}: IProps) => {
|
44 |
+
const { t } = useTranslation();
|
45 |
+
const [form] = Form.useForm();
|
46 |
+
const {
|
47 |
+
visible,
|
48 |
+
hideModal: hidePopover,
|
49 |
+
switchVisible,
|
50 |
+
showModal: showPopover,
|
51 |
+
} = useSetModalState();
|
52 |
+
const { setRecord, currentRecord } = useSetSelectedRecord<number>();
|
53 |
+
const { submittable } = useHandleSubmittable(form);
|
54 |
+
const [isUploading, setIsUploading] = useState(false);
|
55 |
+
|
56 |
+
const handleShowPopover = useCallback(
|
57 |
+
(idx: number) => () => {
|
58 |
+
setRecord(idx);
|
59 |
+
showPopover();
|
60 |
+
},
|
61 |
+
[setRecord, showPopover],
|
62 |
+
);
|
63 |
+
|
64 |
+
const normFile = (e: any) => {
|
65 |
+
if (Array.isArray(e)) {
|
66 |
+
return e;
|
67 |
+
}
|
68 |
+
return e?.fileList;
|
69 |
+
};
|
70 |
+
|
71 |
+
const onChange = useCallback(
|
72 |
+
(optional: boolean) =>
|
73 |
+
({ fileList }: UploadChangeParam<UploadFile>) => {
|
74 |
+
if (!optional) {
|
75 |
+
setIsUploading(fileList.some((x) => x.status === 'uploading'));
|
76 |
+
}
|
77 |
+
},
|
78 |
+
[],
|
79 |
+
);
|
80 |
+
|
81 |
+
const renderWidget = useCallback(
|
82 |
+
(q: BeginQuery, idx: number) => {
|
83 |
+
const props: FormItemProps & { key: number } = {
|
84 |
+
key: idx,
|
85 |
+
label: q.name ?? q.key,
|
86 |
+
name: idx,
|
87 |
+
};
|
88 |
+
if (q.optional === false) {
|
89 |
+
props.rules = [{ required: true }];
|
90 |
+
}
|
91 |
+
|
92 |
+
const urlList: { url: string; result: string }[] =
|
93 |
+
form.getFieldValue(idx) || [];
|
94 |
+
|
95 |
+
const BeginQueryTypeMap = {
|
96 |
+
[BeginQueryType.Line]: (
|
97 |
+
<Form.Item {...props}>
|
98 |
+
<Input></Input>
|
99 |
+
</Form.Item>
|
100 |
+
),
|
101 |
+
[BeginQueryType.Paragraph]: (
|
102 |
+
<Form.Item {...props}>
|
103 |
+
<Input.TextArea rows={1}></Input.TextArea>
|
104 |
+
</Form.Item>
|
105 |
+
),
|
106 |
+
[BeginQueryType.Options]: (
|
107 |
+
<Form.Item {...props}>
|
108 |
+
<Select
|
109 |
+
allowClear
|
110 |
+
options={q.options?.map((x) => ({ label: x, value: x })) ?? []}
|
111 |
+
></Select>
|
112 |
+
</Form.Item>
|
113 |
+
),
|
114 |
+
[BeginQueryType.File]: (
|
115 |
+
<React.Fragment key={idx}>
|
116 |
+
<Form.Item label={q.name ?? q.key} required={!q.optional}>
|
117 |
+
<div className="relative">
|
118 |
+
<Form.Item
|
119 |
+
{...props}
|
120 |
+
valuePropName="fileList"
|
121 |
+
getValueFromEvent={normFile}
|
122 |
+
noStyle
|
123 |
+
>
|
124 |
+
<Upload
|
125 |
+
name="file"
|
126 |
+
action={api.parse}
|
127 |
+
multiple
|
128 |
+
headers={{ [Authorization]: getAuthorization() }}
|
129 |
+
onChange={onChange(q.optional)}
|
130 |
+
>
|
131 |
+
<Button icon={<UploadOutlined />}>
|
132 |
+
{t('common.upload')}
|
133 |
+
</Button>
|
134 |
+
</Upload>
|
135 |
+
</Form.Item>
|
136 |
+
<Form.Item
|
137 |
+
{...pick(props, ['key', 'label', 'rules'])}
|
138 |
+
required={!q.optional}
|
139 |
+
className={urlList.length > 0 ? 'mb-1' : ''}
|
140 |
+
noStyle
|
141 |
+
>
|
142 |
+
<PopoverForm visible={visible} switchVisible={switchVisible}>
|
143 |
+
<Button
|
144 |
+
onClick={handleShowPopover(idx)}
|
145 |
+
className="absolute left-1/2 top-0"
|
146 |
+
icon={<Link className="size-3" />}
|
147 |
+
>
|
148 |
+
{t('flow.pasteFileLink')}
|
149 |
+
</Button>
|
150 |
+
</PopoverForm>
|
151 |
+
</Form.Item>
|
152 |
+
</div>
|
153 |
+
</Form.Item>
|
154 |
+
<Form.Item name={idx} noStyle {...pick(props, ['rules'])} />
|
155 |
+
</React.Fragment>
|
156 |
+
),
|
157 |
+
[BeginQueryType.Integer]: (
|
158 |
+
<Form.Item {...props}>
|
159 |
+
<InputNumber></InputNumber>
|
160 |
+
</Form.Item>
|
161 |
+
),
|
162 |
+
[BeginQueryType.Boolean]: (
|
163 |
+
<Form.Item valuePropName={'checked'} {...props}>
|
164 |
+
<Switch></Switch>
|
165 |
+
</Form.Item>
|
166 |
+
),
|
167 |
+
};
|
168 |
+
|
169 |
+
return (
|
170 |
+
BeginQueryTypeMap[q.type as BeginQueryType] ??
|
171 |
+
BeginQueryTypeMap[BeginQueryType.Paragraph]
|
172 |
+
);
|
173 |
+
},
|
174 |
+
[form, handleShowPopover, onChange, switchVisible, t, visible],
|
175 |
+
);
|
176 |
+
|
177 |
+
const onOk = useCallback(async () => {
|
178 |
+
const values = await form.validateFields();
|
179 |
+
const nextValues = Object.entries(values).map(([key, value]) => {
|
180 |
+
const item = parameters[Number(key)];
|
181 |
+
let nextValue = value;
|
182 |
+
if (Array.isArray(value)) {
|
183 |
+
nextValue = ``;
|
184 |
+
|
185 |
+
value.forEach((x) => {
|
186 |
+
nextValue +=
|
187 |
+
x?.originFileObj instanceof File
|
188 |
+
? `${x.name}\n${x.response?.data}\n----\n`
|
189 |
+
: `${x.url}\n${x.result}\n----\n`;
|
190 |
+
});
|
191 |
+
}
|
192 |
+
return { ...item, value: nextValue };
|
193 |
+
});
|
194 |
+
|
195 |
+
ok(nextValues);
|
196 |
+
}, [form, ok, parameters]);
|
197 |
+
|
198 |
+
return (
|
199 |
+
<>
|
200 |
+
<section className={styles.formWrapper}>
|
201 |
+
<Form.Provider
|
202 |
+
onFormFinish={(name, { values, forms }) => {
|
203 |
+
if (name === 'urlForm') {
|
204 |
+
const { basicForm } = forms;
|
205 |
+
const urlInfo = basicForm.getFieldValue(currentRecord) || [];
|
206 |
+
basicForm.setFieldsValue({
|
207 |
+
[currentRecord]: [...urlInfo, { ...values, name: values.url }],
|
208 |
+
});
|
209 |
+
hidePopover();
|
210 |
+
}
|
211 |
+
}}
|
212 |
+
>
|
213 |
+
<Form
|
214 |
+
name="basicForm"
|
215 |
+
autoComplete="off"
|
216 |
+
layout={'vertical'}
|
217 |
+
form={form}
|
218 |
+
>
|
219 |
+
{parameters.map((x, idx) => {
|
220 |
+
return renderWidget(x, idx);
|
221 |
+
})}
|
222 |
+
</Form>
|
223 |
+
</Form.Provider>
|
224 |
+
</section>
|
225 |
+
<Button
|
226 |
+
type={'primary'}
|
227 |
+
block
|
228 |
+
onClick={onOk}
|
229 |
+
loading={loading}
|
230 |
+
disabled={!submittable || isUploading || submitButtonDisabled}
|
231 |
+
>
|
232 |
+
{t(isNext ? 'common.next' : 'flow.run')}
|
233 |
+
</Button>
|
234 |
+
</>
|
235 |
+
);
|
236 |
+
};
|
237 |
+
|
238 |
+
export default DebugContent;
|
web/src/pages/flow/debug-content/popover-form.tsx
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useParseDocument } from '@/hooks/document-hooks';
|
2 |
+
import { useResetFormOnCloseModal } from '@/hooks/logic-hooks';
|
3 |
+
import { IModalProps } from '@/interfaces/common';
|
4 |
+
import { Button, Form, Input, Popover } from 'antd';
|
5 |
+
import { PropsWithChildren } from 'react';
|
6 |
+
import { useTranslation } from 'react-i18next';
|
7 |
+
|
8 |
+
const reg =
|
9 |
+
/^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/;
|
10 |
+
|
11 |
+
export const PopoverForm = ({
|
12 |
+
children,
|
13 |
+
visible,
|
14 |
+
switchVisible,
|
15 |
+
}: PropsWithChildren<IModalProps<any>>) => {
|
16 |
+
const [form] = Form.useForm();
|
17 |
+
const { parseDocument, loading } = useParseDocument();
|
18 |
+
const { t } = useTranslation();
|
19 |
+
|
20 |
+
useResetFormOnCloseModal({
|
21 |
+
form,
|
22 |
+
visible,
|
23 |
+
});
|
24 |
+
|
25 |
+
const onOk = async () => {
|
26 |
+
const values = await form.validateFields();
|
27 |
+
const val = values.url;
|
28 |
+
|
29 |
+
if (reg.test(val)) {
|
30 |
+
const ret = await parseDocument(val);
|
31 |
+
if (ret?.data?.code === 0) {
|
32 |
+
form.setFieldValue('result', ret?.data?.data);
|
33 |
+
form.submit();
|
34 |
+
}
|
35 |
+
}
|
36 |
+
};
|
37 |
+
|
38 |
+
const content = (
|
39 |
+
<Form form={form} name="urlForm">
|
40 |
+
<Form.Item
|
41 |
+
name="url"
|
42 |
+
rules={[{ required: true, type: 'url' }]}
|
43 |
+
className="m-0"
|
44 |
+
>
|
45 |
+
<Input
|
46 |
+
onPressEnter={(e) => e.preventDefault()}
|
47 |
+
placeholder={t('flow.pasteFileLink')}
|
48 |
+
suffix={
|
49 |
+
<Button
|
50 |
+
type="primary"
|
51 |
+
onClick={onOk}
|
52 |
+
size={'small'}
|
53 |
+
loading={loading}
|
54 |
+
>
|
55 |
+
{t('common.submit')}
|
56 |
+
</Button>
|
57 |
+
}
|
58 |
+
/>
|
59 |
+
</Form.Item>
|
60 |
+
<Form.Item name={'result'} noStyle />
|
61 |
+
</Form>
|
62 |
+
);
|
63 |
+
|
64 |
+
return (
|
65 |
+
<Popover
|
66 |
+
content={content}
|
67 |
+
open={visible}
|
68 |
+
trigger={'click'}
|
69 |
+
onOpenChange={switchVisible}
|
70 |
+
>
|
71 |
+
{children}
|
72 |
+
</Popover>
|
73 |
+
);
|
74 |
+
};
|
web/src/pages/flow/flow-drawer/index.less
CHANGED
@@ -13,3 +13,9 @@
|
|
13 |
padding-top: 16px;
|
14 |
font-weight: normal;
|
15 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
padding-top: 16px;
|
14 |
font-weight: normal;
|
15 |
}
|
16 |
+
|
17 |
+
.formDrawer {
|
18 |
+
:global(.ant-drawer-content-wrapper) {
|
19 |
+
transform: translateX(0) !important;
|
20 |
+
}
|
21 |
+
}
|
web/src/pages/flow/flow-drawer/index.tsx
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { IModalProps } from '@/interfaces/common';
|
|
|
3 |
import { Drawer, Flex, Form, Input } from 'antd';
|
|
|
|
|
4 |
import { useEffect } from 'react';
|
5 |
import { Node } from 'reactflow';
|
6 |
import { Operator, operatorMap } from '../constant';
|
@@ -15,6 +18,7 @@ import CategorizeForm from '../form/categorize-form';
|
|
15 |
import CrawlerForm from '../form/crawler-form';
|
16 |
import DeepLForm from '../form/deepl-form';
|
17 |
import DuckDuckGoForm from '../form/duckduckgo-form';
|
|
|
18 |
import ExeSQLForm from '../form/exesql-form';
|
19 |
import GenerateForm from '../form/generate-form';
|
20 |
import GithubForm from '../form/github-form';
|
@@ -30,22 +34,24 @@ import RelevantForm from '../form/relevant-form';
|
|
30 |
import RetrievalForm from '../form/retrieval-form';
|
31 |
import RewriteQuestionForm from '../form/rewrite-question-form';
|
32 |
import SwitchForm from '../form/switch-form';
|
|
|
33 |
import TuShareForm from '../form/tushare-form';
|
34 |
import WenCaiForm from '../form/wencai-form';
|
35 |
import WikipediaForm from '../form/wikipedia-form';
|
36 |
import YahooFinanceForm from '../form/yahoo-finance-form';
|
37 |
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
38 |
import OperatorIcon from '../operator-icon';
|
|
|
|
|
39 |
|
40 |
-
import {
|
41 |
-
import { lowerFirst } from 'lodash';
|
42 |
-
import EmailForm from '../form/email-form';
|
43 |
-
import TemplateForm from '../form/template-form';
|
44 |
-
import { getDrawerWidth } from '../utils';
|
45 |
import styles from './index.less';
|
46 |
|
47 |
interface IProps {
|
48 |
node?: Node;
|
|
|
|
|
|
|
49 |
}
|
50 |
|
51 |
const FormMap = {
|
@@ -91,6 +97,9 @@ const FormDrawer = ({
|
|
91 |
visible,
|
92 |
hideModal,
|
93 |
node,
|
|
|
|
|
|
|
94 |
}: IModalProps<any> & IProps) => {
|
95 |
const operatorName: Operator = node?.data.label;
|
96 |
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
|
@@ -99,12 +108,14 @@ const FormDrawer = ({
|
|
99 |
id: node?.id,
|
100 |
data: node?.data,
|
101 |
});
|
|
|
102 |
const { t } = useTranslate('flow');
|
103 |
|
104 |
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
|
105 |
|
106 |
useEffect(() => {
|
107 |
if (visible) {
|
|
|
108 |
form.setFieldsValue(node?.data?.form);
|
109 |
}
|
110 |
}, [visible, form, node?.data?.form]);
|
@@ -128,6 +139,14 @@ const FormDrawer = ({
|
|
128 |
onChange={handleNameChange}
|
129 |
></Input>
|
130 |
</Flex>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
<CloseOutlined onClick={hideModal} />
|
132 |
</Flex>
|
133 |
<span className={styles.operatorDescription}>
|
@@ -142,6 +161,7 @@ const FormDrawer = ({
|
|
142 |
mask={false}
|
143 |
width={getDrawerWidth()}
|
144 |
closeIcon={null}
|
|
|
145 |
>
|
146 |
<section className={styles.formWrapper}>
|
147 |
{visible && (
|
@@ -152,6 +172,13 @@ const FormDrawer = ({
|
|
152 |
></OperatorForm>
|
153 |
)}
|
154 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
</Drawer>
|
156 |
);
|
157 |
};
|
|
|
1 |
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { IModalProps } from '@/interfaces/common';
|
3 |
+
import { CloseOutlined } from '@ant-design/icons';
|
4 |
import { Drawer, Flex, Form, Input } from 'antd';
|
5 |
+
import { lowerFirst } from 'lodash';
|
6 |
+
import { Play } from 'lucide-react';
|
7 |
import { useEffect } from 'react';
|
8 |
import { Node } from 'reactflow';
|
9 |
import { Operator, operatorMap } from '../constant';
|
|
|
18 |
import CrawlerForm from '../form/crawler-form';
|
19 |
import DeepLForm from '../form/deepl-form';
|
20 |
import DuckDuckGoForm from '../form/duckduckgo-form';
|
21 |
+
import EmailForm from '../form/email-form';
|
22 |
import ExeSQLForm from '../form/exesql-form';
|
23 |
import GenerateForm from '../form/generate-form';
|
24 |
import GithubForm from '../form/github-form';
|
|
|
34 |
import RetrievalForm from '../form/retrieval-form';
|
35 |
import RewriteQuestionForm from '../form/rewrite-question-form';
|
36 |
import SwitchForm from '../form/switch-form';
|
37 |
+
import TemplateForm from '../form/template-form';
|
38 |
import TuShareForm from '../form/tushare-form';
|
39 |
import WenCaiForm from '../form/wencai-form';
|
40 |
import WikipediaForm from '../form/wikipedia-form';
|
41 |
import YahooFinanceForm from '../form/yahoo-finance-form';
|
42 |
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
43 |
import OperatorIcon from '../operator-icon';
|
44 |
+
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 {
|
51 |
node?: Node;
|
52 |
+
singleDebugDrawerVisible: IModalProps<any>['visible'];
|
53 |
+
hideSingleDebugDrawer: IModalProps<any>['hideModal'];
|
54 |
+
showSingleDebugDrawer: IModalProps<any>['showModal'];
|
55 |
}
|
56 |
|
57 |
const FormMap = {
|
|
|
97 |
visible,
|
98 |
hideModal,
|
99 |
node,
|
100 |
+
singleDebugDrawerVisible,
|
101 |
+
hideSingleDebugDrawer,
|
102 |
+
showSingleDebugDrawer,
|
103 |
}: IModalProps<any> & IProps) => {
|
104 |
const operatorName: Operator = node?.data.label;
|
105 |
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
|
|
|
108 |
id: node?.id,
|
109 |
data: node?.data,
|
110 |
});
|
111 |
+
|
112 |
const { t } = useTranslate('flow');
|
113 |
|
114 |
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
|
115 |
|
116 |
useEffect(() => {
|
117 |
if (visible) {
|
118 |
+
form.resetFields();
|
119 |
form.setFieldsValue(node?.data?.form);
|
120 |
}
|
121 |
}, [visible, form, node?.data?.form]);
|
|
|
139 |
onChange={handleNameChange}
|
140 |
></Input>
|
141 |
</Flex>
|
142 |
+
{needsSingleStepDebugging(operatorName) && (
|
143 |
+
<RunTooltip>
|
144 |
+
<Play
|
145 |
+
className="size-5 cursor-pointer"
|
146 |
+
onClick={showSingleDebugDrawer}
|
147 |
+
/>
|
148 |
+
</RunTooltip>
|
149 |
+
)}
|
150 |
<CloseOutlined onClick={hideModal} />
|
151 |
</Flex>
|
152 |
<span className={styles.operatorDescription}>
|
|
|
161 |
mask={false}
|
162 |
width={getDrawerWidth()}
|
163 |
closeIcon={null}
|
164 |
+
rootClassName={styles.formDrawer}
|
165 |
>
|
166 |
<section className={styles.formWrapper}>
|
167 |
{visible && (
|
|
|
172 |
></OperatorForm>
|
173 |
)}
|
174 |
</section>
|
175 |
+
{singleDebugDrawerVisible && (
|
176 |
+
<SingleDebugDrawer
|
177 |
+
visible={singleDebugDrawerVisible}
|
178 |
+
hideModal={hideSingleDebugDrawer}
|
179 |
+
componentId={node?.id}
|
180 |
+
></SingleDebugDrawer>
|
181 |
+
)}
|
182 |
</Drawer>
|
183 |
);
|
184 |
};
|
web/src/pages/flow/flow-drawer/single-debug-drawer/index.tsx
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import CopyToClipboard from '@/components/copy-to-clipboard';
|
2 |
+
import { useDebugSingle, useFetchInputElements } from '@/hooks/flow-hooks';
|
3 |
+
import { IModalProps } from '@/interfaces/common';
|
4 |
+
import { CloseOutlined } from '@ant-design/icons';
|
5 |
+
import { Drawer } from 'antd';
|
6 |
+
import { isEmpty } from 'lodash';
|
7 |
+
import { useCallback } from 'react';
|
8 |
+
import { useTranslation } from 'react-i18next';
|
9 |
+
import JsonView from 'react18-json-view';
|
10 |
+
import 'react18-json-view/src/style.css';
|
11 |
+
import DebugContent from '../../debug-content';
|
12 |
+
|
13 |
+
interface IProps {
|
14 |
+
componentId?: string;
|
15 |
+
}
|
16 |
+
|
17 |
+
const SingleDebugDrawer = ({
|
18 |
+
componentId,
|
19 |
+
visible,
|
20 |
+
hideModal,
|
21 |
+
}: IModalProps<any> & IProps) => {
|
22 |
+
const { t } = useTranslation();
|
23 |
+
const { data: list } = useFetchInputElements(componentId);
|
24 |
+
const { debugSingle, data, loading } = useDebugSingle();
|
25 |
+
|
26 |
+
const onOk = useCallback(
|
27 |
+
(nextValues: any[]) => {
|
28 |
+
if (componentId) {
|
29 |
+
debugSingle({ component_id: componentId, params: nextValues });
|
30 |
+
}
|
31 |
+
},
|
32 |
+
[componentId, debugSingle],
|
33 |
+
);
|
34 |
+
|
35 |
+
const content = JSON.stringify(data, null, 2);
|
36 |
+
|
37 |
+
return (
|
38 |
+
<Drawer
|
39 |
+
title={
|
40 |
+
<div className="flex justify-between">
|
41 |
+
{t('flow.testRun')}
|
42 |
+
<CloseOutlined onClick={hideModal} />
|
43 |
+
</div>
|
44 |
+
}
|
45 |
+
width={'100%'}
|
46 |
+
onClose={hideModal}
|
47 |
+
open={visible}
|
48 |
+
getContainer={false}
|
49 |
+
mask={false}
|
50 |
+
placement={'bottom'}
|
51 |
+
height={'95%'}
|
52 |
+
closeIcon={null}
|
53 |
+
>
|
54 |
+
<section className="overflow-y-auto">
|
55 |
+
<DebugContent
|
56 |
+
parameters={list}
|
57 |
+
ok={onOk}
|
58 |
+
isNext={false}
|
59 |
+
loading={loading}
|
60 |
+
submitButtonDisabled={list.length === 0}
|
61 |
+
></DebugContent>
|
62 |
+
{!isEmpty(data) ? (
|
63 |
+
<div className="mt-4 rounded-md bg-slate-200 border border-neutral-200">
|
64 |
+
<div className="flex justify-between p-2">
|
65 |
+
<span>JSON</span>
|
66 |
+
<CopyToClipboard text={content}></CopyToClipboard>
|
67 |
+
</div>
|
68 |
+
<JsonView
|
69 |
+
src={data}
|
70 |
+
displaySize
|
71 |
+
collapseStringsAfterLength={100000000000}
|
72 |
+
className="w-full h-[800px] break-words overflow-auto p-2 bg-slate-100"
|
73 |
+
/>
|
74 |
+
</div>
|
75 |
+
) : null}
|
76 |
+
</section>
|
77 |
+
</Drawer>
|
78 |
+
);
|
79 |
+
};
|
80 |
+
|
81 |
+
export default SingleDebugDrawer;
|
web/src/pages/flow/flow-tooltip.tsx
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Tooltip,
|
3 |
+
TooltipContent,
|
4 |
+
TooltipProvider,
|
5 |
+
TooltipTrigger,
|
6 |
+
} from '@/components/ui/tooltip';
|
7 |
+
import { PropsWithChildren } from 'react';
|
8 |
+
import { useTranslation } from 'react-i18next';
|
9 |
+
|
10 |
+
export const RunTooltip = ({ children }: PropsWithChildren) => {
|
11 |
+
const { t } = useTranslation();
|
12 |
+
return (
|
13 |
+
<TooltipProvider>
|
14 |
+
<Tooltip>
|
15 |
+
<TooltipTrigger>{children}</TooltipTrigger>
|
16 |
+
<TooltipContent>
|
17 |
+
<p>{t('flow.testRun')}</p>
|
18 |
+
</TooltipContent>
|
19 |
+
</Tooltip>
|
20 |
+
</TooltipProvider>
|
21 |
+
);
|
22 |
+
};
|
web/src/pages/flow/form/invoke-form/index.tsx
CHANGED
@@ -1,9 +1,11 @@
|
|
1 |
-
import Editor from '@monaco-editor/react';
|
2 |
import { Form, Input, InputNumber, Select, Space, Switch } from 'antd';
|
3 |
import { useTranslation } from 'react-i18next';
|
4 |
import { IOperatorForm } from '../../interface';
|
5 |
import DynamicVariablesForm from './dynamic-variables';
|
6 |
|
|
|
|
|
7 |
enum Method {
|
8 |
GET = 'GET',
|
9 |
POST = 'POST',
|
|
|
1 |
+
import Editor, { loader } from '@monaco-editor/react';
|
2 |
import { Form, Input, InputNumber, Select, Space, Switch } from 'antd';
|
3 |
import { useTranslation } from 'react-i18next';
|
4 |
import { IOperatorForm } from '../../interface';
|
5 |
import DynamicVariablesForm from './dynamic-variables';
|
6 |
|
7 |
+
loader.config({ paths: { vs: '/vs' } });
|
8 |
+
|
9 |
enum Method {
|
10 |
GET = 'GET',
|
11 |
POST = 'POST',
|
web/src/pages/flow/hooks.tsx
CHANGED
@@ -620,7 +620,6 @@ export const useWatchNodeFormDataChange = () => {
|
|
620 |
);
|
621 |
|
622 |
useEffect(() => {
|
623 |
-
console.info('xxx');
|
624 |
nodes.forEach((node) => {
|
625 |
const currentNode = getNode(node.id);
|
626 |
const form = currentNode?.data.form ?? {};
|
@@ -856,3 +855,21 @@ export const useHandleExportOrImportJsonFile = () => {
|
|
856 |
onFileUploadOk,
|
857 |
};
|
858 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
620 |
);
|
621 |
|
622 |
useEffect(() => {
|
|
|
623 |
nodes.forEach((node) => {
|
624 |
const currentNode = getNode(node.id);
|
625 |
const form = currentNode?.data.form ?? {};
|
|
|
855 |
onFileUploadOk,
|
856 |
};
|
857 |
};
|
858 |
+
|
859 |
+
export const useShowSingleDebugDrawer = () => {
|
860 |
+
const { visible, showModal, hideModal } = useSetModalState();
|
861 |
+
const { saveGraph } = useSaveGraph();
|
862 |
+
|
863 |
+
const showSingleDebugDrawer = useCallback(async () => {
|
864 |
+
const saveRet = await saveGraph();
|
865 |
+
if (saveRet?.code === 0) {
|
866 |
+
showModal();
|
867 |
+
}
|
868 |
+
}, [saveGraph, showModal]);
|
869 |
+
|
870 |
+
return {
|
871 |
+
singleDebugDrawerVisible: visible,
|
872 |
+
hideSingleDebugDrawer: hideModal,
|
873 |
+
showSingleDebugDrawer,
|
874 |
+
};
|
875 |
+
};
|
web/src/pages/flow/run-drawer/index.tsx
CHANGED
@@ -1,26 +1,7 @@
|
|
1 |
-
import { Authorization } from '@/constants/authorization';
|
2 |
-
import { useSetModalState } from '@/hooks/common-hooks';
|
3 |
-
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
4 |
-
import { useHandleSubmittable } from '@/hooks/login-hooks';
|
5 |
import { IModalProps } from '@/interfaces/common';
|
6 |
-
import
|
7 |
-
import {
|
8 |
-
import { UploadOutlined } from '@ant-design/icons';
|
9 |
-
import {
|
10 |
-
Button,
|
11 |
-
Drawer,
|
12 |
-
Form,
|
13 |
-
FormItemProps,
|
14 |
-
Input,
|
15 |
-
InputNumber,
|
16 |
-
Select,
|
17 |
-
Switch,
|
18 |
-
Upload,
|
19 |
-
} from 'antd';
|
20 |
-
import { pick } from 'lodash';
|
21 |
-
import React, { useCallback, useState } from 'react';
|
22 |
import { useTranslation } from 'react-i18next';
|
23 |
-
import { BeginQueryType } from '../constant';
|
24 |
import {
|
25 |
useGetBeginNodeDataQuery,
|
26 |
useSaveGraphBeforeOpeningDebugDrawer,
|
@@ -28,152 +9,23 @@ import {
|
|
28 |
import { BeginQuery } from '../interface';
|
29 |
import useGraphStore from '../store';
|
30 |
import { getDrawerWidth } from '../utils';
|
31 |
-
import { PopoverForm } from './popover-form';
|
32 |
|
33 |
-
import
|
34 |
-
import { Link } from 'lucide-react';
|
35 |
-
import styles from './index.less';
|
36 |
|
37 |
const RunDrawer = ({
|
38 |
hideModal,
|
39 |
showModal: showChatModal,
|
40 |
}: IModalProps<any>) => {
|
41 |
const { t } = useTranslation();
|
42 |
-
const [form] = Form.useForm();
|
43 |
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
44 |
-
const {
|
45 |
-
visible,
|
46 |
-
hideModal: hidePopover,
|
47 |
-
switchVisible,
|
48 |
-
showModal: showPopover,
|
49 |
-
} = useSetModalState();
|
50 |
-
const { setRecord, currentRecord } = useSetSelectedRecord<number>();
|
51 |
-
const { submittable } = useHandleSubmittable(form);
|
52 |
-
const [isUploading, setIsUploading] = useState(false);
|
53 |
-
|
54 |
-
const handleShowPopover = useCallback(
|
55 |
-
(idx: number) => () => {
|
56 |
-
setRecord(idx);
|
57 |
-
showPopover();
|
58 |
-
},
|
59 |
-
[setRecord, showPopover],
|
60 |
-
);
|
61 |
|
62 |
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
63 |
const query: BeginQuery[] = getBeginNodeDataQuery();
|
64 |
|
65 |
-
const
|
66 |
-
|
67 |
-
return e;
|
68 |
-
}
|
69 |
-
return e?.fileList;
|
70 |
-
};
|
71 |
-
|
72 |
-
const onChange = useCallback(
|
73 |
-
(optional: boolean) =>
|
74 |
-
({ fileList }: UploadChangeParam<UploadFile>) => {
|
75 |
-
if (!optional) {
|
76 |
-
setIsUploading(fileList.some((x) => x.status === 'uploading'));
|
77 |
-
}
|
78 |
-
},
|
79 |
-
[],
|
80 |
);
|
81 |
|
82 |
-
const renderWidget = useCallback(
|
83 |
-
(q: BeginQuery, idx: number) => {
|
84 |
-
const props: FormItemProps & { key: number } = {
|
85 |
-
key: idx,
|
86 |
-
label: q.name,
|
87 |
-
name: idx,
|
88 |
-
};
|
89 |
-
if (q.optional === false) {
|
90 |
-
props.rules = [{ required: true }];
|
91 |
-
}
|
92 |
-
|
93 |
-
const urlList: { url: string; result: string }[] =
|
94 |
-
form.getFieldValue(idx) || [];
|
95 |
-
|
96 |
-
const BeginQueryTypeMap = {
|
97 |
-
[BeginQueryType.Line]: (
|
98 |
-
<Form.Item {...props}>
|
99 |
-
<Input></Input>
|
100 |
-
</Form.Item>
|
101 |
-
),
|
102 |
-
[BeginQueryType.Paragraph]: (
|
103 |
-
<Form.Item {...props}>
|
104 |
-
<Input.TextArea rows={4}></Input.TextArea>
|
105 |
-
</Form.Item>
|
106 |
-
),
|
107 |
-
[BeginQueryType.Options]: (
|
108 |
-
<Form.Item {...props}>
|
109 |
-
<Select
|
110 |
-
allowClear
|
111 |
-
options={q.options?.map((x) => ({ label: x, value: x })) ?? []}
|
112 |
-
></Select>
|
113 |
-
</Form.Item>
|
114 |
-
),
|
115 |
-
[BeginQueryType.File]: (
|
116 |
-
<React.Fragment key={idx}>
|
117 |
-
<Form.Item label={q.name} required={!q.optional}>
|
118 |
-
<div className="relative">
|
119 |
-
<Form.Item
|
120 |
-
{...props}
|
121 |
-
valuePropName="fileList"
|
122 |
-
getValueFromEvent={normFile}
|
123 |
-
noStyle
|
124 |
-
>
|
125 |
-
<Upload
|
126 |
-
name="file"
|
127 |
-
action={api.parse}
|
128 |
-
multiple
|
129 |
-
headers={{ [Authorization]: getAuthorization() }}
|
130 |
-
onChange={onChange(q.optional)}
|
131 |
-
>
|
132 |
-
<Button icon={<UploadOutlined />}>
|
133 |
-
{t('common.upload')}
|
134 |
-
</Button>
|
135 |
-
</Upload>
|
136 |
-
</Form.Item>
|
137 |
-
<Form.Item
|
138 |
-
{...pick(props, ['key', 'label', 'rules'])}
|
139 |
-
required={!q.optional}
|
140 |
-
className={urlList.length > 0 ? 'mb-1' : ''}
|
141 |
-
noStyle
|
142 |
-
>
|
143 |
-
<PopoverForm visible={visible} switchVisible={switchVisible}>
|
144 |
-
<Button
|
145 |
-
onClick={handleShowPopover(idx)}
|
146 |
-
className="absolute left-1/2 top-0"
|
147 |
-
icon={<Link className="size-3" />}
|
148 |
-
>
|
149 |
-
{t('flow.pasteFileLink')}
|
150 |
-
</Button>
|
151 |
-
</PopoverForm>
|
152 |
-
</Form.Item>
|
153 |
-
</div>
|
154 |
-
</Form.Item>
|
155 |
-
<Form.Item name={idx} noStyle {...pick(props, ['rules'])} />
|
156 |
-
</React.Fragment>
|
157 |
-
),
|
158 |
-
[BeginQueryType.Integer]: (
|
159 |
-
<Form.Item {...props}>
|
160 |
-
<InputNumber></InputNumber>
|
161 |
-
</Form.Item>
|
162 |
-
),
|
163 |
-
[BeginQueryType.Boolean]: (
|
164 |
-
<Form.Item valuePropName={'checked'} {...props}>
|
165 |
-
<Switch></Switch>
|
166 |
-
</Form.Item>
|
167 |
-
),
|
168 |
-
};
|
169 |
-
|
170 |
-
return BeginQueryTypeMap[q.type as BeginQueryType];
|
171 |
-
},
|
172 |
-
[form, handleShowPopover, onChange, switchVisible, t, visible],
|
173 |
-
);
|
174 |
-
|
175 |
-
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatModal!);
|
176 |
-
|
177 |
const handleRunAgent = useCallback(
|
178 |
(nextValues: Record<string, any>) => {
|
179 |
const currentNodes = updateNodeForm('begin', nextValues, ['query']);
|
@@ -183,25 +35,12 @@ const RunDrawer = ({
|
|
183 |
[handleRun, hideModal, updateNodeForm],
|
184 |
);
|
185 |
|
186 |
-
const onOk = useCallback(
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
nextValue = ``;
|
193 |
-
|
194 |
-
value.forEach((x) => {
|
195 |
-
nextValue +=
|
196 |
-
x?.originFileObj instanceof File
|
197 |
-
? `${x.name}\n${x.response?.data}\n----\n`
|
198 |
-
: `${x.url}\n${x.result}\n----\n`;
|
199 |
-
});
|
200 |
-
}
|
201 |
-
return { ...item, value: nextValue };
|
202 |
-
});
|
203 |
-
handleRunAgent(nextValues);
|
204 |
-
}, [form, handleRunAgent, query]);
|
205 |
|
206 |
return (
|
207 |
<Drawer
|
@@ -213,39 +52,11 @@ const RunDrawer = ({
|
|
213 |
width={getDrawerWidth()}
|
214 |
mask={false}
|
215 |
>
|
216 |
-
<
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
const urlInfo = basicForm.getFieldValue(currentRecord) || [];
|
222 |
-
basicForm.setFieldsValue({
|
223 |
-
[currentRecord]: [...urlInfo, { ...values, name: values.url }],
|
224 |
-
});
|
225 |
-
hidePopover();
|
226 |
-
}
|
227 |
-
}}
|
228 |
-
>
|
229 |
-
<Form
|
230 |
-
name="basicForm"
|
231 |
-
autoComplete="off"
|
232 |
-
layout={'vertical'}
|
233 |
-
form={form}
|
234 |
-
>
|
235 |
-
{query.map((x, idx) => {
|
236 |
-
return renderWidget(x, idx);
|
237 |
-
})}
|
238 |
-
</Form>
|
239 |
-
</Form.Provider>
|
240 |
-
</section>
|
241 |
-
<Button
|
242 |
-
type={'primary'}
|
243 |
-
block
|
244 |
-
onClick={onOk}
|
245 |
-
disabled={!submittable || isUploading}
|
246 |
-
>
|
247 |
-
{t('common.next')}
|
248 |
-
</Button>
|
249 |
</Drawer>
|
250 |
);
|
251 |
};
|
|
|
|
|
|
|
|
|
|
|
1 |
import { IModalProps } from '@/interfaces/common';
|
2 |
+
import { Drawer } from 'antd';
|
3 |
+
import { useCallback } from 'react';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import { useTranslation } from 'react-i18next';
|
|
|
5 |
import {
|
6 |
useGetBeginNodeDataQuery,
|
7 |
useSaveGraphBeforeOpeningDebugDrawer,
|
|
|
9 |
import { BeginQuery } from '../interface';
|
10 |
import useGraphStore from '../store';
|
11 |
import { getDrawerWidth } from '../utils';
|
|
|
12 |
|
13 |
+
import DebugContent from '../debug-content';
|
|
|
|
|
14 |
|
15 |
const RunDrawer = ({
|
16 |
hideModal,
|
17 |
showModal: showChatModal,
|
18 |
}: IModalProps<any>) => {
|
19 |
const { t } = useTranslation();
|
|
|
20 |
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
23 |
const query: BeginQuery[] = getBeginNodeDataQuery();
|
24 |
|
25 |
+
const { handleRun, loading } = useSaveGraphBeforeOpeningDebugDrawer(
|
26 |
+
showChatModal!,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
);
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
const handleRunAgent = useCallback(
|
30 |
(nextValues: Record<string, any>) => {
|
31 |
const currentNodes = updateNodeForm('begin', nextValues, ['query']);
|
|
|
35 |
[handleRun, hideModal, updateNodeForm],
|
36 |
);
|
37 |
|
38 |
+
const onOk = useCallback(
|
39 |
+
async (nextValues: any[]) => {
|
40 |
+
handleRunAgent(nextValues);
|
41 |
+
},
|
42 |
+
[handleRunAgent],
|
43 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
return (
|
46 |
<Drawer
|
|
|
52 |
width={getDrawerWidth()}
|
53 |
mask={false}
|
54 |
>
|
55 |
+
<DebugContent
|
56 |
+
ok={onOk}
|
57 |
+
parameters={query}
|
58 |
+
loading={loading}
|
59 |
+
></DebugContent>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
</Drawer>
|
61 |
);
|
62 |
};
|
web/src/pages/flow/utils.ts
CHANGED
@@ -7,7 +7,12 @@ import pipe from 'lodash/fp/pipe';
|
|
7 |
import isObject from 'lodash/isObject';
|
8 |
import { Edge, Node, Position } from 'reactflow';
|
9 |
import { v4 as uuidv4 } from 'uuid';
|
10 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
11 |
import { ICategorizeItemResult, IPosition, NodeData } from './interface';
|
12 |
|
13 |
const buildEdges = (
|
@@ -124,7 +129,7 @@ export const buildDslComponentsByGraph = (
|
|
124 |
const components: DSLComponents = {};
|
125 |
|
126 |
nodes
|
127 |
-
|
128 |
.forEach((x) => {
|
129 |
const id = x.id;
|
130 |
const operatorName = x.data.label;
|
@@ -323,3 +328,7 @@ export const duplicateNodeForm = (nodeData?: NodeData) => {
|
|
323 |
export const getDrawerWidth = () => {
|
324 |
return window.innerWidth > 1278 ? '40%' : 470;
|
325 |
};
|
|
|
|
|
|
|
|
|
|
7 |
import isObject from 'lodash/isObject';
|
8 |
import { Edge, Node, Position } from 'reactflow';
|
9 |
import { v4 as uuidv4 } from 'uuid';
|
10 |
+
import {
|
11 |
+
CategorizeAnchorPointPositions,
|
12 |
+
NoDebugOperatorsList,
|
13 |
+
NodeMap,
|
14 |
+
Operator,
|
15 |
+
} from './constant';
|
16 |
import { ICategorizeItemResult, IPosition, NodeData } from './interface';
|
17 |
|
18 |
const buildEdges = (
|
|
|
129 |
const components: DSLComponents = {};
|
130 |
|
131 |
nodes
|
132 |
+
?.filter((x) => x.data.label !== Operator.Note)
|
133 |
.forEach((x) => {
|
134 |
const id = x.id;
|
135 |
const operatorName = x.data.label;
|
|
|
328 |
export const getDrawerWidth = () => {
|
329 |
return window.innerWidth > 1278 ? '40%' : 470;
|
330 |
};
|
331 |
+
|
332 |
+
export const needsSingleStepDebugging = (label: string) => {
|
333 |
+
return !NoDebugOperatorsList.some((x) => (label as Operator) === x);
|
334 |
+
};
|
web/src/pages/login/index.tsx
CHANGED
@@ -168,7 +168,7 @@ const Login = () => {
|
|
168 |
onClick={toGoogle}
|
169 |
style={{ marginTop: 15 }}
|
170 |
>
|
171 |
-
<div>
|
172 |
<Icon
|
173 |
icon="local:github"
|
174 |
style={{ verticalAlign: 'middle', marginRight: 5 }}
|
|
|
168 |
onClick={toGoogle}
|
169 |
style={{ marginTop: 15 }}
|
170 |
>
|
171 |
+
<div className="flex items-center">
|
172 |
<Icon
|
173 |
icon="local:github"
|
174 |
style={{ verticalAlign: 'middle', marginRight: 5 }}
|
web/src/services/flow-service.ts
CHANGED
@@ -11,6 +11,8 @@ const {
|
|
11 |
runCanvas,
|
12 |
listTemplates,
|
13 |
testDbConnect,
|
|
|
|
|
14 |
} = api;
|
15 |
|
16 |
const methods = {
|
@@ -46,6 +48,14 @@ const methods = {
|
|
46 |
url: testDbConnect,
|
47 |
method: 'post',
|
48 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
} as const;
|
50 |
|
51 |
const chatService = registerServer<keyof typeof methods>(methods, request);
|
|
|
11 |
runCanvas,
|
12 |
listTemplates,
|
13 |
testDbConnect,
|
14 |
+
getInputElements,
|
15 |
+
debug,
|
16 |
} = api;
|
17 |
|
18 |
const methods = {
|
|
|
48 |
url: testDbConnect,
|
49 |
method: 'post',
|
50 |
},
|
51 |
+
getInputElements: {
|
52 |
+
url: getInputElements,
|
53 |
+
method: 'get',
|
54 |
+
},
|
55 |
+
debugSingle: {
|
56 |
+
url: debug,
|
57 |
+
method: 'post',
|
58 |
+
},
|
59 |
} as const;
|
60 |
|
61 |
const chatService = registerServer<keyof typeof methods>(methods, request);
|
web/src/utils/api.ts
CHANGED
@@ -118,4 +118,6 @@ export default {
|
|
118 |
resetCanvas: `${api_host}/canvas/reset`,
|
119 |
runCanvas: `${api_host}/canvas/completion`,
|
120 |
testDbConnect: `${api_host}/canvas/test_db_connect`,
|
|
|
|
|
121 |
};
|
|
|
118 |
resetCanvas: `${api_host}/canvas/reset`,
|
119 |
runCanvas: `${api_host}/canvas/completion`,
|
120 |
testDbConnect: `${api_host}/canvas/test_db_connect`,
|
121 |
+
getInputElements: `${api_host}/canvas/input_elements`,
|
122 |
+
debug: `${api_host}/canvas/debug`,
|
123 |
};
|
web/src/utils/request.ts
CHANGED
@@ -107,31 +107,25 @@ request.interceptors.response.use(async (response: any, options) => {
|
|
107 |
return response;
|
108 |
}
|
109 |
|
110 |
-
const data: ResponseType = await response
|
111 |
-
|
112 |
-
|
|
|
113 |
notification.error({
|
114 |
-
message: data
|
115 |
-
description: data
|
116 |
duration: 3,
|
117 |
});
|
118 |
authorizationUtil.removeAll();
|
119 |
history.push('/login'); // Will not jump to the login page
|
120 |
-
} else if (data
|
121 |
-
|
122 |
-
message.
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
description: data.message,
|
127 |
-
duration: 3,
|
128 |
-
});
|
129 |
-
}
|
130 |
-
|
131 |
-
return response;
|
132 |
-
} else {
|
133 |
-
return response;
|
134 |
}
|
|
|
135 |
});
|
136 |
|
137 |
export default request;
|
|
|
107 |
return response;
|
108 |
}
|
109 |
|
110 |
+
const data: ResponseType = await response?.clone()?.json();
|
111 |
+
if (data?.code === 100) {
|
112 |
+
message.error(data?.message);
|
113 |
+
} else if (data?.code === 401) {
|
114 |
notification.error({
|
115 |
+
message: data?.message,
|
116 |
+
description: data?.message,
|
117 |
duration: 3,
|
118 |
});
|
119 |
authorizationUtil.removeAll();
|
120 |
history.push('/login'); // Will not jump to the login page
|
121 |
+
} else if (data?.code !== 0) {
|
122 |
+
notification.error({
|
123 |
+
message: `${i18n.t('message.hint')} : ${data?.code}`,
|
124 |
+
description: data?.message,
|
125 |
+
duration: 3,
|
126 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
}
|
128 |
+
return response;
|
129 |
});
|
130 |
|
131 |
export default request;
|