mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-12 05:48:58 +08:00
### 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)
This commit is contained in:
parent
0bca46ac3a
commit
1defe0b19b
@ -1,5 +1,6 @@
|
||||
import { ResponseType } from '@/interfaces/database/base';
|
||||
import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
|
||||
import { IDebugSingleRequestBody } from '@/interfaces/request/flow';
|
||||
import i18n from '@/locales/config';
|
||||
import flowService from '@/services/flow-service';
|
||||
import { buildMessageListWithUuid } from '@/utils/chat';
|
||||
@ -220,3 +221,50 @@ export const useTestDbConnect = () => {
|
||||
|
||||
return { data, loading, testDbConnect: mutateAsync };
|
||||
};
|
||||
|
||||
export const useFetchInputElements = (componentId?: string) => {
|
||||
const { id } = useParams();
|
||||
|
||||
const { data, isPending: loading } = useQuery({
|
||||
queryKey: ['fetchInputElements', id, componentId],
|
||||
initialData: [],
|
||||
enabled: !!id && !!componentId,
|
||||
retryOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const { data } = await flowService.getInputElements({
|
||||
id,
|
||||
component_id: componentId,
|
||||
});
|
||||
return data?.data ?? [];
|
||||
} catch (error) {
|
||||
console.log('🚀 ~ queryFn: ~ error:', error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading };
|
||||
};
|
||||
|
||||
export const useDebugSingle = () => {
|
||||
const { id } = useParams();
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: ['debugSingle'],
|
||||
mutationFn: async (params: IDebugSingleRequestBody) => {
|
||||
const ret = await flowService.debugSingle({ id, ...params });
|
||||
if (ret?.data?.code !== 0) {
|
||||
message.error(ret?.data?.message);
|
||||
}
|
||||
return ret?.data?.data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, debugSingle: mutateAsync };
|
||||
};
|
||||
|
4
web/src/interfaces/request/flow.ts
Normal file
4
web/src/interfaces/request/flow.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IDebugSingleRequestBody {
|
||||
component_id: string;
|
||||
params: any[];
|
||||
}
|
@ -381,8 +381,7 @@ The above is the content you need to summarize.`,
|
||||
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.`,
|
||||
temperature: 'Temperature',
|
||||
temperatureMessage: 'Temperature is required',
|
||||
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.`,
|
||||
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.`,
|
||||
topP: 'Top P',
|
||||
topPMessage: 'Top P is required',
|
||||
topPTip:
|
||||
@ -397,8 +396,7 @@ The above is the content you need to summarize.`,
|
||||
'Similar to the presence penalty, this reduces the model’s tendency to repeat the same words frequently.',
|
||||
maxTokens: 'Max tokens',
|
||||
maxTokensMessage: 'Max tokens is required',
|
||||
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.`,
|
||||
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.`,
|
||||
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
|
||||
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
|
||||
quote: 'Show quote',
|
||||
@ -430,7 +428,7 @@ The above is the content you need to summarize.`,
|
||||
partialTitle: 'Partial Embed',
|
||||
extensionTitle: 'Chrome Extension',
|
||||
tokenError: 'Please create API Token first!',
|
||||
betaError: 'The beta field of the API Token cannot be empty!',
|
||||
betaError: 'Please apply an API key in system setting firstly.',
|
||||
searching: 'Searching...',
|
||||
parsing: 'Parsing',
|
||||
uploading: 'Uploading',
|
||||
@ -453,8 +451,7 @@ The above is the content you need to summarize.`,
|
||||
profileDescription: 'Update your photo and personal details here.',
|
||||
maxTokens: 'Max Tokens',
|
||||
maxTokensMessage: 'Max Tokens is required',
|
||||
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.`,
|
||||
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.`,
|
||||
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
|
||||
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
|
||||
password: 'Password',
|
||||
@ -774,7 +771,7 @@ The above is the content you need to summarize.`,
|
||||
sourceLang: 'Source language',
|
||||
targetLang: 'Target language',
|
||||
gitHub: 'GitHub',
|
||||
githubDescription:
|
||||
gitHubDescription:
|
||||
'This component is used to search the repository from https://github.com/. Top N specifies the number of search results to be adjusted.',
|
||||
baiduFanyi: 'BaiduFanyi',
|
||||
baiduFanyiDescription:
|
||||
|
@ -414,7 +414,7 @@ export default {
|
||||
partialTitle: '部分嵌入',
|
||||
extensionTitle: 'Chrome 插件',
|
||||
tokenError: '請先創建 API Token!',
|
||||
betaError: 'API Token的beta欄位不可以為空!',
|
||||
betaError: '請先在系統設定中申請API密鑰。',
|
||||
searching: '搜索中',
|
||||
parsing: '解析中',
|
||||
uploading: '上傳中',
|
||||
|
@ -431,7 +431,7 @@ export default {
|
||||
partialTitle: '部分嵌入',
|
||||
extensionTitle: 'Chrome 插件',
|
||||
tokenError: '请先创建 API Token!',
|
||||
betaError: 'API Token的beta字段不可以为空!',
|
||||
betaError: '请先在系统设置中申请API密钥。',
|
||||
searching: '搜索中',
|
||||
parsing: '解析中',
|
||||
uploading: '上传中',
|
||||
@ -760,7 +760,7 @@ export default {
|
||||
sourceLang: '源语言',
|
||||
targetLang: '目标语言',
|
||||
gitHub: 'GitHub',
|
||||
githubDescription:
|
||||
gitHubDescription:
|
||||
'该组件用于从 https://github.com/ 搜索仓库。Top N 指定需要调整的搜索结果数量。',
|
||||
baiduFanyi: '百度翻译',
|
||||
baiduFanyiDescription:
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { get } from 'lodash';
|
||||
import { FolderInput, FolderOutput } from 'lucide-react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import ReactFlow, {
|
||||
@ -24,6 +25,7 @@ import {
|
||||
useHandleExportOrImportJsonFile,
|
||||
useSelectCanvasData,
|
||||
useShowFormDrawer,
|
||||
useShowSingleDebugDrawer,
|
||||
useValidateConnection,
|
||||
useWatchNodeFormDataChange,
|
||||
} from '../hooks';
|
||||
@ -95,6 +97,11 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
showModal: showChatModal,
|
||||
hideModal: hideChatModal,
|
||||
} = useSetModalState();
|
||||
const {
|
||||
singleDebugDrawerVisible,
|
||||
showSingleDebugDrawer,
|
||||
hideSingleDebugDrawer,
|
||||
} = useShowSingleDebugDrawer();
|
||||
|
||||
const { formDrawerVisible, hideFormDrawer, showFormDrawer, clickedNode } =
|
||||
useShowFormDrawer();
|
||||
@ -116,11 +123,24 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
const onNodeClick: NodeMouseHandler = useCallback(
|
||||
(e, node) => {
|
||||
if (node.data.label !== Operator.Note) {
|
||||
hideSingleDebugDrawer();
|
||||
hideRunOrChatDrawer();
|
||||
showFormDrawer(node);
|
||||
}
|
||||
// handle single debug icon click
|
||||
if (
|
||||
get(e.target, 'dataset.play') === 'true' ||
|
||||
get(e.target, 'parentNode.dataset.play') === 'true'
|
||||
) {
|
||||
showSingleDebugDrawer();
|
||||
}
|
||||
},
|
||||
[hideRunOrChatDrawer, showFormDrawer],
|
||||
[
|
||||
hideRunOrChatDrawer,
|
||||
hideSingleDebugDrawer,
|
||||
showFormDrawer,
|
||||
showSingleDebugDrawer,
|
||||
],
|
||||
);
|
||||
|
||||
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
||||
@ -193,12 +213,6 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
onSelectionChange={onSelectionChange}
|
||||
nodeOrigin={[0.5, 0]}
|
||||
isValidConnection={isValidConnection}
|
||||
onChangeCapture={(...params) => {
|
||||
console.info('onChangeCapture:', ...params);
|
||||
}}
|
||||
onChange={(...params) => {
|
||||
console.info('params:', ...params);
|
||||
}}
|
||||
defaultEdgeOptions={{
|
||||
type: 'buttonEdge',
|
||||
markerEnd: 'logo',
|
||||
@ -214,7 +228,7 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
<ControlButton onClick={handleImportJson}>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<TooltipTrigger asChild>
|
||||
<FolderInput />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Import</TooltipContent>
|
||||
@ -224,7 +238,7 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
<ControlButton onClick={handleExportJson}>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<TooltipTrigger asChild>
|
||||
<FolderOutput />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Export</TooltipContent>
|
||||
@ -238,6 +252,9 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
node={clickedNode}
|
||||
visible={formDrawerVisible}
|
||||
hideModal={hideFormDrawer}
|
||||
singleDebugDrawerVisible={singleDebugDrawerVisible}
|
||||
hideSingleDebugDrawer={hideSingleDebugDrawer}
|
||||
showSingleDebugDrawer={showSingleDebugDrawer}
|
||||
></FormDrawer>
|
||||
)}
|
||||
{chatVisible && (
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { Flex } from 'antd';
|
||||
import { Play } from 'lucide-react';
|
||||
import { Operator, operatorMap } from '../../constant';
|
||||
import OperatorIcon from '../../operator-icon';
|
||||
import { needsSingleStepDebugging } from '../../utils';
|
||||
import NodeDropdown from './dropdown';
|
||||
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import styles from './index.less';
|
||||
import { NextNodePopover } from './popover';
|
||||
|
||||
import { RunTooltip } from '../../flow-tooltip';
|
||||
import styles from './index.less';
|
||||
interface IProps {
|
||||
id: string;
|
||||
label: string;
|
||||
@ -15,12 +17,17 @@ interface IProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function RunStatus({ id, name }: Omit<IProps, 'label'>) {
|
||||
export function RunStatus({ id, name, label }: IProps) {
|
||||
const { t } = useTranslate('flow');
|
||||
return (
|
||||
<section className="flex justify-end items-center pb-1 ">
|
||||
<section className="flex justify-end items-center pb-1 gap-2 text-blue-600">
|
||||
{needsSingleStepDebugging(label) && (
|
||||
<RunTooltip>
|
||||
<Play className="size-3 cursor-pointer" data-play />
|
||||
</RunTooltip> // data-play is used to trigger single step debugging
|
||||
)}
|
||||
<NextNodePopover nodeId={id} name={name}>
|
||||
<span className="text-blue-600 cursor-pointer text-[10px]">
|
||||
<span className="cursor-pointer text-[10px]">
|
||||
{t('operationResults')}
|
||||
</span>
|
||||
</NextNodePopover>
|
||||
@ -30,8 +37,10 @@ export function RunStatus({ id, name }: Omit<IProps, 'label'>) {
|
||||
|
||||
const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => {
|
||||
return (
|
||||
<section className="haha">
|
||||
{label !== Operator.Answer && <RunStatus id={id} name={name}></RunStatus>}
|
||||
<section>
|
||||
{label !== Operator.Answer && (
|
||||
<RunStatus id={id} name={name} label={label}></RunStatus>
|
||||
)}
|
||||
<Flex
|
||||
flex={1}
|
||||
align="center"
|
||||
|
@ -2931,3 +2931,13 @@ export const BeginQueryTypeIconMap = {
|
||||
[BeginQueryType.Integer]: ListOrdered,
|
||||
[BeginQueryType.Boolean]: ToggleLeft,
|
||||
};
|
||||
|
||||
export const NoDebugOperatorsList = [
|
||||
Operator.Begin,
|
||||
Operator.Answer,
|
||||
Operator.Concentrator,
|
||||
Operator.Template,
|
||||
Operator.Message,
|
||||
Operator.RewriteQuestion,
|
||||
Operator.Switch,
|
||||
];
|
||||
|
5
web/src/pages/flow/debug-content/index.less
Normal file
5
web/src/pages/flow/debug-content/index.less
Normal file
@ -0,0 +1,5 @@
|
||||
.formWrapper {
|
||||
:global(.ant-form-item-label) {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
}
|
238
web/src/pages/flow/debug-content/index.tsx
Normal file
238
web/src/pages/flow/debug-content/index.tsx
Normal file
@ -0,0 +1,238 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
||||
import { useHandleSubmittable } from '@/hooks/login-hooks';
|
||||
import api from '@/utils/api';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItemProps,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Switch,
|
||||
Upload,
|
||||
} from 'antd';
|
||||
import { UploadChangeParam, UploadFile } from 'antd/es/upload';
|
||||
import { pick } from 'lodash';
|
||||
import { Link } from 'lucide-react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BeginQueryType } from '../constant';
|
||||
import { BeginQuery } from '../interface';
|
||||
import { PopoverForm } from './popover-form';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
interface IProps {
|
||||
parameters: BeginQuery[];
|
||||
ok(parameters: any[]): void;
|
||||
isNext?: boolean;
|
||||
loading?: boolean;
|
||||
submitButtonDisabled?: boolean;
|
||||
}
|
||||
|
||||
const DebugContent = ({
|
||||
parameters,
|
||||
ok,
|
||||
isNext = true,
|
||||
loading = false,
|
||||
submitButtonDisabled = false,
|
||||
}: IProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const {
|
||||
visible,
|
||||
hideModal: hidePopover,
|
||||
switchVisible,
|
||||
showModal: showPopover,
|
||||
} = useSetModalState();
|
||||
const { setRecord, currentRecord } = useSetSelectedRecord<number>();
|
||||
const { submittable } = useHandleSubmittable(form);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
const handleShowPopover = useCallback(
|
||||
(idx: number) => () => {
|
||||
setRecord(idx);
|
||||
showPopover();
|
||||
},
|
||||
[setRecord, showPopover],
|
||||
);
|
||||
|
||||
const normFile = (e: any) => {
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
return e?.fileList;
|
||||
};
|
||||
|
||||
const onChange = useCallback(
|
||||
(optional: boolean) =>
|
||||
({ fileList }: UploadChangeParam<UploadFile>) => {
|
||||
if (!optional) {
|
||||
setIsUploading(fileList.some((x) => x.status === 'uploading'));
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const renderWidget = useCallback(
|
||||
(q: BeginQuery, idx: number) => {
|
||||
const props: FormItemProps & { key: number } = {
|
||||
key: idx,
|
||||
label: q.name ?? q.key,
|
||||
name: idx,
|
||||
};
|
||||
if (q.optional === false) {
|
||||
props.rules = [{ required: true }];
|
||||
}
|
||||
|
||||
const urlList: { url: string; result: string }[] =
|
||||
form.getFieldValue(idx) || [];
|
||||
|
||||
const BeginQueryTypeMap = {
|
||||
[BeginQueryType.Line]: (
|
||||
<Form.Item {...props}>
|
||||
<Input></Input>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.Paragraph]: (
|
||||
<Form.Item {...props}>
|
||||
<Input.TextArea rows={1}></Input.TextArea>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.Options]: (
|
||||
<Form.Item {...props}>
|
||||
<Select
|
||||
allowClear
|
||||
options={q.options?.map((x) => ({ label: x, value: x })) ?? []}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.File]: (
|
||||
<React.Fragment key={idx}>
|
||||
<Form.Item label={q.name ?? q.key} required={!q.optional}>
|
||||
<div className="relative">
|
||||
<Form.Item
|
||||
{...props}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={normFile}
|
||||
noStyle
|
||||
>
|
||||
<Upload
|
||||
name="file"
|
||||
action={api.parse}
|
||||
multiple
|
||||
headers={{ [Authorization]: getAuthorization() }}
|
||||
onChange={onChange(q.optional)}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>
|
||||
{t('common.upload')}
|
||||
</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...pick(props, ['key', 'label', 'rules'])}
|
||||
required={!q.optional}
|
||||
className={urlList.length > 0 ? 'mb-1' : ''}
|
||||
noStyle
|
||||
>
|
||||
<PopoverForm visible={visible} switchVisible={switchVisible}>
|
||||
<Button
|
||||
onClick={handleShowPopover(idx)}
|
||||
className="absolute left-1/2 top-0"
|
||||
icon={<Link className="size-3" />}
|
||||
>
|
||||
{t('flow.pasteFileLink')}
|
||||
</Button>
|
||||
</PopoverForm>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item name={idx} noStyle {...pick(props, ['rules'])} />
|
||||
</React.Fragment>
|
||||
),
|
||||
[BeginQueryType.Integer]: (
|
||||
<Form.Item {...props}>
|
||||
<InputNumber></InputNumber>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.Boolean]: (
|
||||
<Form.Item valuePropName={'checked'} {...props}>
|
||||
<Switch></Switch>
|
||||
</Form.Item>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
BeginQueryTypeMap[q.type as BeginQueryType] ??
|
||||
BeginQueryTypeMap[BeginQueryType.Paragraph]
|
||||
);
|
||||
},
|
||||
[form, handleShowPopover, onChange, switchVisible, t, visible],
|
||||
);
|
||||
|
||||
const onOk = useCallback(async () => {
|
||||
const values = await form.validateFields();
|
||||
const nextValues = Object.entries(values).map(([key, value]) => {
|
||||
const item = parameters[Number(key)];
|
||||
let nextValue = value;
|
||||
if (Array.isArray(value)) {
|
||||
nextValue = ``;
|
||||
|
||||
value.forEach((x) => {
|
||||
nextValue +=
|
||||
x?.originFileObj instanceof File
|
||||
? `${x.name}\n${x.response?.data}\n----\n`
|
||||
: `${x.url}\n${x.result}\n----\n`;
|
||||
});
|
||||
}
|
||||
return { ...item, value: nextValue };
|
||||
});
|
||||
|
||||
ok(nextValues);
|
||||
}, [form, ok, parameters]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className={styles.formWrapper}>
|
||||
<Form.Provider
|
||||
onFormFinish={(name, { values, forms }) => {
|
||||
if (name === 'urlForm') {
|
||||
const { basicForm } = forms;
|
||||
const urlInfo = basicForm.getFieldValue(currentRecord) || [];
|
||||
basicForm.setFieldsValue({
|
||||
[currentRecord]: [...urlInfo, { ...values, name: values.url }],
|
||||
});
|
||||
hidePopover();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form
|
||||
name="basicForm"
|
||||
autoComplete="off"
|
||||
layout={'vertical'}
|
||||
form={form}
|
||||
>
|
||||
{parameters.map((x, idx) => {
|
||||
return renderWidget(x, idx);
|
||||
})}
|
||||
</Form>
|
||||
</Form.Provider>
|
||||
</section>
|
||||
<Button
|
||||
type={'primary'}
|
||||
block
|
||||
onClick={onOk}
|
||||
loading={loading}
|
||||
disabled={!submittable || isUploading || submitButtonDisabled}
|
||||
>
|
||||
{t(isNext ? 'common.next' : 'flow.run')}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DebugContent;
|
74
web/src/pages/flow/debug-content/popover-form.tsx
Normal file
74
web/src/pages/flow/debug-content/popover-form.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import { useParseDocument } from '@/hooks/document-hooks';
|
||||
import { useResetFormOnCloseModal } from '@/hooks/logic-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { Button, Form, Input, Popover } from 'antd';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const reg =
|
||||
/^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/;
|
||||
|
||||
export const PopoverForm = ({
|
||||
children,
|
||||
visible,
|
||||
switchVisible,
|
||||
}: PropsWithChildren<IModalProps<any>>) => {
|
||||
const [form] = Form.useForm();
|
||||
const { parseDocument, loading } = useParseDocument();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useResetFormOnCloseModal({
|
||||
form,
|
||||
visible,
|
||||
});
|
||||
|
||||
const onOk = async () => {
|
||||
const values = await form.validateFields();
|
||||
const val = values.url;
|
||||
|
||||
if (reg.test(val)) {
|
||||
const ret = await parseDocument(val);
|
||||
if (ret?.data?.code === 0) {
|
||||
form.setFieldValue('result', ret?.data?.data);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const content = (
|
||||
<Form form={form} name="urlForm">
|
||||
<Form.Item
|
||||
name="url"
|
||||
rules={[{ required: true, type: 'url' }]}
|
||||
className="m-0"
|
||||
>
|
||||
<Input
|
||||
onPressEnter={(e) => e.preventDefault()}
|
||||
placeholder={t('flow.pasteFileLink')}
|
||||
suffix={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onOk}
|
||||
size={'small'}
|
||||
loading={loading}
|
||||
>
|
||||
{t('common.submit')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name={'result'} noStyle />
|
||||
</Form>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={content}
|
||||
open={visible}
|
||||
trigger={'click'}
|
||||
onOpenChange={switchVisible}
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
);
|
||||
};
|
@ -13,3 +13,9 @@
|
||||
padding-top: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.formDrawer {
|
||||
:global(.ant-drawer-content-wrapper) {
|
||||
transform: translateX(0) !important;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Drawer, Flex, Form, Input } from 'antd';
|
||||
import { lowerFirst } from 'lodash';
|
||||
import { Play } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import { Node } from 'reactflow';
|
||||
import { Operator, operatorMap } from '../constant';
|
||||
@ -15,6 +18,7 @@ import CategorizeForm from '../form/categorize-form';
|
||||
import CrawlerForm from '../form/crawler-form';
|
||||
import DeepLForm from '../form/deepl-form';
|
||||
import DuckDuckGoForm from '../form/duckduckgo-form';
|
||||
import EmailForm from '../form/email-form';
|
||||
import ExeSQLForm from '../form/exesql-form';
|
||||
import GenerateForm from '../form/generate-form';
|
||||
import GithubForm from '../form/github-form';
|
||||
@ -30,22 +34,24 @@ import RelevantForm from '../form/relevant-form';
|
||||
import RetrievalForm from '../form/retrieval-form';
|
||||
import RewriteQuestionForm from '../form/rewrite-question-form';
|
||||
import SwitchForm from '../form/switch-form';
|
||||
import TemplateForm from '../form/template-form';
|
||||
import TuShareForm from '../form/tushare-form';
|
||||
import WenCaiForm from '../form/wencai-form';
|
||||
import WikipediaForm from '../form/wikipedia-form';
|
||||
import YahooFinanceForm from '../form/yahoo-finance-form';
|
||||
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
||||
import OperatorIcon from '../operator-icon';
|
||||
import { getDrawerWidth, needsSingleStepDebugging } from '../utils';
|
||||
import SingleDebugDrawer from './single-debug-drawer';
|
||||
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { lowerFirst } from 'lodash';
|
||||
import EmailForm from '../form/email-form';
|
||||
import TemplateForm from '../form/template-form';
|
||||
import { getDrawerWidth } from '../utils';
|
||||
import { RunTooltip } from '../flow-tooltip';
|
||||
import styles from './index.less';
|
||||
|
||||
interface IProps {
|
||||
node?: Node;
|
||||
singleDebugDrawerVisible: IModalProps<any>['visible'];
|
||||
hideSingleDebugDrawer: IModalProps<any>['hideModal'];
|
||||
showSingleDebugDrawer: IModalProps<any>['showModal'];
|
||||
}
|
||||
|
||||
const FormMap = {
|
||||
@ -91,6 +97,9 @@ const FormDrawer = ({
|
||||
visible,
|
||||
hideModal,
|
||||
node,
|
||||
singleDebugDrawerVisible,
|
||||
hideSingleDebugDrawer,
|
||||
showSingleDebugDrawer,
|
||||
}: IModalProps<any> & IProps) => {
|
||||
const operatorName: Operator = node?.data.label;
|
||||
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
|
||||
@ -99,12 +108,14 @@ const FormDrawer = ({
|
||||
id: node?.id,
|
||||
data: node?.data,
|
||||
});
|
||||
|
||||
const { t } = useTranslate('flow');
|
||||
|
||||
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
form.resetFields();
|
||||
form.setFieldsValue(node?.data?.form);
|
||||
}
|
||||
}, [visible, form, node?.data?.form]);
|
||||
@ -128,6 +139,14 @@ const FormDrawer = ({
|
||||
onChange={handleNameChange}
|
||||
></Input>
|
||||
</Flex>
|
||||
{needsSingleStepDebugging(operatorName) && (
|
||||
<RunTooltip>
|
||||
<Play
|
||||
className="size-5 cursor-pointer"
|
||||
onClick={showSingleDebugDrawer}
|
||||
/>
|
||||
</RunTooltip>
|
||||
)}
|
||||
<CloseOutlined onClick={hideModal} />
|
||||
</Flex>
|
||||
<span className={styles.operatorDescription}>
|
||||
@ -142,6 +161,7 @@ const FormDrawer = ({
|
||||
mask={false}
|
||||
width={getDrawerWidth()}
|
||||
closeIcon={null}
|
||||
rootClassName={styles.formDrawer}
|
||||
>
|
||||
<section className={styles.formWrapper}>
|
||||
{visible && (
|
||||
@ -152,6 +172,13 @@ const FormDrawer = ({
|
||||
></OperatorForm>
|
||||
)}
|
||||
</section>
|
||||
{singleDebugDrawerVisible && (
|
||||
<SingleDebugDrawer
|
||||
visible={singleDebugDrawerVisible}
|
||||
hideModal={hideSingleDebugDrawer}
|
||||
componentId={node?.id}
|
||||
></SingleDebugDrawer>
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
81
web/src/pages/flow/flow-drawer/single-debug-drawer/index.tsx
Normal file
81
web/src/pages/flow/flow-drawer/single-debug-drawer/index.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||
import { useDebugSingle, useFetchInputElements } from '@/hooks/flow-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Drawer } from 'antd';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import JsonView from 'react18-json-view';
|
||||
import 'react18-json-view/src/style.css';
|
||||
import DebugContent from '../../debug-content';
|
||||
|
||||
interface IProps {
|
||||
componentId?: string;
|
||||
}
|
||||
|
||||
const SingleDebugDrawer = ({
|
||||
componentId,
|
||||
visible,
|
||||
hideModal,
|
||||
}: IModalProps<any> & IProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { data: list } = useFetchInputElements(componentId);
|
||||
const { debugSingle, data, loading } = useDebugSingle();
|
||||
|
||||
const onOk = useCallback(
|
||||
(nextValues: any[]) => {
|
||||
if (componentId) {
|
||||
debugSingle({ component_id: componentId, params: nextValues });
|
||||
}
|
||||
},
|
||||
[componentId, debugSingle],
|
||||
);
|
||||
|
||||
const content = JSON.stringify(data, null, 2);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
<div className="flex justify-between">
|
||||
{t('flow.testRun')}
|
||||
<CloseOutlined onClick={hideModal} />
|
||||
</div>
|
||||
}
|
||||
width={'100%'}
|
||||
onClose={hideModal}
|
||||
open={visible}
|
||||
getContainer={false}
|
||||
mask={false}
|
||||
placement={'bottom'}
|
||||
height={'95%'}
|
||||
closeIcon={null}
|
||||
>
|
||||
<section className="overflow-y-auto">
|
||||
<DebugContent
|
||||
parameters={list}
|
||||
ok={onOk}
|
||||
isNext={false}
|
||||
loading={loading}
|
||||
submitButtonDisabled={list.length === 0}
|
||||
></DebugContent>
|
||||
{!isEmpty(data) ? (
|
||||
<div className="mt-4 rounded-md bg-slate-200 border border-neutral-200">
|
||||
<div className="flex justify-between p-2">
|
||||
<span>JSON</span>
|
||||
<CopyToClipboard text={content}></CopyToClipboard>
|
||||
</div>
|
||||
<JsonView
|
||||
src={data}
|
||||
displaySize
|
||||
collapseStringsAfterLength={100000000000}
|
||||
className="w-full h-[800px] break-words overflow-auto p-2 bg-slate-100"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default SingleDebugDrawer;
|
22
web/src/pages/flow/flow-tooltip.tsx
Normal file
22
web/src/pages/flow/flow-tooltip.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RunTooltip = ({ children }: PropsWithChildren) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>{children}</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{t('flow.testRun')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
@ -1,9 +1,11 @@
|
||||
import Editor from '@monaco-editor/react';
|
||||
import Editor, { loader } from '@monaco-editor/react';
|
||||
import { Form, Input, InputNumber, Select, Space, Switch } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IOperatorForm } from '../../interface';
|
||||
import DynamicVariablesForm from './dynamic-variables';
|
||||
|
||||
loader.config({ paths: { vs: '/vs' } });
|
||||
|
||||
enum Method {
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
|
@ -620,7 +620,6 @@ export const useWatchNodeFormDataChange = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
console.info('xxx');
|
||||
nodes.forEach((node) => {
|
||||
const currentNode = getNode(node.id);
|
||||
const form = currentNode?.data.form ?? {};
|
||||
@ -856,3 +855,21 @@ export const useHandleExportOrImportJsonFile = () => {
|
||||
onFileUploadOk,
|
||||
};
|
||||
};
|
||||
|
||||
export const useShowSingleDebugDrawer = () => {
|
||||
const { visible, showModal, hideModal } = useSetModalState();
|
||||
const { saveGraph } = useSaveGraph();
|
||||
|
||||
const showSingleDebugDrawer = useCallback(async () => {
|
||||
const saveRet = await saveGraph();
|
||||
if (saveRet?.code === 0) {
|
||||
showModal();
|
||||
}
|
||||
}, [saveGraph, showModal]);
|
||||
|
||||
return {
|
||||
singleDebugDrawerVisible: visible,
|
||||
hideSingleDebugDrawer: hideModal,
|
||||
showSingleDebugDrawer,
|
||||
};
|
||||
};
|
||||
|
@ -1,26 +1,7 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
||||
import { useHandleSubmittable } from '@/hooks/login-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import api from '@/utils/api';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Drawer,
|
||||
Form,
|
||||
FormItemProps,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Switch,
|
||||
Upload,
|
||||
} from 'antd';
|
||||
import { pick } from 'lodash';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Drawer } from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BeginQueryType } from '../constant';
|
||||
import {
|
||||
useGetBeginNodeDataQuery,
|
||||
useSaveGraphBeforeOpeningDebugDrawer,
|
||||
@ -28,152 +9,23 @@ import {
|
||||
import { BeginQuery } from '../interface';
|
||||
import useGraphStore from '../store';
|
||||
import { getDrawerWidth } from '../utils';
|
||||
import { PopoverForm } from './popover-form';
|
||||
|
||||
import { UploadChangeParam, UploadFile } from 'antd/es/upload';
|
||||
import { Link } from 'lucide-react';
|
||||
import styles from './index.less';
|
||||
import DebugContent from '../debug-content';
|
||||
|
||||
const RunDrawer = ({
|
||||
hideModal,
|
||||
showModal: showChatModal,
|
||||
}: IModalProps<any>) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
||||
const {
|
||||
visible,
|
||||
hideModal: hidePopover,
|
||||
switchVisible,
|
||||
showModal: showPopover,
|
||||
} = useSetModalState();
|
||||
const { setRecord, currentRecord } = useSetSelectedRecord<number>();
|
||||
const { submittable } = useHandleSubmittable(form);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
const handleShowPopover = useCallback(
|
||||
(idx: number) => () => {
|
||||
setRecord(idx);
|
||||
showPopover();
|
||||
},
|
||||
[setRecord, showPopover],
|
||||
);
|
||||
|
||||
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
||||
const query: BeginQuery[] = getBeginNodeDataQuery();
|
||||
|
||||
const normFile = (e: any) => {
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
return e?.fileList;
|
||||
};
|
||||
|
||||
const onChange = useCallback(
|
||||
(optional: boolean) =>
|
||||
({ fileList }: UploadChangeParam<UploadFile>) => {
|
||||
if (!optional) {
|
||||
setIsUploading(fileList.some((x) => x.status === 'uploading'));
|
||||
}
|
||||
},
|
||||
[],
|
||||
const { handleRun, loading } = useSaveGraphBeforeOpeningDebugDrawer(
|
||||
showChatModal!,
|
||||
);
|
||||
|
||||
const renderWidget = useCallback(
|
||||
(q: BeginQuery, idx: number) => {
|
||||
const props: FormItemProps & { key: number } = {
|
||||
key: idx,
|
||||
label: q.name,
|
||||
name: idx,
|
||||
};
|
||||
if (q.optional === false) {
|
||||
props.rules = [{ required: true }];
|
||||
}
|
||||
|
||||
const urlList: { url: string; result: string }[] =
|
||||
form.getFieldValue(idx) || [];
|
||||
|
||||
const BeginQueryTypeMap = {
|
||||
[BeginQueryType.Line]: (
|
||||
<Form.Item {...props}>
|
||||
<Input></Input>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.Paragraph]: (
|
||||
<Form.Item {...props}>
|
||||
<Input.TextArea rows={4}></Input.TextArea>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.Options]: (
|
||||
<Form.Item {...props}>
|
||||
<Select
|
||||
allowClear
|
||||
options={q.options?.map((x) => ({ label: x, value: x })) ?? []}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.File]: (
|
||||
<React.Fragment key={idx}>
|
||||
<Form.Item label={q.name} required={!q.optional}>
|
||||
<div className="relative">
|
||||
<Form.Item
|
||||
{...props}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={normFile}
|
||||
noStyle
|
||||
>
|
||||
<Upload
|
||||
name="file"
|
||||
action={api.parse}
|
||||
multiple
|
||||
headers={{ [Authorization]: getAuthorization() }}
|
||||
onChange={onChange(q.optional)}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>
|
||||
{t('common.upload')}
|
||||
</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...pick(props, ['key', 'label', 'rules'])}
|
||||
required={!q.optional}
|
||||
className={urlList.length > 0 ? 'mb-1' : ''}
|
||||
noStyle
|
||||
>
|
||||
<PopoverForm visible={visible} switchVisible={switchVisible}>
|
||||
<Button
|
||||
onClick={handleShowPopover(idx)}
|
||||
className="absolute left-1/2 top-0"
|
||||
icon={<Link className="size-3" />}
|
||||
>
|
||||
{t('flow.pasteFileLink')}
|
||||
</Button>
|
||||
</PopoverForm>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item name={idx} noStyle {...pick(props, ['rules'])} />
|
||||
</React.Fragment>
|
||||
),
|
||||
[BeginQueryType.Integer]: (
|
||||
<Form.Item {...props}>
|
||||
<InputNumber></InputNumber>
|
||||
</Form.Item>
|
||||
),
|
||||
[BeginQueryType.Boolean]: (
|
||||
<Form.Item valuePropName={'checked'} {...props}>
|
||||
<Switch></Switch>
|
||||
</Form.Item>
|
||||
),
|
||||
};
|
||||
|
||||
return BeginQueryTypeMap[q.type as BeginQueryType];
|
||||
},
|
||||
[form, handleShowPopover, onChange, switchVisible, t, visible],
|
||||
);
|
||||
|
||||
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatModal!);
|
||||
|
||||
const handleRunAgent = useCallback(
|
||||
(nextValues: Record<string, any>) => {
|
||||
const currentNodes = updateNodeForm('begin', nextValues, ['query']);
|
||||
@ -183,25 +35,12 @@ const RunDrawer = ({
|
||||
[handleRun, hideModal, updateNodeForm],
|
||||
);
|
||||
|
||||
const onOk = useCallback(async () => {
|
||||
const values = await form.validateFields();
|
||||
const nextValues = Object.entries(values).map(([key, value]) => {
|
||||
const item = query[Number(key)];
|
||||
let nextValue = value;
|
||||
if (Array.isArray(value)) {
|
||||
nextValue = ``;
|
||||
|
||||
value.forEach((x) => {
|
||||
nextValue +=
|
||||
x?.originFileObj instanceof File
|
||||
? `${x.name}\n${x.response?.data}\n----\n`
|
||||
: `${x.url}\n${x.result}\n----\n`;
|
||||
});
|
||||
}
|
||||
return { ...item, value: nextValue };
|
||||
});
|
||||
handleRunAgent(nextValues);
|
||||
}, [form, handleRunAgent, query]);
|
||||
const onOk = useCallback(
|
||||
async (nextValues: any[]) => {
|
||||
handleRunAgent(nextValues);
|
||||
},
|
||||
[handleRunAgent],
|
||||
);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
@ -213,39 +52,11 @@ const RunDrawer = ({
|
||||
width={getDrawerWidth()}
|
||||
mask={false}
|
||||
>
|
||||
<section className={styles.formWrapper}>
|
||||
<Form.Provider
|
||||
onFormFinish={(name, { values, forms }) => {
|
||||
if (name === 'urlForm') {
|
||||
const { basicForm } = forms;
|
||||
const urlInfo = basicForm.getFieldValue(currentRecord) || [];
|
||||
basicForm.setFieldsValue({
|
||||
[currentRecord]: [...urlInfo, { ...values, name: values.url }],
|
||||
});
|
||||
hidePopover();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form
|
||||
name="basicForm"
|
||||
autoComplete="off"
|
||||
layout={'vertical'}
|
||||
form={form}
|
||||
>
|
||||
{query.map((x, idx) => {
|
||||
return renderWidget(x, idx);
|
||||
})}
|
||||
</Form>
|
||||
</Form.Provider>
|
||||
</section>
|
||||
<Button
|
||||
type={'primary'}
|
||||
block
|
||||
onClick={onOk}
|
||||
disabled={!submittable || isUploading}
|
||||
>
|
||||
{t('common.next')}
|
||||
</Button>
|
||||
<DebugContent
|
||||
ok={onOk}
|
||||
parameters={query}
|
||||
loading={loading}
|
||||
></DebugContent>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
@ -7,7 +7,12 @@ import pipe from 'lodash/fp/pipe';
|
||||
import isObject from 'lodash/isObject';
|
||||
import { Edge, Node, Position } from 'reactflow';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { CategorizeAnchorPointPositions, NodeMap, Operator } from './constant';
|
||||
import {
|
||||
CategorizeAnchorPointPositions,
|
||||
NoDebugOperatorsList,
|
||||
NodeMap,
|
||||
Operator,
|
||||
} from './constant';
|
||||
import { ICategorizeItemResult, IPosition, NodeData } from './interface';
|
||||
|
||||
const buildEdges = (
|
||||
@ -124,7 +129,7 @@ export const buildDslComponentsByGraph = (
|
||||
const components: DSLComponents = {};
|
||||
|
||||
nodes
|
||||
.filter((x) => x.data.label !== Operator.Note)
|
||||
?.filter((x) => x.data.label !== Operator.Note)
|
||||
.forEach((x) => {
|
||||
const id = x.id;
|
||||
const operatorName = x.data.label;
|
||||
@ -323,3 +328,7 @@ export const duplicateNodeForm = (nodeData?: NodeData) => {
|
||||
export const getDrawerWidth = () => {
|
||||
return window.innerWidth > 1278 ? '40%' : 470;
|
||||
};
|
||||
|
||||
export const needsSingleStepDebugging = (label: string) => {
|
||||
return !NoDebugOperatorsList.some((x) => (label as Operator) === x);
|
||||
};
|
||||
|
@ -168,7 +168,7 @@ const Login = () => {
|
||||
onClick={toGoogle}
|
||||
style={{ marginTop: 15 }}
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
<Icon
|
||||
icon="local:github"
|
||||
style={{ verticalAlign: 'middle', marginRight: 5 }}
|
||||
|
@ -11,6 +11,8 @@ const {
|
||||
runCanvas,
|
||||
listTemplates,
|
||||
testDbConnect,
|
||||
getInputElements,
|
||||
debug,
|
||||
} = api;
|
||||
|
||||
const methods = {
|
||||
@ -46,6 +48,14 @@ const methods = {
|
||||
url: testDbConnect,
|
||||
method: 'post',
|
||||
},
|
||||
getInputElements: {
|
||||
url: getInputElements,
|
||||
method: 'get',
|
||||
},
|
||||
debugSingle: {
|
||||
url: debug,
|
||||
method: 'post',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const chatService = registerServer<keyof typeof methods>(methods, request);
|
||||
|
@ -118,4 +118,6 @@ export default {
|
||||
resetCanvas: `${api_host}/canvas/reset`,
|
||||
runCanvas: `${api_host}/canvas/completion`,
|
||||
testDbConnect: `${api_host}/canvas/test_db_connect`,
|
||||
getInputElements: `${api_host}/canvas/input_elements`,
|
||||
debug: `${api_host}/canvas/debug`,
|
||||
};
|
||||
|
@ -107,31 +107,25 @@ request.interceptors.response.use(async (response: any, options) => {
|
||||
return response;
|
||||
}
|
||||
|
||||
const data: ResponseType = await response.clone().json();
|
||||
|
||||
if (data.code === 401 || data.code === 401) {
|
||||
const data: ResponseType = await response?.clone()?.json();
|
||||
if (data?.code === 100) {
|
||||
message.error(data?.message);
|
||||
} else if (data?.code === 401) {
|
||||
notification.error({
|
||||
message: data.message,
|
||||
description: data.message,
|
||||
message: data?.message,
|
||||
description: data?.message,
|
||||
duration: 3,
|
||||
});
|
||||
authorizationUtil.removeAll();
|
||||
history.push('/login'); // Will not jump to the login page
|
||||
} else if (data.code !== 0) {
|
||||
if (data.code === 100) {
|
||||
message.error(data.message);
|
||||
} else {
|
||||
notification.error({
|
||||
message: `${i18n.t('message.hint')} : ${data.code}`,
|
||||
description: data.message,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
} else {
|
||||
return response;
|
||||
} else if (data?.code !== 0) {
|
||||
notification.error({
|
||||
message: `${i18n.t('message.hint')} : ${data?.code}`,
|
||||
description: data?.message,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
return response;
|
||||
});
|
||||
|
||||
export default request;
|
||||
|
Loading…
x
Reference in New Issue
Block a user