Browse Source

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

pull/146/head
cp3hnu 1 year ago
parent
commit
1b70a295f4
23 changed files with 368 additions and 278 deletions
  1. +4
    -4
      react-ui/src/app.tsx
  2. BIN
      react-ui/src/assets/img/resample-icon.png
  3. +1
    -1
      react-ui/src/enums/index.ts
  4. +1
    -1
      react-ui/src/global.less
  5. +1
    -1
      react-ui/src/iconfont/iconfont.js
  6. +19
    -4
      react-ui/src/pages/AutoML/Create/index.tsx
  7. +3
    -14
      react-ui/src/pages/AutoML/Info/index.tsx
  8. +33
    -8
      react-ui/src/pages/AutoML/Instance/index.less
  9. +115
    -33
      react-ui/src/pages/AutoML/Instance/index.tsx
  10. +62
    -5
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx
  11. +2
    -2
      react-ui/src/pages/AutoML/components/ConfigInfo/index.less
  12. +1
    -0
      react-ui/src/pages/AutoML/components/ConfigTitle/index.less
  13. +47
    -41
      react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx
  14. +1
    -135
      react-ui/src/pages/AutoML/components/CreateForm/index.less
  15. +3
    -3
      react-ui/src/pages/AutoML/components/ExperimentHistory/index.less
  16. +27
    -6
      react-ui/src/pages/AutoML/components/ExperimentResult/index.less
  17. +14
    -9
      react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx
  18. +2
    -1
      react-ui/src/pages/AutoML/types.ts
  19. +6
    -0
      react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less
  20. +10
    -8
      react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
  21. +1
    -1
      react-ui/src/pages/Experiment/components/LogGroup/index.tsx
  22. +5
    -1
      react-ui/src/pages/Experiment/components/LogList/index.less
  23. +10
    -0
      react-ui/src/types.ts

+ 4
- 4
react-ui/src/app.tsx View File

