Browse Source

feat: 完成自动机器学习实例详情

pull/146/head
cp3hnu 1 year ago
parent
commit
cab811d246
19 changed files with 628 additions and 278 deletions
  1. +5
    -0
      react-ui/config/routes.ts
  2. +25
    -14
      react-ui/src/pages/AutoML/Info/index.tsx
  3. +17
    -0
      react-ui/src/pages/AutoML/Instance/index.less
  4. +118
    -0
      react-ui/src/pages/AutoML/Instance/index.tsx
  5. +7
    -5
      react-ui/src/pages/AutoML/List/index.tsx
  6. +198
    -251
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx
  7. +0
    -5
      react-ui/src/pages/AutoML/components/ConfigInfo/index.less
  8. +14
    -0
      react-ui/src/pages/AutoML/components/ExperimentHistory/index.less
  9. +132
    -0
      react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
  10. +0
    -0
      react-ui/src/pages/AutoML/components/ExperimentLog/index.less
  11. +0
    -0
      react-ui/src/pages/AutoML/components/ExperimentLog/index.tsx
  12. +18
    -0
      react-ui/src/pages/AutoML/components/ExperimentResult/index.less
  13. +51
    -0
      react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx
  14. +19
    -1
      react-ui/src/pages/AutoML/types.ts
  15. +1
    -1
      react-ui/src/pages/Experiment/components/LogGroup/index.tsx
  16. +1
    -0
      react-ui/src/pages/Experiment/index.jsx
  17. +2
    -1
      react-ui/src/requestConfig.ts
  18. +0
    -0
      react-ui/src/services/autoML/index.js
  19. +20
    -0
      react-ui/src/services/file/index.js

+ 5
- 0
react-ui/config/routes.ts View File

@@ -169,6 +169,11 @@ export default [
path: 'edit/:id',
component: './AutoML/Create/index',
},
{
name: '实验实例',
path: 'instance/:autoMLId/:id',
component: './AutoML/Instance/index',
},
],
},
],


+ 25
- 14
react-ui/src/pages/AutoML/Info/index.tsx View File

@@ -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<string>(cacheState?.activeTab ?? CommonTabKeys.Public);
const [activeTab, setActiveTab] = useState<string>(CommonTabKeys.Public);
const params = useParams();
const autoMLId = safeInvoke(Number)(params.id);
const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(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 (
<div className={styles['auto-ml-info']}>
<div className={styles['auto-ml-info__tabs']}>
<Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} />
</div>
<div className={styles['auto-ml-info__content']}>
{activeTab === CommonTabKeys.Public && <AutoMLBasic />}
{activeTab === CommonTabKeys.Public && <AutoMLBasic info={autoMLInfo} />}
{activeTab === CommonTabKeys.Private && <AutoMLTable />}
</div>
{activeTab === CommonTabKeys.Private && (


+ 17
- 0
react-ui/src/pages/AutoML/Instance/index.less View File

@@ -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;
}
}

+ 118
- 0
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -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<string>(TabKeys.Params);
const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined);
const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(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: <KFIcon type="icon-jibenxinxi" />,
},
{
key: TabKeys.Log,
label: '日志',
icon: <KFIcon type="icon-Trialliebiao" />,
},
{
key: TabKeys.Result,
label: '实验结果',
icon: <KFIcon type="icon-Trialliebiao" />,
},
{
key: TabKeys.History,
label: '运行历史',
icon: <KFIcon type="icon-Trialliebiao" />,
},
];

return (
<div className={styles['auto-ml-instance']}>
<div className={styles['auto-ml-instance__tabs']}>
<Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} />
</div>
<div className={styles['auto-ml-instance__content']}>
{activeTab === TabKeys.Params && <AutoMLBasic info={autoMLInfo} hasBasicInfo={false} />}
{activeTab === TabKeys.Log && instanceInfo && instanceInfo.nodeStatus && (
<LogList
instanceName={instanceInfo.argo_ins_name}
instanceNamespace={instanceInfo.argo_ins_ns}
pipelineNodeId={instanceInfo.nodeStatus.nodeId}
workflowId={instanceInfo.nodeStatus.id}
instanceNodeStartTime={instanceInfo.nodeStatus.startedAt}
instanceNodeStatus={instanceInfo.nodeStatus.phase as ExperimentStatus}
></LogList>
)}
{activeTab === TabKeys.Result && (
<ExperimentResult fileUrl={instanceInfo?.result_path} imageUrl={instanceInfo?.img_path} />
)}
{activeTab === TabKeys.History && (
<ExperimentHistory
fileUrl={instanceInfo?.run_history_path}
isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification}
/>
)}
</div>
</div>
);
}

