From 75ed802a7fd514a0a2dd926bda7400b99fef199a Mon Sep 17 00:00:00 2001 From: zhaowei Date: Fri, 25 Apr 2025 08:26:52 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=9C=BA=E5=99=A8=E5=AD=A6?= =?UTF-8?q?=E4=B9=A0=E6=B7=BB=E5=8A=A0=E6=96=87=E6=9C=AC=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/.eslintrc.js | 8 +- react-ui/.prettierignore | 1 + .../babel-plugin-auto-css-modules.js | 6 +- react-ui/.storybook/mock/websocket.mock.js | 10 +- react-ui/README.md | 2 +- .../src/components/CopyingText/clipboard.js | 4 +- react-ui/src/enums/index.ts | 14 + .../src/pages/ActiveLearn/Create/index.tsx | 33 ++- react-ui/src/pages/ActiveLearn/Info/index.tsx | 17 +- .../components/ActiveLearnBasic/index.tsx | 2 +- .../components/CreateForm/utils.ts | 6 +- react-ui/src/pages/ActiveLearn/types.ts | 11 +- react-ui/src/pages/AutoML/Create/index.tsx | 42 ++- react-ui/src/pages/AutoML/Info/index.tsx | 10 +- .../AutoML/components/AutoMLBasic/index.tsx | 246 ++++++++++-------- .../components/CreateForm/BasicConfig.tsx | 16 +- .../CreateForm/TextExecuteConfig.tsx | 137 ++++++++++ .../components/ExperimentHistory/index.tsx | 2 +- .../components/ExperimentList/config.ts | 4 +- .../components/ExperimentList/index.tsx | 17 +- react-ui/src/pages/AutoML/types.ts | 11 +- .../components/ExperimentHistory/index.less | 2 +- .../components/HyperParameterBasic/index.tsx | 2 +- .../components/TrialFileTree/index.less | 19 +- .../Model/components/ModelMetrics/index.tsx | 2 +- .../src/pages/ModelDeployment/List/index.tsx | 8 +- .../components/VersionCompareModal/index.tsx | 2 +- react-ui/src/pages/System/User/edit.tsx | 2 +- react-ui/src/services/activeLearn/index.js | 4 +- react-ui/src/services/auth/index.js | 2 +- react-ui/src/services/autoML/index.js | 26 +- react-ui/src/services/codeConfig/index.js | 4 +- react-ui/src/services/dataset/index.js | 14 +- react-ui/src/services/experiment/index.js | 6 +- react-ui/src/services/file/index.js | 5 +- react-ui/src/services/hyperParameter/index.js | 5 +- react-ui/src/services/pipeline/index.js | 4 +- react-ui/src/utils/IconUtil.ts | 1 - react-ui/src/utils/downloadfile.ts | 14 +- react-ui/src/utils/format.ts | 13 +- react-ui/src/utils/table.tsx | 17 +- 41 files changed, 506 insertions(+), 245 deletions(-) create mode 100644 react-ui/src/pages/AutoML/components/CreateForm/TextExecuteConfig.tsx diff --git a/react-ui/.eslintrc.js b/react-ui/.eslintrc.js index 85537dd8..4d55233b 100644 --- a/react-ui/.eslintrc.js +++ b/react-ui/.eslintrc.js @@ -1,8 +1,8 @@ module.exports = { extends: [ - require.resolve('@umijs/lint/dist/config/eslint'), - 'plugin:react/recommended', - "plugin:react-hooks/recommended" + require.resolve('@umijs/lint/dist/config/eslint'), + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', ], globals: { page: true, @@ -11,6 +11,6 @@ module.exports = { rules: { '@typescript-eslint/no-use-before-define': 'off', 'react/react-in-jsx-scope': 'off', - 'react/display-name': 'off' + 'react/display-name': 'off', }, }; diff --git a/react-ui/.prettierignore b/react-ui/.prettierignore index 7999ccda..e726b5a2 100644 --- a/react-ui/.prettierignore +++ b/react-ui/.prettierignore @@ -20,3 +20,4 @@ yarn-error.log CNAME /build /public +/src/iconfont/ diff --git a/react-ui/.storybook/babel-plugin-auto-css-modules.js b/react-ui/.storybook/babel-plugin-auto-css-modules.js index 660744b4..d24babf5 100644 --- a/react-ui/.storybook/babel-plugin-auto-css-modules.js +++ b/react-ui/.storybook/babel-plugin-auto-css-modules.js @@ -1,4 +1,4 @@ -export default function(babel) { +export default function (babel) { const { types: t } = babel; return { visitor: { @@ -6,10 +6,10 @@ export default function(babel) { const source = path.node.source.value; if (source.endsWith('.less')) { if (path.node.specifiers.length > 0) { - path.node.source.value += "?modules"; + path.node.source.value += '?modules'; } } }, }, }; -}; \ No newline at end of file +} diff --git a/react-ui/.storybook/mock/websocket.mock.js b/react-ui/.storybook/mock/websocket.mock.js index 3661b99b..4825c2c4 100644 --- a/react-ui/.storybook/mock/websocket.mock.js +++ b/react-ui/.storybook/mock/websocket.mock.js @@ -6,7 +6,7 @@ export const createWebSocketMock = () => { this.listeners = {}; this.count = 0; - console.log("Mock WebSocket connected to:", url); + console.log('Mock WebSocket connected to:', url); // 模拟服务器推送消息 this.intervalId = setInterval(() => { @@ -21,8 +21,8 @@ export const createWebSocketMock = () => { } sendMessage(data) { - if (this.listeners["message"]) { - this.listeners["message"].forEach((callback) => callback({ data })); + if (this.listeners['message']) { + this.listeners['message'].forEach((callback) => callback({ data })); } } @@ -41,7 +41,7 @@ export const createWebSocketMock = () => { close() { this.readyState = WebSocket.CLOSED; - console.log("Mock WebSocket closed"); + console.log('Mock WebSocket closed'); } } @@ -89,4 +89,4 @@ export const logStreamData = { ], }, ], -}; \ No newline at end of file +}; diff --git a/react-ui/README.md b/react-ui/README.md index 7d8b3af5..25f8d456 100644 --- a/react-ui/README.md +++ b/react-ui/README.md @@ -1 +1 @@ -# Documentation \ No newline at end of file +# Documentation diff --git a/react-ui/src/components/CopyingText/clipboard.js b/react-ui/src/components/CopyingText/clipboard.js index 177fcbce..1a27f01d 100644 --- a/react-ui/src/components/CopyingText/clipboard.js +++ b/react-ui/src/components/CopyingText/clipboard.js @@ -1,5 +1,5 @@ +import { message } from 'antd'; import ClipboardJS from 'clipboard'; -import { message } from "antd"; const clipboard = new ClipboardJS('#copying'); @@ -9,4 +9,4 @@ clipboard.on('success', () => { clipboard.on('error', () => { message.error('复制失败'); -}); \ No newline at end of file +}); diff --git a/react-ui/src/enums/index.ts b/react-ui/src/enums/index.ts index afba79b8..b043a2d3 100644 --- a/react-ui/src/enums/index.ts +++ b/react-ui/src/enums/index.ts @@ -86,6 +86,19 @@ export const serviceTypeOptions = [ { label: '文本', value: ServiceType.Text }, ]; +// 自动化类型 +export enum AutoMLType { + Table = 'auto_ml', + Text = 'text_classification', + Video = 'video_classification', +} + +export const autoMLTypeOptions = [ + { label: '表格', value: AutoMLType.Table }, + { label: '文本分类', value: AutoMLType.Text }, + { label: '视频分类', value: AutoMLType.Video }, +]; + // 自动化任务类型 export enum AutoMLTaskType { Classification = 'classification', @@ -148,4 +161,5 @@ export enum AutoMLTrailStatus { CRASHED = 'CRASHED', // 崩溃 STOP = 'STOP', // 停止 CANCELLED = 'CANCELLED', // 取消 + MEMOUT = 'MEMOUT', // 内存溢出 } diff --git a/react-ui/src/pages/ActiveLearn/Create/index.tsx b/react-ui/src/pages/ActiveLearn/Create/index.tsx index 8ad3f3b1..5b8fe0f8 100644 --- a/react-ui/src/pages/ActiveLearn/Create/index.tsx +++ b/react-ui/src/pages/ActiveLearn/Create/index.tsx @@ -30,27 +30,26 @@ function CreateActiveLearn() { const isCopy = pathname.includes('copy'); useEffect(() => { + // 获取服务详情 + const getActiveLearnInfo = async (id: number) => { + const [res] = await to(getActiveLearnInfoReq({ id })); + if (res && res.data) { + const info: ActiveLearnData = res.data; + const { name: name_str, ...rest } = info; + const name = isCopy ? `${name_str}-copy` : name_str; + const formData = { + ...rest, + name, + }; + + form.setFieldsValue(formData); + } + }; // 编辑,复制 if (id && !Number.isNaN(id)) { getActiveLearnInfo(id); } - }, [id]); - - // 获取服务详情 - const getActiveLearnInfo = async (id: number) => { - const [res] = await to(getActiveLearnInfoReq({ id })); - if (res && res.data) { - const info: ActiveLearnData = res.data; - const { name: name_str, ...rest } = info; - const name = isCopy ? `${name_str}-copy` : name_str; - const formData = { - ...rest, - name, - }; - - form.setFieldsValue(formData); - } - }; + }, [id, isCopy, form]); // 创建、更新、复制实验 const createExperiment = async (formData: FormData) => { diff --git a/react-ui/src/pages/ActiveLearn/Info/index.tsx b/react-ui/src/pages/ActiveLearn/Info/index.tsx index a5cc1bec..0ba305bb 100644 --- a/react-ui/src/pages/ActiveLearn/Info/index.tsx +++ b/react-ui/src/pages/ActiveLearn/Info/index.tsx @@ -19,18 +19,17 @@ function ActiveLearnInfo() { const [info, setInfo] = useState(undefined); useEffect(() => { + // 获取详情 + const getActiveLearnInfo = async () => { + const [res] = await to(getActiveLearnInfoReq({ id: id })); + if (res && res.data) { + setInfo(res.data); + } + }; if (id) { getActiveLearnInfo(); } - }, []); - - // 获取详情 - const getActiveLearnInfo = async () => { - const [res] = await to(getActiveLearnInfoReq({ id: id })); - if (res && res.data) { - setInfo(res.data); - } - }; + }, [id]); return (
diff --git a/react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx b/react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx index e8e597e9..892e6725 100644 --- a/react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx +++ b/react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx @@ -245,7 +245,7 @@ function BasicInfo({ info, className, runStatus, isInstance = false }: BasicInfo ellipsis: true, }, ]; - }, [runStatus]); + }, [runStatus, info]); return (
diff --git a/react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts b/react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts index 0090f5df..8bf7bce4 100644 --- a/react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts +++ b/react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts @@ -39,7 +39,7 @@ export const regressorAlgorithms = [ { label: 'gaussian_process(高斯回归)', value: 'gaussian_process', - } + }, ]; // 框架类型 @@ -65,7 +65,6 @@ export const frameworkTypeOptions = [ }, ]; - // 查询策略 export const queryStrategies = [ { @@ -87,6 +86,5 @@ export const queryStrategies = [ { label: 'upper_confidence_bound', value: 'upper_confidence_bound', - } + }, ]; - diff --git a/react-ui/src/pages/ActiveLearn/types.ts b/react-ui/src/pages/ActiveLearn/types.ts index 68ca88f2..be5cf89c 100644 --- a/react-ui/src/pages/ActiveLearn/types.ts +++ b/react-ui/src/pages/ActiveLearn/types.ts @@ -1,7 +1,7 @@ import { type ParameterInputObject } from '@/components/ResourceSelect'; -import { type NodeStatus } from '@/types'; import { AutoMLTaskType } from '@/enums'; import { HyperParameterFile } from '@/pages/HyperParameter/types'; +import { type NodeStatus } from '@/types'; // 操作类型 export enum OperationType { @@ -39,7 +39,6 @@ export type FormData = { batch_size: number; // batch_size epochs: number; // epochs lr: number; // 学习率 - }; // 主动学习 @@ -75,8 +74,8 @@ export type ActiveLearnInstanceData = { update_time: string; finish_time: string; nodeStatus?: NodeStatus; - data: string, - trial_list?: ActiveLearnTrial[] + data: string; + trial_list?: ActiveLearnTrial[]; }; export type ActiveLearnTrial = { @@ -85,8 +84,8 @@ export type ActiveLearnTrial = { data_num?: number; accuracy?: number; mse?: number; - query_idx?: string + query_idx?: string; file?: HyperParameterFile; }; -export {type HyperParameterFile} \ No newline at end of file +export { type HyperParameterFile }; diff --git a/react-ui/src/pages/AutoML/Create/index.tsx b/react-ui/src/pages/AutoML/Create/index.tsx index b5ceae7c..2ce59490 100644 --- a/react-ui/src/pages/AutoML/Create/index.tsx +++ b/react-ui/src/pages/AutoML/Create/index.tsx @@ -4,17 +4,19 @@ * @Description: 创建实验 */ import PageTitle from '@/components/PageTitle'; -import { AutoMLEnsembleClass, AutoMLTaskType } from '@/enums'; +import { AutoMLEnsembleClass, AutoMLTaskType, AutoMLType } from '@/enums'; import { addAutoMLReq, getAutoMLInfoReq, updateAutoMLReq } from '@/services/autoML'; import { convertEmptyStringToUndefined, parseJsonText, trimCharacter } from '@/utils'; import { safeInvoke } from '@/utils/functional'; import { to } from '@/utils/promise'; import { useLocation, useNavigate, useParams } from '@umijs/max'; import { App, Button, Form } from 'antd'; +import { omit } from 'lodash'; import { useEffect } from 'react'; import BasicConfig from '../components/CreateForm/BasicConfig'; import DatasetConfig from '../components/CreateForm/DatasetConfig'; import ExecuteConfig from '../components/CreateForm/ExecuteConfig'; +import TextExecuteConfig from '../components/CreateForm/TextExecuteConfig'; import TrialConfig from '../components/CreateForm/TrialConfig'; import { AutoMLData, FormData } from '../types'; import styles from './index.less'; @@ -27,6 +29,7 @@ function CreateAutoML() { const id = safeInvoke(Number)(params.id); const { pathname } = useLocation(); const isCopy = pathname.includes('copy'); + const type = Form.useWatch('type', form); useEffect(() => { // 获取服务详情 @@ -34,6 +37,8 @@ function CreateAutoML() { const [res] = await to(getAutoMLInfoReq({ id })); if (res && res.data) { const autoMLInfo: AutoMLData = res.data; + const { name: name_str, description, type, param } = autoMLInfo; + const paramObj = parseJsonText(param); const { include_classifier: include_classifier_str, include_feature_preprocessor: include_feature_preprocessor_str, @@ -42,9 +47,8 @@ function CreateAutoML() { exclude_feature_preprocessor: exclude_feature_preprocessor_str, exclude_regressor: exclude_regressor_str, metrics: metrics_str, - ml_name: ml_name_str, ...rest - } = autoMLInfo; + } = paramObj; const include_classifier = include_classifier_str?.split(',').filter(Boolean); const include_feature_preprocessor = include_feature_preprocessor_str ?.split(',') @@ -60,7 +64,7 @@ function CreateAutoML() { name: key, value, })); - const ml_name = isCopy ? `${ml_name_str}-copy` : ml_name_str; + const name = isCopy ? `${name_str}-copy` : name_str; const formData = { ...rest, @@ -71,7 +75,9 @@ function CreateAutoML() { exclude_feature_preprocessor, exclude_regressor, metrics, - ml_name, + name, + description, + type, }; form.setFieldsValue(formData); @@ -86,6 +92,7 @@ function CreateAutoML() { // 创建、更新、复制实验 const createExperiment = async (formData: FormData) => { + const { name, description, type } = formData; const include_classifier = formData['include_classifier']?.join(','); const include_feature_preprocessor = formData['include_feature_preprocessor']?.join(','); const include_regressor = formData['include_regressor']?.join(','); @@ -104,8 +111,8 @@ function CreateAutoML() { const target_columns = trimCharacter(formData['target_columns'], ','); // 根据后台要求,修改表单数据 - const object = { - ...formData, + const param = { + ...omit(formData, ['name', 'description', 'type']), include_classifier: convertEmptyStringToUndefined(include_classifier), include_feature_preprocessor: convertEmptyStringToUndefined(include_feature_preprocessor), include_regressor: convertEmptyStringToUndefined(include_regressor), @@ -116,6 +123,13 @@ function CreateAutoML() { target_columns, }; + const object = { + name, + description, + type, + param: JSON.stringify(param), + }; + const params = id && !isCopy ? { @@ -169,6 +183,7 @@ function CreateAutoML() { autoComplete="off" scrollToFirstError initialValues={{ + type: AutoMLType.Table, task_type: AutoMLTaskType.Classification, shuffle: false, ensemble_class: AutoMLEnsembleClass.Default, @@ -186,9 +201,16 @@ function CreateAutoML() { }} > - - - + + {type === AutoMLType.Table ? ( + <> + + + + + ) : type === AutoMLType.Text ? ( + + ) : null}