@@ -50,7 +50,7 @@ export async function getInitialState(): Promise<GlobalInitialState> {
// 如果不是登录页面,执行
const { location } = history;

console.log('getInitialState', needAuth(location.pathname));
// console.log('getInitialState', needAuth(location.pathname));
if (needAuth(location.pathname)) {
const currentUser = await fetchUserInfo();
return {
@@ -163,7 +163,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => {
const { location } = e;
const menus = getRemoteMenu();
console.log('onRouteChange', menus);
// console.log('onRouteChange', menus);
if (menus === null && needAuth(location.pathname)) {
history.go(0);
}
@@ -174,12 +174,12 @@ export const patchRoutes: RuntimeConfig['patchRoutes'] = (e) => {
};

export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => {
console.log('patchClientRoutes', e);
// console.log('patchClientRoutes', e);
patchRouteWithRemoteMenus(e.routes);
};

export function render(oldRender: () => void) {
console.log('render');
// console.log('render');
const token = getAccessToken();
if (!token || token?.length === 0) {
oldRender();


BIN
react-ui/src/assets/img/resample-icon.png View File

Before After
Width: 51  |  Height: 51  |  Size: 1.3 kB

+ 1
- 1
react-ui/src/enums/index.ts View File

@@ -18,9 +18,9 @@ export enum AvailableRange {

// 实验状态
export enum ExperimentStatus {
Pending = 'Pending', // 启动中
Running = 'Running', // 运行中
Succeeded = 'Succeeded', // 成功
Pending = 'Pending', // 启动中
Failed = 'Failed', // 失败
Error = 'Error', // 错误
Terminated = 'Terminated', // 终止


+ 1
- 1
react-ui/src/global.less View File

@@ -43,7 +43,7 @@ body {
}

.ant-pro-layout .ant-pro-sider-menu {
padding-top: 40px;
padding-top: 15px;
}
.ant-pro-global-header-logo-mix {
padding-left: 12px;


+ 1
- 1
react-ui/src/iconfont/iconfont.js
File diff suppressed because it is too large
View File


+ 19
- 4
react-ui/src/pages/AutoML/Create/index.tsx View File

@@ -104,10 +104,15 @@ function CreateAutoML() {
const exclude_classifier = formData['exclude_classifier']?.join(',');
const exclude_feature_preprocessor = formData['exclude_feature_preprocessor']?.join(',');
const exclude_regressor = formData['exclude_regressor']?.join(',');
const metrics = formData['metrics']?.reduce((acc, cur) => {
acc[cur.name] = cur.value;
return acc;
}, {} as Record<string, number>);
const formMetrics = formData['metrics'];
const metrics =
formMetrics && Array.isArray(formMetrics) && formMetrics.length > 0
? formMetrics.reduce((acc, cur) => {
acc[cur.name] = cur.value;
return acc;
}, {} as Record<string, number>)
: undefined;

const target_columns = trimCharacter(formData['target_columns'], ',');

// 根据后台要求,修改表单数据
@@ -174,6 +179,16 @@ function CreateAutoML() {
shuffle: false,
ensemble_class: AutoMLEnsembleClass.Default,
greater_is_better: true,
ensemble_size: 50,
ensemble_nbest: 50,
max_models_on_disc: 50,
memory_limit: 3072,
per_run_time_limit: 600,
time_left_for_this_task: 3600,
resampling_strategy: 'holdout',
test_size: 0.25,
train_size: 0.67,
seed: 1,
}}
>
<BasicConfig />


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

@@ -4,16 +4,14 @@
* @Description: 自主机器学习详情
*/
import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import { CommonTabKeys } from '@/enums';
import { getAutoMLInfoReq } from '@/services/autoML';
import themes from '@/styles/theme.less';
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 AutoMLTable from '../components/AutoMLTable';
import { AutoMLData } from '../types';
import styles from './index.less';

@@ -52,19 +50,10 @@ function AutoMLInfo() {

return (
<div className={styles['auto-ml-info']}>
<div className={styles['auto-ml-info__tabs']}>
<Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} />
</div>
<PageTitle title="实验详情"></PageTitle>
<div className={styles['auto-ml-info__content']}>
{activeTab === CommonTabKeys.Public && <AutoMLBasic info={autoMLInfo} />}
{activeTab === CommonTabKeys.Private && <AutoMLTable />}
<AutoMLBasic info={autoMLInfo} />
</div>
{activeTab === CommonTabKeys.Private && (
<div className={styles['auto-ml-info__tips']}>
<KFIcon type="icon-tishi" color={themes['warningColor']} font={17} />
<span style={{ marginLeft: '4px' }}>Trial是一次独立的尝试,他会使用某组超参来运行</span>
</div>
)}
</div>
);
}


+ 33
- 8
react-ui/src/pages/AutoML/Instance/index.less View File

@@ -2,16 +2,41 @@
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%;
height: 100%;
:global {
.ant-tabs-nav-list {
width: 100%;
height: 50px;
padding-left: 15px;
background-image: url(@/assets/img/page-title-bg.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100% 100%;
}

.ant-tabs-content-holder {
height: calc(100% - 50px);
.ant-tabs-content {
height: 100%;
.ant-tabs-tabpane {
height: 100%;
}
}
}
}
}

&__basic {
height: calc(100% - 10px);
margin-top: 10px;
}

&__content {
height: calc(100% - 60px);
&__log {
height: calc(100% - 10px);
margin-top: 10px;
padding: 20px calc(@content-padding - 8px);
overflow-y: visible;
background-color: white;
border-radius: 10px;
}
}

+ 115
- 33
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -2,12 +2,13 @@ import KFIcon from '@/components/KFIcon';
import { AutoMLTaskType, ExperimentStatus } from '@/enums';
import LogList from '@/pages/Experiment/components/LogList';
import { getExperimentInsReq } from '@/services/autoML';
import { NodeStatus } from '@/types';
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 { useEffect, useRef, useState } from 'react';
import AutoMLBasic from '../components/AutoMLBasic';
import ExperimentHistory from '../components/ExperimentHistory';
import ExperimentResult from '../components/ExperimentResult';
@@ -28,11 +29,15 @@ function AutoMLInstance() {
const params = useParams();
// const autoMLId = safeInvoke(Number)(params.autoMLId);
const instanceId = safeInvoke(Number)(params.id);
const evtSourceRef = useRef<EventSource | null>(null);

useEffect(() => {
if (instanceId) {
getExperimentInsInfo();
}
return () => {
closeSSE();
};
}, []);

// 获取实验实例详情
@@ -40,7 +45,7 @@ function AutoMLInstance() {
const [res] = await to(getExperimentInsReq(instanceId));
if (res && res.data) {
const info = res.data as AutoMLInstanceData;
const { param, node_status } = info;
const { param, node_status, argo_ins_name, argo_ins_ns, status } = info;
// 解析配置参数
const paramJson = parseJsonText(param);
if (paramJson) {
@@ -52,65 +57,142 @@ function AutoMLInstance() {
Object.keys(nodeStatusJson).forEach((key) => {
if (key.startsWith('auto-ml')) {
const value = nodeStatusJson[key];
value.nodeId = key;
info.nodeStatus = value;
}
});
}
setInstanceInfo(info);
// 运行中或者等待中,开启 SSE
if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) {
setupSSE(argo_ins_name, argo_ins_ns);
}
}
};

const tabItems = [
const setupSSE = (name: string, namespace: string) => {
let { origin } = location;
if (process.env.NODE_ENV === 'development') {
origin = 'http://172.20.32.181:31213';
}
const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
const evtSource = new EventSource(
`${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
{ withCredentials: false },
);
evtSource.onmessage = (event) => {
const data = event?.data;
if (!data) {
return;
}
const dataJson = parseJsonText(data);
if (dataJson) {
const nodes = dataJson?.result?.object?.status?.nodes;
if (nodes) {
const statusData = Object.values(nodes).find((node: any) =>
node.displayName.startsWith('auto-ml'),
) as NodeStatus;
if (statusData) {
setInstanceInfo((prev) => ({
...(prev as AutoMLInstanceData),
nodeStatus: statusData,
}));

// 实验结束,关闭 SSE
if (
statusData.phase !== ExperimentStatus.Pending &&
statusData.phase !== ExperimentStatus.Running
) {
closeSSE();
getExperimentInsInfo();
}
}
}
}
};
evtSource.onerror = (error) => {
console.error('SSE error: ', error);
};

evtSourceRef.current = evtSource;
};

const closeSSE = () => {
if (evtSourceRef.current) {
evtSourceRef.current.close();
evtSourceRef.current = null;
}
};

const basicTabItems = [
{
key: TabKeys.Params,
label: '参数信息',
label: '基本信息',
icon: <KFIcon type="icon-jibenxinxi" />,
children: (
<AutoMLBasic
className={styles['auto-ml-instance__basic']}
info={autoMLInfo}
runStatus={instanceInfo?.nodeStatus}
isInstance
/>
),
},
{
key: TabKeys.Log,
label: '日志',
icon: <KFIcon type="icon-Trialliebiao" />,
icon: <KFIcon type="icon-rizhi" />,
children: (
<div className={styles['auto-ml-instance__log']}>
{instanceInfo && instanceInfo.nodeStatus && (
<LogList
instanceName={instanceInfo.argo_ins_name}
instanceNamespace={instanceInfo.argo_ins_ns}
pipelineNodeId={instanceInfo.nodeStatus.displayName}
workflowId={instanceInfo.nodeStatus.id}
instanceNodeStartTime={instanceInfo.nodeStatus.startedAt}
instanceNodeStatus={instanceInfo.nodeStatus.phase as ExperimentStatus}
></LogList>
)}
</div>
),
},
];

const resultTabItems = [
{
key: TabKeys.Result,
label: '实验结果',
icon: <KFIcon type="icon-Trialliebiao" />,
icon: <KFIcon type="icon-shiyanjieguo1" />,
children: (
<ExperimentResult fileUrl={instanceInfo?.result_path} imageUrl={instanceInfo?.img_path} />
),
},
{
key: TabKeys.History,
label: '运行历史',
label: 'Trial列表',
icon: <KFIcon type="icon-Trialliebiao" />,
children: (
<ExperimentHistory
fileUrl={instanceInfo?.run_history_path}
isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification}
/>
),
},
];

const tabItems =
instanceInfo?.status === ExperimentStatus.Succeeded
? [...basicTabItems, ...resultTabItems]
: basicTabItems;

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>
<Tabs
className={styles['auto-ml-instance__tabs']}
items={tabItems}
activeKey={activeTab}
onChange={setActiveTab}
/>
</div>
);
}


+ 62
- 5
react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx View File

@@ -1,6 +1,11 @@
import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums';
import { AutoMLData } from '@/pages/AutoML/types';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import { type NodeStatus } from '@/types';
import { parseJsonText } from '@/utils';
import { elapsedTime } from '@/utils/date';
import { Flex } from 'antd';
import classNames from 'classnames';
import { useMemo } from 'react';
import ConfigInfo, {
formatBoolean,
@@ -38,10 +43,12 @@ const formatMetricsWeight = (value: string) => {

type AutoMLBasicProps = {
info?: AutoMLData;
hasBasicInfo?: boolean;
className?: string;
isInstance?: boolean;
runStatus?: NodeStatus;
};

function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) {
function AutoMLBasic({ info, className, runStatus, isInstance = false }: AutoMLBasicProps) {
const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];
@@ -142,7 +149,7 @@ function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) {
ellipsis: true,
},
{
label: '时间限制(秒)',
label: '单次时间限制(秒)',
value: info.per_run_time_limit,
ellipsis: true,
},
@@ -227,9 +234,59 @@ function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) {
];
}, [info]);

const instanceDatas = useMemo(() => {
if (!runStatus) {
return [];
}

return [
{
label: '启动时间',
value: formatDate(runStatus.startedAt),
ellipsis: true,
},
{
label: '执行时长',
value: elapsedTime(runStatus.startedAt, runStatus.finishedAt),
ellipsis: true,
},
{
label: '状态',
value: (
<Flex align="center">
<img
style={{ width: '17px', marginRight: '7px' }}
src={experimentStatusInfo[runStatus.phase]?.icon}
draggable={false}
alt=""
/>
<div
style={{
color: experimentStatusInfo[runStatus?.phase]?.color,
fontSize: '15px',
lineHeight: 1.6,
}}
>
{experimentStatusInfo[runStatus?.phase]?.label}
</div>
</Flex>
),
ellipsis: true,
},
];
}, [runStatus]);

return (
<div className={styles['auto-ml-basic']}>
{hasBasicInfo && (
<div className={classNames(styles['auto-ml-basic'], className)}>
{isInstance && runStatus ? (
<ConfigInfo
title="运行信息"
data={instanceDatas}
labelWidth={70}
threeColumn
style={{ marginBottom: '20px' }}
/>
) : (
<ConfigInfo
title="基本信息"
data={basicDatas}


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

@@ -1,13 +1,13 @@
.config-info {
flex: 1;
min-width: 0;
border: 1px solid @border-color-base;
border-radius: 4px;

&__content {
padding: 20px;
padding: 20px @content-padding;
background-color: white;
border: 1px solid @border-color-base;
border-radius: 0 0 4px 4px;
}

:global {


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

@@ -8,6 +8,7 @@
rgba(22, 100, 255, 0.04) 100%
);
border: 1px solid #e8effb;
border-radius: 4px 4px 0 0;

&__img {
width: 16px;


+ 47
- 41
react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx View File

@@ -315,7 +315,7 @@ function ExecuteConfig() {
<Form.Item
label="内存限制(MB)"
name="memory_limit"
tooltip="机器学习算法的内存限制(MB)。如果auto-sklearn试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072"
tooltip="机器学习算法的内存限制(MB)。如果自动机器学习试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072"
>
<InputNumber placeholder="请输入内存限制" min={0} precision={0} />
</Form.Item>
@@ -325,7 +325,7 @@ function ExecuteConfig() {
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="时间限制(秒)"
label="单次时间限制(秒)"
name="per_run_time_limit"
tooltip="单次调用机器学习模型的时间限制(以秒为单位)。如果机器学习算法运行超过时间限制,将终止模型拟合,默认600"
>
@@ -339,13 +339,56 @@ function ExecuteConfig() {
<Form.Item
label="搜索时间限制(秒)"
name="time_left_for_this_task"
tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,auto-sklearn有更高的机会找到更好的模型。默认3600。"
tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,自动机器学习有更高的机会找到更好的模型。默认3600。"
>
<InputNumber placeholder="请输入搜索时间限制" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="测试集比率"
name="test_size"
tooltip="将数据划分为训练数据和测试数据,测试数据集所占比例,0到1之间"
>
<InputNumber placeholder="请输入测试集比率" min={0} max={1} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="计算指标" name="scoring_functions" tooltip="需要计算并打印的指标">
<Select
allowClear
placeholder="请选择计算指标"
options={
task_type === AutoMLTaskType.Classification
? classificationMetrics
: regressionMetrics
}
showSearch
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="随机种子" name="seed" tooltip="随机种子,将决定输出文件名">
<InputNumber placeholder="请输入随机种子" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<SubAreaTitle
title="重采样策略"
image={require('@/assets/img/resample-icon.png')}
style={{ marginTop: '20px', marginBottom: '24px' }}
></SubAreaTitle>

<Row gutter={8}>
<Col span={10}>
<Form.Item
@@ -399,50 +442,13 @@ function ExecuteConfig() {
<Form.Item
label="训练集比率"
name="train_size"
tooltip="将数据划分为训练数据和测试数据,训练数据集所占比例,0到1之间"
tooltip="重采样划分训练集和验证集,训练集的比率,0到1之间"
>
<InputNumber placeholder="请输入训练集比率" min={0} max={1} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="测试集比率"
name="test_size"
tooltip="将数据划分为训练数据和测试数据,测试数据集所占比例,0到1之间"
>
<InputNumber placeholder="请输入测试集比率" min={0} max={1} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="计算指标" name="scoring_functions" tooltip="需要计算并打印的指标">
<Select
allowClear
placeholder="请选择计算指标"
options={
task_type === AutoMLTaskType.Classification
? classificationMetrics
: regressionMetrics
}
showSearch
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="随机种子" name="seed" tooltip="随机种子,将决定输出文件名">
<InputNumber placeholder="请输入随机种子" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

{/* <Row gutter={8}>
<Col span={10}>
<Form.Item


+ 1
- 135
react-ui/src/pages/AutoML/components/CreateForm/index.less View File

@@ -7,7 +7,7 @@
}

.add-weight {
margin-bottom: 0;
margin-bottom: 0 !important;

// 增加样式权重
& &__button {
@@ -18,137 +18,3 @@
}
}
}

// .command {
// width: 83.33%;
// margin-bottom: 20px;
// border: 1px solid rgba(234, 234, 234, 0.8);
// border-radius: 4px;
// &__header {
// height: 50px;
// padding-left: 8px;
// color: @text-color;
// font-size: @font-size;
// background: #f8f8f9;
// border-radius: 4px 4px 0px 0px;

// &__name {
// flex: none;
// width: 100px;
// }
// &__command {
// flex: 1;
// margin-right: 15px;
// }

// &__operation {
// flex: none;
// width: 100px;
// }
// }
// &__body {
// padding: 8px;
// border-bottom: 1px solid rgba(234, 234, 234, 0.8);

// &:last-child {
// border-bottom: none;
// }

// &__name {
// flex: none;
// width: 100px;
// }

// &__command {
// flex: 1;
// margin-right: 15px;
// margin-bottom: 0 !important;
// }

// &__operation {
// flex: none;
// width: 100px;
// }
// }

// &__add {
// display: flex;
// align-items: center;
// justify-content: center;
// padding: 15px 0;
// }
// }

// .hyper-parameter {
// width: 83.33%;
// margin-bottom: 20px;
// border: 1px solid rgba(234, 234, 234, 0.8);
// border-radius: 4px;
// &__header {
// height: 50px;
// padding-left: 8px;
// color: @text-color;
// font-size: @font-size;
// background: #f8f8f9;
// border-radius: 4px 4px 0px 0px;

// &__name,
// &__type,
// &__space {
// flex: 1;
// margin-right: 15px;
// }

// &__operation {
// flex: none;
// width: 100px;
// }
// }
// &__body {
// padding: 8px;
// border-bottom: 1px solid rgba(234, 234, 234, 0.8);

// &:last-child {
// border-bottom: none;
// }

// &__name,
// &__type,
// &__space {
// flex: 1;
// margin-right: 15px;
// margin-bottom: 0 !important;
// }

// &__operation {
// flex: none;
// width: 100px;
// }
// }

// &__add {
// display: flex;
// align-items: center;
// justify-content: center;
// padding: 15px 0;
// }
// }

// .trial-metrics {
// width: calc(41.67% - 6px);
// margin-bottom: 20px;
// padding: 0 20px;
// border: 1px dashed #e0e0e0;
// border-radius: 8px;
// }

// .upload-tip {
// margin-top: 5px;
// color: @text-color-secondary;
// font-size: 14px;
// }

// .upload-button {
// height: 46px;
// font-size: 15px;
// }

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

@@ -1,9 +1,9 @@
.experiment-history {
height: 100%;
height: calc(100% - 10px);
margin-top: 10px;
&__content {
height: 100%;
margin-top: 10px;
padding: 20px @content-padding 0;
padding: 20px @content-padding;
background-color: white;
border-radius: 10px;



+ 27
- 6
react-ui/src/pages/AutoML/components/ExperimentResult/index.less View File

@@ -1,18 +1,39 @@
.experiment-result {
height: 100%;
height: calc(100% - 10px);
margin-top: 10px;
padding: 20px @content-padding 0;
padding: 20px @content-padding;
overflow-y: auto;
background-color: white;
border-radius: 10px;

&__text {
margin-bottom: 20px;
width: 100%;
height: 460px;
margin-bottom: 16px;
padding: 20px @content-padding;
overflow: auto;
white-space: pre-wrap;
border: 1px solid @border-color-base;
border-radius: 0 0 4px 4px;
}

&__image {
display: block;
height: 200px;
&__image-container {
display: flex;
align-items: flex-start;
width: 100%;
padding: 20px @content-padding;
overflow-x: auto;
border: 1px solid @border-color-base;
border-radius: 0 0 4px 4px;

&__image {
height: 248px;
margin-right: 20px;
border: 1px solid rgba(96, 107, 122, 0.3);

&:last-child {
margin-right: 0;
}
}
}
}

+ 14
- 9
react-ui/src/pages/AutoML/components/ExperimentResult/index.tsx View File

@@ -1,6 +1,7 @@
import { getFileReq } from '@/services/file';
import { to } from '@/utils/promise';
import { useEffect, useMemo, useState } from 'react';
import ConfigTitle from '../ConfigTitle';
import styles from './index.less';

type ExperimentResultProps = {
@@ -34,16 +35,20 @@ function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) {

return (
<div className={styles['experiment-result']}>
<ConfigTitle title="实验结果"></ConfigTitle>
<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=""
/>
))}
<ConfigTitle title="可视化结果"></ConfigTitle>
<div className={styles['experiment-result__image-container']}>
{images.map((item, index) => (
<img
key={index}
className={styles['experiment-result__image-container__image']}
src={item}
draggable={false}
alt=""
/>
))}
</div>
</div>
);
}


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

@@ -1,4 +1,5 @@
import { type ParameterInputObject } from '@/components/ResourceSelect';
import { type NodeStatus } from '@/types';

// 操作类型
export enum OperationType {
@@ -80,5 +81,5 @@ export type AutoMLInstanceData = {
create_time: string;
update_time: string;
finish_time: string;
nodeStatus?: { [key: string]: string };
nodeStatus?: NodeStatus;
};

+ 6
- 0
react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less View File

@@ -39,4 +39,10 @@
margin-right: 6px;
border-radius: 50%;
}

&__log {
height: 100%;
padding: 8px;
background: white;
}
}

+ 10
- 8
react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx View File

@@ -48,14 +48,16 @@ const ExperimentDrawer = ({
key: '1',
label: '日志详情',
children: (
<LogList
instanceName={instanceName}
instanceNamespace={instanceNamespace}
pipelineNodeId={instanceNodeData.id}
workflowId={workflowId}
instanceNodeStartTime={instanceNodeStartTime}
instanceNodeStatus={instanceNodeStatus}
></LogList>
<div className={styles['experiment-drawer__log']}>
<LogList
instanceName={instanceName}
instanceNamespace={instanceNamespace}
pipelineNodeId={instanceNodeData.id}
workflowId={workflowId}
instanceNodeStartTime={instanceNodeStartTime}
instanceNodeStatus={instanceNodeStatus}
></LogList>
</div>
),
icon: <ProfileOutlined />,
},


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

@@ -90,7 +90,7 @@ function LogGroup({
start_time: startTime,
};
const res = await getExperimentPodsLog(params);
const { log_detail } = res.data;
const { log_detail } = res.data || {};
if (log_detail) {
setLogList((oldList) => oldList.concat(log_detail));



+ 5
- 1
react-ui/src/pages/Experiment/components/LogList/index.less View File

@@ -1,7 +1,7 @@
.log-list {
height: 100%;
padding: 8px;
overflow-y: auto;
background: #19253b;

&__empty {
padding: 15px;
@@ -12,4 +12,8 @@
word-break: break-all;
background: #19253b;
}

&::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.5);
}
}

+ 10
- 0
react-ui/src/types.ts View File

@@ -114,3 +114,13 @@ export type ComputingResource = {
standard: string;
create_by: string;
};

// 实验运行节点状态
export type NodeStatus = {
id: string; // workflow Id
displayName: string;
name: string;
phase: ExperimentStatus;
startedAt: string;
finishedAt: string;
};

Loading…
Cancel
Save