export default AutoMLInstance;

+ 7
- 5
react-ui/src/pages/AutoML/List/index.tsx View File

@@ -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() {
<ExperimentInstance
experimentInsList={experimentInsList}
experimentInsTotal={experimentInsTotal}
onClickInstance={(item) => gotoInstanceInfo(item, record)}
onClickInstance={(item) => gotoInstanceInfo(record, item)}
onRemove={() => {
refreshExperimentIns(record.id);
refreshExperimentList();


+ 198
- 251
react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx View File

@@ -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<BasicInfoData[]>([]);
const [configDatas, setConfigDatas] = useState<BasicInfoData[]>([]);
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: <CopyingText text="测试项目名称测试项目名称测试项目名称"></CopyingText>,
// 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 (
<div className={styles['auto-ml-basic']}>
<Flex gap={15} align="stretch">
<ConfigInfo title="基本信息" data={basicDatas} labelWidth={70} threeColumn />
{/* <StatusChart
chartData={{ Failed: 10, Pending: 20, Running: 30, Succeeded: 40, Terminated: 50 }}
/> */}
</Flex>
{hasBasicInfo && (
<ConfigInfo
title="基本信息"
data={basicDatas}
labelWidth={70}
threeColumn
style={{ marginBottom: '20px' }}
/>
)}
<ConfigInfo
title="配置信息"
data={configDatas.slice(0, -3)}
data={configDatas}
labelWidth={150}
threeColumn
style={{ marginTop: '20px' }}
/>
<ConfigInfo
title="优化指标"
data={configDatas.slice(-3)}
labelWidth={70}
threeColumn
style={{ marginTop: '20px' }}
style={{ marginBottom: '20px' }}
/>
<ConfigInfo title="优化指标" data={metricsData} labelWidth={70} threeColumn />
</div>
);
}


+ 0
- 5
react-ui/src/pages/AutoML/components/ConfigInfo/index.less View File

@@ -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;


+ 14
- 0
react-ui/src/pages/AutoML/components/ExperimentHistory/index.less View File

@@ -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%;
}
}
}

+ 132
- 0
react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx View File

@@ -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<TableData[]>([]);
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<TableData>['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 (
<div className={styles['experiment-history']}>
<div className={styles['experiment-history__content']}>
<div
className={classNames(
'vertical-scroll-table-no-page',
styles['experiment-history__content__table'],
)}
>
<Table
dataSource={tableData}
columns={columns}
pagination={false}
scroll={{ y: 'calc(100% - 55px)' }}
rowKey="id"
/>
</div>
</div>
</div>
);
}

export default ExperimentHistory;

+ 0
- 0
react-ui/src/pages/AutoML/components/ExperimentLog/index.less View File


+ 0
- 0
react-ui/src/pages/AutoML/components/ExperimentLog/index.tsx View File


+ 18
- 0
react-ui/src/pages/AutoML/components/ExperimentResult/index.less View File

@@ -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;
}
}

+ 51
- 0
react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx View File

@@ -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<string | undefined>('');

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 (
<div className={styles['experiment-result']}>
<div className={styles['experiment-result__text']}>{result}</div>
{images.map((item, index) => (
<img
key={index}
className={styles['experiment-result__image']}
src={item}
draggable={false}
alt=""
/>
))}
</div>
);
}

export default ExperimentResult;

+ 19
- 1
react-ui/src/pages/AutoML/types.ts View File

@@ -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 };
};

+ 1
- 1
react-ui/src/pages/Experiment/components/LogGroup/index.tsx View File

@@ -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}"}`,


+ 1
- 0
react-ui/src/pages/Experiment/index.jsx View File

@@ -271,6 +271,7 @@ function Experiment() {
const [res] = await to(runExperiments(id));
if (res) {
message.success('运行成功');
refreshExperimentList();
refreshExperimentIns(id);
} else {
message.error('运行失败');


+ 2
- 1
react-ui/src/requestConfig.ts View File

@@ -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();


react-ui/src/services/autoML.js → react-ui/src/services/autoML/index.js View File


+ 20
- 0
react-ui/src/services/file/index.js View File

@@ -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
});
}

Loading…
Cancel
Save