| @@ -50,7 +50,7 @@ export async function getInitialState(): Promise<GlobalInitialState> { | |||||
| // 如果不是登录页面,执行 | // 如果不是登录页面,执行 | ||||
| const { location } = history; | const { location } = history; | ||||
| console.log('getInitialState', needAuth(location.pathname)); | |||||
| // console.log('getInitialState', needAuth(location.pathname)); | |||||
| if (needAuth(location.pathname)) { | if (needAuth(location.pathname)) { | ||||
| const currentUser = await fetchUserInfo(); | const currentUser = await fetchUserInfo(); | ||||
| return { | return { | ||||
| @@ -163,7 +163,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||||
| export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => { | export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => { | ||||
| const { location } = e; | const { location } = e; | ||||
| const menus = getRemoteMenu(); | const menus = getRemoteMenu(); | ||||
| console.log('onRouteChange', menus); | |||||
| // console.log('onRouteChange', menus); | |||||
| if (menus === null && needAuth(location.pathname)) { | if (menus === null && needAuth(location.pathname)) { | ||||
| history.go(0); | history.go(0); | ||||
| } | } | ||||
| @@ -174,12 +174,12 @@ export const patchRoutes: RuntimeConfig['patchRoutes'] = (e) => { | |||||
| }; | }; | ||||
| export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => { | export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => { | ||||
| console.log('patchClientRoutes', e); | |||||
| // console.log('patchClientRoutes', e); | |||||
| patchRouteWithRemoteMenus(e.routes); | patchRouteWithRemoteMenus(e.routes); | ||||
| }; | }; | ||||
| export function render(oldRender: () => void) { | export function render(oldRender: () => void) { | ||||
| console.log('render'); | |||||
| // console.log('render'); | |||||
| const token = getAccessToken(); | const token = getAccessToken(); | ||||
| if (!token || token?.length === 0) { | if (!token || token?.length === 0) { | ||||
| oldRender(); | oldRender(); | ||||
| @@ -18,9 +18,9 @@ export enum AvailableRange { | |||||
| // 实验状态 | // 实验状态 | ||||
| export enum ExperimentStatus { | export enum ExperimentStatus { | ||||
| Pending = 'Pending', // 启动中 | |||||
| Running = 'Running', // 运行中 | Running = 'Running', // 运行中 | ||||
| Succeeded = 'Succeeded', // 成功 | Succeeded = 'Succeeded', // 成功 | ||||
| Pending = 'Pending', // 启动中 | |||||
| Failed = 'Failed', // 失败 | Failed = 'Failed', // 失败 | ||||
| Error = 'Error', // 错误 | Error = 'Error', // 错误 | ||||
| Terminated = 'Terminated', // 终止 | Terminated = 'Terminated', // 终止 | ||||
| @@ -43,7 +43,7 @@ body { | |||||
| } | } | ||||
| .ant-pro-layout .ant-pro-sider-menu { | .ant-pro-layout .ant-pro-sider-menu { | ||||
| padding-top: 40px; | |||||
| padding-top: 15px; | |||||
| } | } | ||||
| .ant-pro-global-header-logo-mix { | .ant-pro-global-header-logo-mix { | ||||
| padding-left: 12px; | padding-left: 12px; | ||||
| @@ -104,10 +104,15 @@ function CreateAutoML() { | |||||
| const exclude_classifier = formData['exclude_classifier']?.join(','); | const exclude_classifier = formData['exclude_classifier']?.join(','); | ||||
| const exclude_feature_preprocessor = formData['exclude_feature_preprocessor']?.join(','); | const exclude_feature_preprocessor = formData['exclude_feature_preprocessor']?.join(','); | ||||
| const exclude_regressor = formData['exclude_regressor']?.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'], ','); | const target_columns = trimCharacter(formData['target_columns'], ','); | ||||
| // 根据后台要求,修改表单数据 | // 根据后台要求,修改表单数据 | ||||
| @@ -174,6 +179,16 @@ function CreateAutoML() { | |||||
| shuffle: false, | shuffle: false, | ||||
| ensemble_class: AutoMLEnsembleClass.Default, | ensemble_class: AutoMLEnsembleClass.Default, | ||||
| greater_is_better: true, | 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 /> | <BasicConfig /> | ||||
| @@ -4,16 +4,14 @@ | |||||
| * @Description: 自主机器学习详情 | * @Description: 自主机器学习详情 | ||||
| */ | */ | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | |||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { getAutoMLInfoReq } from '@/services/autoML'; | import { getAutoMLInfoReq } from '@/services/autoML'; | ||||
| import themes from '@/styles/theme.less'; | |||||
| import { safeInvoke } from '@/utils/functional'; | import { safeInvoke } from '@/utils/functional'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { useParams } from '@umijs/max'; | import { useParams } from '@umijs/max'; | ||||
| import { Tabs } from 'antd'; | |||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import AutoMLBasic from '../components/AutoMLBasic'; | import AutoMLBasic from '../components/AutoMLBasic'; | ||||
| import AutoMLTable from '../components/AutoMLTable'; | |||||
| import { AutoMLData } from '../types'; | import { AutoMLData } from '../types'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -52,19 +50,10 @@ function AutoMLInfo() { | |||||
| return ( | return ( | ||||
| <div className={styles['auto-ml-info']}> | <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']}> | <div className={styles['auto-ml-info__content']}> | ||||
| {activeTab === CommonTabKeys.Public && <AutoMLBasic info={autoMLInfo} />} | |||||
| {activeTab === CommonTabKeys.Private && <AutoMLTable />} | |||||
| <AutoMLBasic info={autoMLInfo} /> | |||||
| </div> | </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> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -2,16 +2,41 @@ | |||||
| height: 100%; | height: 100%; | ||||
| &__tabs { | &__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; | margin-top: 10px; | ||||
| padding: 20px calc(@content-padding - 8px); | |||||
| overflow-y: visible; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,12 +2,13 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | ||||
| import LogList from '@/pages/Experiment/components/LogList'; | import LogList from '@/pages/Experiment/components/LogList'; | ||||
| import { getExperimentInsReq } from '@/services/autoML'; | import { getExperimentInsReq } from '@/services/autoML'; | ||||
| import { NodeStatus } from '@/types'; | |||||
| import { parseJsonText } from '@/utils'; | import { parseJsonText } from '@/utils'; | ||||
| import { safeInvoke } from '@/utils/functional'; | import { safeInvoke } from '@/utils/functional'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { useParams } from '@umijs/max'; | import { useParams } from '@umijs/max'; | ||||
| import { Tabs } from 'antd'; | import { Tabs } from 'antd'; | ||||
| import { useEffect, useState } from 'react'; | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| import AutoMLBasic from '../components/AutoMLBasic'; | import AutoMLBasic from '../components/AutoMLBasic'; | ||||
| import ExperimentHistory from '../components/ExperimentHistory'; | import ExperimentHistory from '../components/ExperimentHistory'; | ||||
| import ExperimentResult from '../components/ExperimentResult'; | import ExperimentResult from '../components/ExperimentResult'; | ||||
| @@ -28,11 +29,15 @@ function AutoMLInstance() { | |||||
| const params = useParams(); | const params = useParams(); | ||||
| // const autoMLId = safeInvoke(Number)(params.autoMLId); | // const autoMLId = safeInvoke(Number)(params.autoMLId); | ||||
| const instanceId = safeInvoke(Number)(params.id); | const instanceId = safeInvoke(Number)(params.id); | ||||
| const evtSourceRef = useRef<EventSource | null>(null); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (instanceId) { | if (instanceId) { | ||||
| getExperimentInsInfo(); | getExperimentInsInfo(); | ||||
| } | } | ||||
| return () => { | |||||
| closeSSE(); | |||||
| }; | |||||
| }, []); | }, []); | ||||
| // 获取实验实例详情 | // 获取实验实例详情 | ||||
| @@ -40,7 +45,7 @@ function AutoMLInstance() { | |||||
| const [res] = await to(getExperimentInsReq(instanceId)); | const [res] = await to(getExperimentInsReq(instanceId)); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const info = res.data as AutoMLInstanceData; | 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); | const paramJson = parseJsonText(param); | ||||
| if (paramJson) { | if (paramJson) { | ||||
| @@ -52,65 +57,142 @@ function AutoMLInstance() { | |||||
| Object.keys(nodeStatusJson).forEach((key) => { | Object.keys(nodeStatusJson).forEach((key) => { | ||||
| if (key.startsWith('auto-ml')) { | if (key.startsWith('auto-ml')) { | ||||
| const value = nodeStatusJson[key]; | const value = nodeStatusJson[key]; | ||||
| value.nodeId = key; | |||||
| info.nodeStatus = value; | info.nodeStatus = value; | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| setInstanceInfo(info); | 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, | key: TabKeys.Params, | ||||
| label: '参数信息', | |||||
| label: '基本信息', | |||||
| icon: <KFIcon type="icon-jibenxinxi" />, | icon: <KFIcon type="icon-jibenxinxi" />, | ||||
| children: ( | |||||
| <AutoMLBasic | |||||
| className={styles['auto-ml-instance__basic']} | |||||
| info={autoMLInfo} | |||||
| runStatus={instanceInfo?.nodeStatus} | |||||
| isInstance | |||||
| /> | |||||
| ), | |||||
| }, | }, | ||||
| { | { | ||||
| key: TabKeys.Log, | key: TabKeys.Log, | ||||
| label: '日志', | 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, | key: TabKeys.Result, | ||||
| label: '实验结果', | label: '实验结果', | ||||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||||
| icon: <KFIcon type="icon-shiyanjieguo1" />, | |||||
| children: ( | |||||
| <ExperimentResult fileUrl={instanceInfo?.result_path} imageUrl={instanceInfo?.img_path} /> | |||||
| ), | |||||
| }, | }, | ||||
| { | { | ||||
| key: TabKeys.History, | key: TabKeys.History, | ||||
| label: '运行历史', | |||||
| label: 'Trial列表', | |||||
| icon: <KFIcon type="icon-Trialliebiao" />, | 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 ( | return ( | ||||
| <div className={styles['auto-ml-instance']}> | <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> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -1,6 +1,11 @@ | |||||
| import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; | import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; | ||||
| import { AutoMLData } from '@/pages/AutoML/types'; | import { AutoMLData } from '@/pages/AutoML/types'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { type NodeStatus } from '@/types'; | |||||
| import { parseJsonText } from '@/utils'; | import { parseJsonText } from '@/utils'; | ||||
| import { elapsedTime } from '@/utils/date'; | |||||
| import { Flex } from 'antd'; | |||||
| import classNames from 'classnames'; | |||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| import ConfigInfo, { | import ConfigInfo, { | ||||
| formatBoolean, | formatBoolean, | ||||
| @@ -38,10 +43,12 @@ const formatMetricsWeight = (value: string) => { | |||||
| type AutoMLBasicProps = { | type AutoMLBasicProps = { | ||||
| info?: AutoMLData; | 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(() => { | const basicDatas: BasicInfoData[] = useMemo(() => { | ||||
| if (!info) { | if (!info) { | ||||
| return []; | return []; | ||||
| @@ -142,7 +149,7 @@ function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { | |||||
| ellipsis: true, | ellipsis: true, | ||||
| }, | }, | ||||
| { | { | ||||
| label: '时间限制(秒)', | |||||
| label: '单次时间限制(秒)', | |||||
| value: info.per_run_time_limit, | value: info.per_run_time_limit, | ||||
| ellipsis: true, | ellipsis: true, | ||||
| }, | }, | ||||
| @@ -227,9 +234,59 @@ function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { | |||||
| ]; | ]; | ||||
| }, [info]); | }, [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 ( | 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 | <ConfigInfo | ||||
| title="基本信息" | title="基本信息" | ||||
| data={basicDatas} | data={basicDatas} | ||||
| @@ -1,13 +1,13 @@ | |||||
| .config-info { | .config-info { | ||||
| flex: 1; | flex: 1; | ||||
| min-width: 0; | min-width: 0; | ||||
| border: 1px solid @border-color-base; | |||||
| border-radius: 4px; | |||||
| &__content { | &__content { | ||||
| padding: 20px; | padding: 20px; | ||||
| padding: 20px @content-padding; | padding: 20px @content-padding; | ||||
| background-color: white; | background-color: white; | ||||
| border: 1px solid @border-color-base; | |||||
| border-radius: 0 0 4px 4px; | |||||
| } | } | ||||
| :global { | :global { | ||||
| @@ -8,6 +8,7 @@ | |||||
| rgba(22, 100, 255, 0.04) 100% | rgba(22, 100, 255, 0.04) 100% | ||||
| ); | ); | ||||
| border: 1px solid #e8effb; | border: 1px solid #e8effb; | ||||
| border-radius: 4px 4px 0 0; | |||||
| &__img { | &__img { | ||||
| width: 16px; | width: 16px; | ||||
| @@ -315,7 +315,7 @@ function ExecuteConfig() { | |||||
| <Form.Item | <Form.Item | ||||
| label="内存限制(MB)" | label="内存限制(MB)" | ||||
| name="memory_limit" | name="memory_limit" | ||||
| tooltip="机器学习算法的内存限制(MB)。如果auto-sklearn试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072" | |||||
| tooltip="机器学习算法的内存限制(MB)。如果自动机器学习试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072" | |||||
| > | > | ||||
| <InputNumber placeholder="请输入内存限制" min={0} precision={0} /> | <InputNumber placeholder="请输入内存限制" min={0} precision={0} /> | ||||
| </Form.Item> | </Form.Item> | ||||
| @@ -325,7 +325,7 @@ function ExecuteConfig() { | |||||
| <Row gutter={8}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | <Form.Item | ||||
| label="时间限制(秒)" | |||||
| label="单次时间限制(秒)" | |||||
| name="per_run_time_limit" | name="per_run_time_limit" | ||||
| tooltip="单次调用机器学习模型的时间限制(以秒为单位)。如果机器学习算法运行超过时间限制,将终止模型拟合,默认600" | tooltip="单次调用机器学习模型的时间限制(以秒为单位)。如果机器学习算法运行超过时间限制,将终止模型拟合,默认600" | ||||
| > | > | ||||
| @@ -339,13 +339,56 @@ function ExecuteConfig() { | |||||
| <Form.Item | <Form.Item | ||||
| label="搜索时间限制(秒)" | label="搜索时间限制(秒)" | ||||
| name="time_left_for_this_task" | name="time_left_for_this_task" | ||||
| tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,auto-sklearn有更高的机会找到更好的模型。默认3600。" | |||||
| tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,自动机器学习有更高的机会找到更好的模型。默认3600。" | |||||
| > | > | ||||
| <InputNumber placeholder="请输入搜索时间限制" min={0} precision={0} /> | <InputNumber placeholder="请输入搜索时间限制" min={0} precision={0} /> | ||||
| </Form.Item> | </Form.Item> | ||||
| </Col> | </Col> | ||||
| </Row> | </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}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | <Form.Item | ||||
| @@ -399,50 +442,13 @@ function ExecuteConfig() { | |||||
| <Form.Item | <Form.Item | ||||
| label="训练集比率" | label="训练集比率" | ||||
| name="train_size" | name="train_size" | ||||
| tooltip="将数据划分为训练数据和测试数据,训练数据集所占比例,0到1之间" | |||||
| tooltip="重采样划分训练集和验证集,训练集的比率,0到1之间" | |||||
| > | > | ||||
| <InputNumber placeholder="请输入训练集比率" min={0} max={1} /> | <InputNumber placeholder="请输入训练集比率" min={0} max={1} /> | ||||
| </Form.Item> | </Form.Item> | ||||
| </Col> | </Col> | ||||
| </Row> | </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}> | {/* <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | <Form.Item | ||||
| @@ -7,7 +7,7 @@ | |||||
| } | } | ||||
| .add-weight { | .add-weight { | ||||
| margin-bottom: 0; | |||||
| margin-bottom: 0 !important; | |||||
| // 增加样式权重 | // 增加样式权重 | ||||
| & &__button { | & &__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; | |||||
| // } | |||||
| @@ -1,9 +1,9 @@ | |||||
| .experiment-history { | .experiment-history { | ||||
| height: 100%; | |||||
| height: calc(100% - 10px); | |||||
| margin-top: 10px; | |||||
| &__content { | &__content { | ||||
| height: 100%; | height: 100%; | ||||
| margin-top: 10px; | |||||
| padding: 20px @content-padding 0; | |||||
| padding: 20px @content-padding; | |||||
| background-color: white; | background-color: white; | ||||
| border-radius: 10px; | border-radius: 10px; | ||||
| @@ -1,18 +1,39 @@ | |||||
| .experiment-result { | .experiment-result { | ||||
| height: 100%; | |||||
| height: calc(100% - 10px); | |||||
| margin-top: 10px; | margin-top: 10px; | ||||
| padding: 20px @content-padding 0; | |||||
| padding: 20px @content-padding; | |||||
| overflow-y: auto; | overflow-y: auto; | ||||
| background-color: white; | background-color: white; | ||||
| border-radius: 10px; | border-radius: 10px; | ||||
| &__text { | &__text { | ||||
| margin-bottom: 20px; | |||||
| width: 100%; | |||||
| height: 460px; | |||||
| margin-bottom: 16px; | |||||
| padding: 20px @content-padding; | |||||
| overflow: auto; | |||||
| white-space: pre-wrap; | 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; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| import { getFileReq } from '@/services/file'; | import { getFileReq } from '@/services/file'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { useEffect, useMemo, useState } from 'react'; | import { useEffect, useMemo, useState } from 'react'; | ||||
| import ConfigTitle from '../ConfigTitle'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| type ExperimentResultProps = { | type ExperimentResultProps = { | ||||
| @@ -34,16 +35,20 @@ function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) { | |||||
| return ( | return ( | ||||
| <div className={styles['experiment-result']}> | <div className={styles['experiment-result']}> | ||||
| <ConfigTitle title="实验结果"></ConfigTitle> | |||||
| <div className={styles['experiment-result__text']}>{result}</div> | <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> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| import { type ParameterInputObject } from '@/components/ResourceSelect'; | import { type ParameterInputObject } from '@/components/ResourceSelect'; | ||||
| import { type NodeStatus } from '@/types'; | |||||
| // 操作类型 | // 操作类型 | ||||
| export enum OperationType { | export enum OperationType { | ||||
| @@ -80,5 +81,5 @@ export type AutoMLInstanceData = { | |||||
| create_time: string; | create_time: string; | ||||
| update_time: string; | update_time: string; | ||||
| finish_time: string; | finish_time: string; | ||||
| nodeStatus?: { [key: string]: string }; | |||||
| nodeStatus?: NodeStatus; | |||||
| }; | }; | ||||
| @@ -39,4 +39,10 @@ | |||||
| margin-right: 6px; | margin-right: 6px; | ||||
| border-radius: 50%; | border-radius: 50%; | ||||
| } | } | ||||
| &__log { | |||||
| height: 100%; | |||||
| padding: 8px; | |||||
| background: white; | |||||
| } | |||||
| } | } | ||||
| @@ -48,14 +48,16 @@ const ExperimentDrawer = ({ | |||||
| key: '1', | key: '1', | ||||
| label: '日志详情', | label: '日志详情', | ||||
| children: ( | 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 />, | icon: <ProfileOutlined />, | ||||
| }, | }, | ||||
| @@ -90,7 +90,7 @@ function LogGroup({ | |||||
| start_time: startTime, | start_time: startTime, | ||||
| }; | }; | ||||
| const res = await getExperimentPodsLog(params); | const res = await getExperimentPodsLog(params); | ||||
| const { log_detail } = res.data; | |||||
| const { log_detail } = res.data || {}; | |||||
| if (log_detail) { | if (log_detail) { | ||||
| setLogList((oldList) => oldList.concat(log_detail)); | setLogList((oldList) => oldList.concat(log_detail)); | ||||
| @@ -1,7 +1,7 @@ | |||||
| .log-list { | .log-list { | ||||
| height: 100%; | height: 100%; | ||||
| padding: 8px; | |||||
| overflow-y: auto; | overflow-y: auto; | ||||
| background: #19253b; | |||||
| &__empty { | &__empty { | ||||
| padding: 15px; | padding: 15px; | ||||
| @@ -12,4 +12,8 @@ | |||||
| word-break: break-all; | word-break: break-all; | ||||
| background: #19253b; | background: #19253b; | ||||
| } | } | ||||
| &::-webkit-scrollbar-thumb { | |||||
| background: rgba(255, 255, 255, 0.5); | |||||
| } | |||||
| } | } | ||||
| @@ -114,3 +114,13 @@ export type ComputingResource = { | |||||
| standard: string; | standard: string; | ||||
| create_by: string; | create_by: string; | ||||
| }; | }; | ||||
| // 实验运行节点状态 | |||||
| export type NodeStatus = { | |||||
| id: string; // workflow Id | |||||
| displayName: string; | |||||
| name: string; | |||||
| phase: ExperimentStatus; | |||||
| startedAt: string; | |||||
| finishedAt: string; | |||||
| }; | |||||