From cab811d246b0d545cb7fe0557516b73078cee01b Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Sat, 30 Nov 2024 15:43:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E5=AD=A6=E4=B9=A0=E5=AE=9E=E4=BE=8B=E8=AF=A6?= =?UTF-8?q?=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/config/routes.ts | 5 + react-ui/src/pages/AutoML/Info/index.tsx | 39 +- react-ui/src/pages/AutoML/Instance/index.less | 17 + react-ui/src/pages/AutoML/Instance/index.tsx | 118 +++++ react-ui/src/pages/AutoML/List/index.tsx | 12 +- .../AutoML/components/AutoMLBasic/index.tsx | 449 ++++++++---------- .../AutoML/components/ConfigInfo/index.less | 5 - .../components/ExperimentHistory/index.less | 14 + .../components/ExperimentHistory/index.tsx | 132 +++++ .../components/ExperimentLog/index.less | 0 .../AutoML/components/ExperimentLog/index.tsx | 0 .../components/ExperimentResult/index.less | 18 + .../components/ExperimentResult/index.tsx | 51 ++ react-ui/src/pages/AutoML/types.ts | 20 +- .../Experiment/components/LogGroup/index.tsx | 2 +- react-ui/src/pages/Experiment/index.jsx | 1 + react-ui/src/requestConfig.ts | 3 +- .../services/{autoML.js => autoML/index.js} | 0 react-ui/src/services/file/index.js | 20 + 19 files changed, 628 insertions(+), 278 deletions(-) create mode 100644 react-ui/src/pages/AutoML/Instance/index.less create mode 100644 react-ui/src/pages/AutoML/Instance/index.tsx create mode 100644 react-ui/src/pages/AutoML/components/ExperimentHistory/index.less create mode 100644 react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx create mode 100644 react-ui/src/pages/AutoML/components/ExperimentLog/index.less create mode 100644 react-ui/src/pages/AutoML/components/ExperimentLog/index.tsx create mode 100644 react-ui/src/pages/AutoML/components/ExperimentResult/index.less create mode 100644 react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx rename react-ui/src/services/{autoML.js => autoML/index.js} (100%) create mode 100644 react-ui/src/services/file/index.js diff --git a/react-ui/config/routes.ts b/react-ui/config/routes.ts index c58b182e..cf4e1aaf 100644 --- a/react-ui/config/routes.ts +++ b/react-ui/config/routes.ts @@ -169,6 +169,11 @@ export default [ path: 'edit/:id', component: './AutoML/Create/index', }, + { + name: '实验实例', + path: 'instance/:autoMLId/:id', + component: './AutoML/Instance/index', + }, ], }, ], diff --git a/react-ui/src/pages/AutoML/Info/index.tsx b/react-ui/src/pages/AutoML/Info/index.tsx index c167af97..caa2cc55 100644 --- a/react-ui/src/pages/AutoML/Info/index.tsx +++ b/react-ui/src/pages/AutoML/Info/index.tsx @@ -5,26 +5,23 @@ */ import KFIcon from '@/components/KFIcon'; import { CommonTabKeys } from '@/enums'; -import { useCacheState } from '@/hooks/pageCacheState'; +import { getAutoMLInfoReq } from '@/services/autoML'; import themes from '@/styles/theme.less'; -import { useNavigate } from '@umijs/max'; +import { safeInvoke } from '@/utils/functional'; +import { to } from '@/utils/promise'; +import { useParams } from '@umijs/max'; import { Tabs } from 'antd'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import AutoMLBasic from '../components/AutoMLBasic'; import AutoMLTable from '../components/AutoMLTable'; +import { AutoMLData } from '../types'; import styles from './index.less'; -export type MirrorData = { - id: number; - name: string; - description: string; - create_time: string; -}; - function AutoMLInfo() { - const navigate = useNavigate(); - const [cacheState, setCacheState] = useCacheState(); - const [activeTab, setActiveTab] = useState(cacheState?.activeTab ?? CommonTabKeys.Public); + const [activeTab, setActiveTab] = useState(CommonTabKeys.Public); + const params = useParams(); + const autoMLId = safeInvoke(Number)(params.id); + const [autoMLInfo, setAutoMLInfo] = useState(undefined); const tabItems = [ { @@ -39,13 +36,27 @@ function AutoMLInfo() { }, ]; + useEffect(() => { + if (autoMLId) { + getAutoMLInfo(); + } + }, []); + + // 获取自动机器学习详情 + const getAutoMLInfo = async () => { + const [res] = await to(getAutoMLInfoReq({ id: autoMLId })); + if (res && res.data) { + setAutoMLInfo(res.data); + } + }; + return (
- {activeTab === CommonTabKeys.Public && } + {activeTab === CommonTabKeys.Public && } {activeTab === CommonTabKeys.Private && }
{activeTab === CommonTabKeys.Private && ( diff --git a/react-ui/src/pages/AutoML/Instance/index.less b/react-ui/src/pages/AutoML/Instance/index.less new file mode 100644 index 00000000..0c62da46 --- /dev/null +++ b/react-ui/src/pages/AutoML/Instance/index.less @@ -0,0 +1,17 @@ +.auto-ml-instance { + height: 100%; + + &__tabs { + height: 50px; + padding-left: 25px; + background-image: url(@/assets/img/page-title-bg.png); + background-repeat: no-repeat; + background-position: top center; + background-size: 100% 100%; + } + + &__content { + height: calc(100% - 60px); + margin-top: 10px; + } +} diff --git a/react-ui/src/pages/AutoML/Instance/index.tsx b/react-ui/src/pages/AutoML/Instance/index.tsx new file mode 100644 index 00000000..a8707c34 --- /dev/null +++ b/react-ui/src/pages/AutoML/Instance/index.tsx @@ -0,0 +1,118 @@ +import KFIcon from '@/components/KFIcon'; +import { AutoMLTaskType, ExperimentStatus } from '@/enums'; +import LogList from '@/pages/Experiment/components/LogList'; +import { getExperimentInsReq } from '@/services/autoML'; +import { parseJsonText } from '@/utils'; +import { safeInvoke } from '@/utils/functional'; +import { to } from '@/utils/promise'; +import { useParams } from '@umijs/max'; +import { Tabs } from 'antd'; +import { useEffect, useState } from 'react'; +import AutoMLBasic from '../components/AutoMLBasic'; +import ExperimentHistory from '../components/ExperimentHistory'; +import ExperimentResult from '../components/ExperimentResult'; +import { AutoMLData, AutoMLInstanceData } from '../types'; +import styles from './index.less'; + +enum TabKeys { + Params = 'params', + Log = 'log', + Result = 'result', + History = 'history', +} + +function AutoMLInstance() { + const [activeTab, setActiveTab] = useState(TabKeys.Params); + const [autoMLInfo, setAutoMLInfo] = useState(undefined); + const [instanceInfo, setInstanceInfo] = useState(undefined); + const params = useParams(); + // const autoMLId = safeInvoke(Number)(params.autoMLId); + const instanceId = safeInvoke(Number)(params.id); + + useEffect(() => { + if (instanceId) { + getExperimentInsInfo(); + } + }, []); + + // 获取实验实例详情 + const getExperimentInsInfo = async () => { + const [res] = await to(getExperimentInsReq(instanceId)); + if (res && res.data) { + const info = res.data as AutoMLInstanceData; + const { param, node_status } = info; + // 解析配置参数 + const paramJson = parseJsonText(param); + if (paramJson) { + setAutoMLInfo(paramJson); + } + // 进行节点状态 + const nodeStatusJson = parseJsonText(node_status); + if (nodeStatusJson) { + Object.keys(nodeStatusJson).forEach((key) => { + if (key.startsWith('auto-ml')) { + const value = nodeStatusJson[key]; + value.nodeId = key; + info.nodeStatus = value; + } + }); + } + setInstanceInfo(info); + } + }; + + const tabItems = [ + { + key: TabKeys.Params, + label: '参数信息', + icon: , + }, + { + key: TabKeys.Log, + label: '日志', + icon: , + }, + { + key: TabKeys.Result, + label: '实验结果', + icon: , + }, + { + key: TabKeys.History, + label: '运行历史', + icon: , + }, + ]; + + return ( +
+
+ +
+
+ {activeTab === TabKeys.Params && } + {activeTab === TabKeys.Log && instanceInfo && instanceInfo.nodeStatus && ( + + )} + {activeTab === TabKeys.Result && ( + + )} + {activeTab === TabKeys.History && ( + + )} +
+
+ ); +} + +export default AutoMLInstance; diff --git a/react-ui/src/pages/AutoML/List/index.tsx b/react-ui/src/pages/AutoML/List/index.tsx index 288001ec..aa269e50 100644 --- a/react-ui/src/pages/AutoML/List/index.tsx +++ b/react-ui/src/pages/AutoML/List/index.tsx @@ -143,8 +143,10 @@ function AutoMLList() { const startAutoML = async (record: AutoMLData) => { const [res] = await to(runAutoMLReq(record.id)); if (res) { - message.success('操作成功'); - getAutoMLList(); + message.success('运行成功'); + setExpandedRowKeys([record.id]); + refreshExperimentList(); + refreshExperimentIns(record.id); } }; @@ -183,8 +185,8 @@ function AutoMLList() { }; // 跳转到实验实例详情 - const gotoInstanceInfo = (item, record) => { - navigate({ pathname: `/pipeline/experiment/instance/${record.workflow_id}/${item.id}` }); + const gotoInstanceInfo = (autoML: AutoMLData, record: ExperimentInstanceData) => { + navigate({ pathname: `/pipeline/automl/instance/${autoML.id}/${record.id}` }); }; // 刷新实验实例列表 @@ -386,7 +388,7 @@ function AutoMLList() { gotoInstanceInfo(item, record)} + onClickInstance={(item) => gotoInstanceInfo(record, item)} onRemove={() => { refreshExperimentIns(record.id); refreshExperimentList(); diff --git a/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx b/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx index 30b646af..02a5e69b 100644 --- a/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx +++ b/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx @@ -1,19 +1,13 @@ +import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; import { AutoMLData } from '@/pages/AutoML/types'; -import { getAutoMLInfoReq } from '@/services/autoML'; -import { safeInvoke } from '@/utils/functional'; -import { to } from '@/utils/promise'; -import { useParams } from '@umijs/max'; -import { Flex } from 'antd'; -import { useEffect, useState } from 'react'; +import { parseJsonText } from '@/utils'; +import { useMemo } from 'react'; import ConfigInfo, { formatBoolean, formatDate, formatEnum, type BasicInfoData, } from '../ConfigInfo'; -// import CopyingText from '../CopyingText'; -import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; -import { parseJsonText } from '@/utils'; import styles from './index.less'; // 格式化数据集 @@ -42,263 +36,216 @@ const formatMetricsWeight = (value: string) => { .join('\n'); }; -function AutoMLBasic() { - const params = useParams(); - const id = safeInvoke(Number)(params.id); - const [basicDatas, setBasicDatas] = useState([]); - const [configDatas, setConfigDatas] = useState([]); +type AutoMLBasicProps = { + info?: AutoMLData; + hasBasicInfo?: boolean; +}; - useEffect(() => { - if (id) { - getAutoMLInfo(id); +function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { + const basicDatas: BasicInfoData[] = useMemo(() => { + if (!info) { + return []; } - }, []); - // const basicDatas: BasicInfoData[] = [ - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: '测试项目名称', - // ellipsis: true, - // }, - // { - // label: '项目名称', - // value: , - // ellipsis: false, - // }, - // ]; + return [ + { + label: '实验名称', + value: info.ml_name, + ellipsis: true, + }, + { + label: '实验描述', + value: info.ml_description, + ellipsis: true, + }, + { + label: '创建人', + value: info.create_by, + ellipsis: true, + }, + { + label: '创建时间', + value: info.create_time, + ellipsis: true, + format: formatDate, + }, + { + label: '更新时间', + value: info.update_time, + ellipsis: true, + format: formatDate, + }, + ]; + }, [info]); - // 获取服务详情 - const getAutoMLInfo = async (id: number) => { - const [res] = await to(getAutoMLInfoReq({ id })); - if (res && res.data) { - const info: AutoMLData = res.data; - const basicDatas: BasicInfoData[] = [ - { - label: '实验名称', - value: info.ml_name, - ellipsis: true, - }, - { - label: '实验描述', - value: info.ml_description, - ellipsis: true, - }, - { - label: '创建人', - value: info.create_by, - ellipsis: true, - }, - { - label: '创建时间', - value: info.create_time, - ellipsis: true, - format: formatDate, - }, - { - label: '更新时间', - value: info.update_time, - ellipsis: true, - format: formatDate, - }, - { - label: '状态', - value: info.run_state, - ellipsis: true, - }, - ]; - setBasicDatas(basicDatas); + const configDatas: BasicInfoData[] = useMemo(() => { + if (!info) { + return []; + } + return [ + { + label: '任务类型', + value: info.task_type, + ellipsis: true, + format: formatEnum(autoMLTaskTypeOptions), + }, + { + label: '特征预处理算法', + value: info.include_feature_preprocessor, + ellipsis: true, + }, + { + label: '排除的特征预处理算法', + value: info.exclude_feature_preprocessor, + ellipsis: true, + }, + { + label: info.task_type === AutoMLTaskType.Regression ? '回归算法' : '分类算法', + value: + info.task_type === AutoMLTaskType.Regression + ? info.include_regressor + : info.include_classifier, + ellipsis: true, + }, + { + label: info.task_type === AutoMLTaskType.Regression ? '排除的回归算法' : '排除的分类算法', + value: + info.task_type === AutoMLTaskType.Regression + ? info.exclude_regressor + : info.exclude_classifier, + ellipsis: true, + }, + { + label: '集成方式', + value: info.ensemble_class, + ellipsis: true, + format: formatEnum(autoMLEnsembleClassOptions), + }, + { + label: '集成模型数量', + value: info.ensemble_size, + ellipsis: true, + }, + { + label: '集成最佳模型数量', + value: info.ensemble_nbest, + ellipsis: true, + }, + { + label: '最大数量', + value: info.max_models_on_disc, + ellipsis: true, + }, + { + label: '内存限制(MB)', + value: info.memory_limit, + ellipsis: true, + }, + { + label: '时间限制(秒)', + value: info.per_run_time_limit, + ellipsis: true, + }, + { + label: '搜索时间限制(秒)', + value: info.time_left_for_this_task, + ellipsis: true, + }, + { + label: '重采样策略', + value: info.resampling_strategy, + ellipsis: true, + }, + { + label: '交叉验证折数', + value: info.folds, + ellipsis: true, + }, + { + label: '是否打乱', + value: info.shuffle, + ellipsis: true, + format: formatBoolean, + }, + { + label: '训练集比率', + value: info.train_size, + ellipsis: true, + }, + { + label: '测试集比率', + value: info.test_size, + ellipsis: true, + }, + { + label: '计算指标', + value: info.scoring_functions, + ellipsis: true, + }, + { + label: '随机种子', + value: info.seed, + ellipsis: true, + }, - const configDatas: BasicInfoData[] = [ - { - label: '任务类型', - value: info.task_type, - ellipsis: true, - format: formatEnum(autoMLTaskTypeOptions), - }, - { - label: '特征预处理算法', - value: info.include_feature_preprocessor, - ellipsis: true, - }, - { - label: '排除的特征预处理算法', - value: info.exclude_feature_preprocessor, - ellipsis: true, - }, - { - label: info.task_type === AutoMLTaskType.Regression ? '回归算法' : '分类算法', - value: - info.task_type === AutoMLTaskType.Regression - ? info.include_regressor - : info.include_classifier, - ellipsis: true, - }, - { - label: info.task_type === AutoMLTaskType.Regression ? '排除的回归算法' : '排除的分类算法', - value: - info.task_type === AutoMLTaskType.Regression - ? info.exclude_regressor - : info.exclude_classifier, - ellipsis: true, - }, - { - label: '集成方式', - value: info.ensemble_class, - ellipsis: true, - format: formatEnum(autoMLEnsembleClassOptions), - }, - { - label: '集成模型数量', - value: info.ensemble_size, - ellipsis: true, - }, - { - label: '集成最佳模型数量', - value: info.ensemble_nbest, - ellipsis: true, - }, - { - label: '最大数量', - value: info.max_models_on_disc, - ellipsis: true, - }, - { - label: '内存限制(MB)', - value: info.memory_limit, - ellipsis: true, - }, - { - label: '时间限制(秒)', - value: info.per_run_time_limit, - ellipsis: true, - }, - { - label: '搜索时间限制(秒)', - value: info.time_left_for_this_task, - ellipsis: true, - }, - { - label: '重采样策略', - value: info.resampling_strategy, - ellipsis: true, - }, - { - label: '交叉验证折数', - value: info.folds, - ellipsis: true, - }, - { - label: '是否打乱', - value: info.shuffle, - ellipsis: true, - format: formatBoolean, - }, - { - label: '训练集比率', - value: info.train_size, - ellipsis: true, - }, - { - label: '测试集比率', - value: info.test_size, - ellipsis: true, - }, - { - label: '计算指标', - value: info.scoring_functions, - ellipsis: true, - }, - { - label: '随机种子', - value: info.seed, - ellipsis: true, - }, + { + label: '数据集', + value: info.dataset, + ellipsis: true, + format: formatDataset, + }, + { + label: '预测目标列', + value: info.target_columns, + ellipsis: true, + }, + ]; + }, [info]); - { - label: '数据集', - value: info.dataset, - ellipsis: true, - format: formatDataset, - }, - { - label: '预测目标列', - value: info.target_columns, - ellipsis: true, - }, - { - label: '指标名称', - value: info.metric_name, - ellipsis: true, - }, - { - label: '优化方向', - value: info.greater_is_better, - ellipsis: true, - format: formatOptimizeMode, - }, - { - label: '指标权重', - value: info.metrics, - ellipsis: true, - format: formatMetricsWeight, - }, - ]; - setConfigDatas(configDatas); + const metricsData = useMemo(() => { + if (!info) { + return []; } - }; + return [ + { + label: '指标名称', + value: info.metric_name, + ellipsis: true, + }, + { + label: '优化方向', + value: info.greater_is_better, + ellipsis: true, + format: formatOptimizeMode, + }, + { + label: '指标权重', + value: info.metrics, + ellipsis: true, + format: formatMetricsWeight, + }, + ]; + }, [info]); return (
- - - {/* */} - + {hasBasicInfo && ( + + )} - +
); } diff --git a/react-ui/src/pages/AutoML/components/ConfigInfo/index.less b/react-ui/src/pages/AutoML/components/ConfigInfo/index.less index babbac65..78bc1a9c 100644 --- a/react-ui/src/pages/AutoML/components/ConfigInfo/index.less +++ b/react-ui/src/pages/AutoML/components/ConfigInfo/index.less @@ -12,18 +12,13 @@ :global { .kf-basic-info { - gap: 15px; width: 100%; &__item { - width: calc((100% - 15px) / 2); &__label { font-size: @font-size; text-align: left; text-align-last: left; - &::after { - display: none; - } } &__value { min-width: 0; diff --git a/react-ui/src/pages/AutoML/components/ExperimentHistory/index.less b/react-ui/src/pages/AutoML/components/ExperimentHistory/index.less new file mode 100644 index 00000000..24d00d35 --- /dev/null +++ b/react-ui/src/pages/AutoML/components/ExperimentHistory/index.less @@ -0,0 +1,14 @@ +.experiment-history { + height: 100%; + &__content { + height: 100%; + margin-top: 10px; + padding: 20px @content-padding 0; + background-color: white; + border-radius: 10px; + + &__table { + height: 100%; + } + } +} diff --git a/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx new file mode 100644 index 00000000..e95ccd42 --- /dev/null +++ b/react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx @@ -0,0 +1,132 @@ +import { getFileReq } from '@/services/file'; +import { to } from '@/utils/promise'; +import tableCellRender from '@/utils/table'; +import { Table, type TableProps } from 'antd'; +import classNames from 'classnames'; +import { useEffect, useState } from 'react'; +import styles from './index.less'; + +type ExperimentHistoryProps = { + fileUrl?: string; + isClassification: boolean; +}; + +type TableData = { + id?: string; + accuracy?: number; + duration?: number; + train_loss?: number; + status?: string; + feature?: string; + althorithm?: string; +}; + +function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps) { + const [tableData, setTableData] = useState([]); + useEffect(() => { + if (fileUrl) { + getHistoryFile(); + } + }, [fileUrl]); + + // 获取实验运行历史记录 + const getHistoryFile = async () => { + const [res] = await to(getFileReq(fileUrl)); + if (res) { + const data: any[] = res.data; + const list: TableData[] = data.map((item) => { + return { + id: item[0]?.[0], + accuracy: item[1]?.[5]?.accuracy, + duration: item[1]?.[5]?.duration, + train_loss: item[1]?.[5]?.train_loss, + status: item[1]?.[2]?.['__enum__']?.split('.')?.[1], + }; + }); + list.forEach((item) => { + if (!item.id) return; + const config = (res as any).configs?.[item.id]; + item.feature = config?.['feature_preprocessor:__choice__']; + item.althorithm = isClassification + ? config?.['classifier:__choice__'] + : config?.['regressor:__choice__']; + }); + setTableData(list); + } + }; + + const columns: TableProps['columns'] = [ + { + title: 'ID', + dataIndex: 'id', + key: 'id', + width: 80, + render: tableCellRender(false), + }, + { + title: '准确率', + dataIndex: 'accuracy', + key: 'accuracy', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '耗时', + dataIndex: 'duration', + key: 'duration', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '训练损失', + dataIndex: 'train_loss', + key: 'train_loss', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '特征处理', + dataIndex: 'feature', + key: 'feature', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '算法', + dataIndex: 'althorithm', + key: 'althorithm', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 120, + render: tableCellRender(false), + }, + ]; + + return ( +
+
+
+ + + + + ); +} + +export default ExperimentHistory; diff --git a/react-ui/src/pages/AutoML/components/ExperimentLog/index.less b/react-ui/src/pages/AutoML/components/ExperimentLog/index.less new file mode 100644 index 00000000..e69de29b diff --git a/react-ui/src/pages/AutoML/components/ExperimentLog/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentLog/index.tsx new file mode 100644 index 00000000..e69de29b diff --git a/react-ui/src/pages/AutoML/components/ExperimentResult/index.less b/react-ui/src/pages/AutoML/components/ExperimentResult/index.less new file mode 100644 index 00000000..bdf1858c --- /dev/null +++ b/react-ui/src/pages/AutoML/components/ExperimentResult/index.less @@ -0,0 +1,18 @@ +.experiment-result { + height: 100%; + margin-top: 10px; + padding: 20px @content-padding 0; + overflow-y: auto; + background-color: white; + border-radius: 10px; + + &__text { + margin-bottom: 20px; + white-space: pre-wrap; + } + + &__image { + display: block; + height: 200px; + } +} diff --git a/react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx new file mode 100644 index 00000000..2e0b71a6 --- /dev/null +++ b/react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx @@ -0,0 +1,51 @@ +import { getFileReq } from '@/services/file'; +import { to } from '@/utils/promise'; +import { useEffect, useMemo, useState } from 'react'; +import styles from './index.less'; + +type ExperimentResultProps = { + fileUrl?: string; + imageUrl?: string; +}; + +function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) { + const [result, setResult] = useState(''); + + const images = useMemo(() => { + if (imageUrl) { + return imageUrl.split(',').map((item) => item.trim()); + } + return []; + }, [imageUrl]); + + useEffect(() => { + if (fileUrl) { + getResultFile(); + } + }, [fileUrl]); + + // 获取实验运行历史记录 + const getResultFile = async () => { + const [res] = await to(getFileReq(fileUrl)); + if (res) { + setResult(res as any as string); + } + }; + + return ( +
+
{result}
+ {images.map((item, index) => ( + + ))} +
+ ); +} + +export default ExperimentResult; diff --git a/react-ui/src/pages/AutoML/types.ts b/react-ui/src/pages/AutoML/types.ts index 3464e599..f068d168 100644 --- a/react-ui/src/pages/AutoML/types.ts +++ b/react-ui/src/pages/AutoML/types.ts @@ -61,6 +61,24 @@ export type AutoMLData = { 'metrics|dataset|include_classifier|include_feature_preprocessor|include_regressor|exclude_classifier|exclude_feature_preprocessor|exclude_regressor' >; -export type ExperimentInstanceData = { +// 自动机器学习实验实例 +export type AutoMLInstanceData = { id: number; + auto_ml_id: number; + result_path: string; + model_path: string; + img_path: string; + run_history_path: string; + state: number; + status: string; + node_status: string; + node_result: string; + param: string; + source: string | null; + argo_ins_name: string; + argo_ins_ns: string; + create_time: string; + update_time: string; + finish_time: string; + nodeStatus?: { [key: string]: string }; }; diff --git a/react-ui/src/pages/Experiment/components/LogGroup/index.tsx b/react-ui/src/pages/Experiment/components/LogGroup/index.tsx index b9a93e8f..27f3354c 100644 --- a/react-ui/src/pages/Experiment/components/LogGroup/index.tsx +++ b/react-ui/src/pages/Experiment/components/LogGroup/index.tsx @@ -135,7 +135,7 @@ function LogGroup({ const setupSockect = () => { let { host } = location; if (process.env.NODE_ENV === 'development') { - host = '172.20.32.185:31213'; + host = '172.20.32.181:31213'; } const socket = new WebSocket( `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx index 95bc2953..998fb436 100644 --- a/react-ui/src/pages/Experiment/index.jsx +++ b/react-ui/src/pages/Experiment/index.jsx @@ -271,6 +271,7 @@ function Experiment() { const [res] = await to(runExperiments(id)); if (res) { message.success('运行成功'); + refreshExperimentList(); refreshExperimentIns(id); } else { message.error('运行失败'); diff --git a/react-ui/src/requestConfig.ts b/react-ui/src/requestConfig.ts index b268bc47..6d298d33 100644 --- a/react-ui/src/requestConfig.ts +++ b/react-ui/src/requestConfig.ts @@ -58,11 +58,12 @@ export const requestConfig: RequestConfig = { const options = config as RequestOptions; const skipErrorHandler = options?.skipErrorHandler; const skipLoading = options?.skipLoading; + const skipValidating = options?.skipValidating; if (!skipLoading) { Loading.hide(); } if (status >= 200 && status < 300) { - if (data && (data instanceof Blob || data.code === 200)) { + if (data && (skipValidating || data instanceof Blob || data.code === 200)) { return response; } else if (data && data.code === 401) { clearSessionToken(); diff --git a/react-ui/src/services/autoML.js b/react-ui/src/services/autoML/index.js similarity index 100% rename from react-ui/src/services/autoML.js rename to react-ui/src/services/autoML/index.js diff --git a/react-ui/src/services/file/index.js b/react-ui/src/services/file/index.js new file mode 100644 index 00000000..a6786007 --- /dev/null +++ b/react-ui/src/services/file/index.js @@ -0,0 +1,20 @@ +/* + * @Author: 赵伟 + * @Date: 2024-11-30 11:43:26 + * @Description: 请求文件,比如 json 文件 + */ + + +import { request } from '@umijs/max'; + +// 获取文件,不需要token,非结构化数据 +export function getFileReq(url, config) { + return request(url, { + method: 'GET', + headers: { + isToken: false, + }, + skipValidating: true, + ...config + }); +} \ No newline at end of file