|
- import KFIcon from '@/components/KFIcon';
- import { AutoMLTaskType, AutoMLType, ExperimentStatus } from '@/enums';
- 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, useRef, useState } from 'react';
- import AutoMLBasic from '../components/AutoMLBasic';
- import ExperimentHistory from '../components/ExperimentHistory';
- import ExperimentLog from '../components/ExperimentLog';
- import ExperimentResult from '../components/ExperimentResult';
- import TensorBoard from '../components/TensorBoard';
- import { AutoMLData, AutoMLInstanceData } from '../types';
- import styles from './index.less';
-
- enum TabKeys {
- Params = 'params',
- Log = 'log',
- Result = 'result',
- History = 'history',
- Visual = 'Visual',
- }
-
- const NodePrefix = 'workflow';
-
- function AutoMLInstance() {
- const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined);
- const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(undefined);
- const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
- const [nodes, setNodes] = useState<Record<string, NodeStatus> | undefined>(undefined);
- const [type, setType] = useState<string | undefined>(undefined);
- const params = useParams();
- const instanceId = safeInvoke(Number)(params.id);
- const evtSourceRef = useRef<EventSource | null>(null);
-
- useEffect(() => {
- if (instanceId) {
- getExperimentInsInfo(false);
- }
- return () => {
- closeSSE();
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [instanceId]);
-
- // 获取实验实例详情
- const getExperimentInsInfo = async (isStatusDetermined: boolean) => {
- const [res] = await to(getExperimentInsReq(instanceId));
- if (res && res.data) {
- const info = res.data as AutoMLInstanceData;
- const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time, type } = info;
-
- setType(type);
- // 解析配置参数
- const paramJson = parseJsonText(param);
- if (paramJson) {
- setAutoMLInfo({
- ...paramJson.data,
- create_time,
- type,
- });
- }
-
- setInstanceInfo(info);
-
- // 这个接口返回的状态有延时,SSE 返回的状态是最新的
- // SSE 调用时,不需要解析 node_status, 也不要重新建立 SSE
- if (isStatusDetermined) {
- return;
- }
-
- // 进行节点状态
- const nodeStatusJson = parseJsonText(node_status);
- if (nodeStatusJson) {
- setNodes(nodeStatusJson);
- // 工作流
- Object.keys(nodeStatusJson).some((key) => {
- if (key.startsWith(NodePrefix)) {
- const workflowStatus = nodeStatusJson[key];
- setWorkflowStatus(workflowStatus);
- return true;
- }
- return false;
- });
- }
-
- // 运行中或者等待中,开启 SSE
- if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) {
- setupSSE(argo_ins_name, argo_ins_ns);
- }
- }
- };
-
- const setupSSE = (name: string, namespace: string) => {
- const { origin } = location;
- 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 workflowStatus = Object.values(nodes).find((node: any) =>
- node.displayName.startsWith(NodePrefix),
- ) as NodeStatus;
-
- // 节点
- setNodes(nodes);
-
- if (workflowStatus) {
- setWorkflowStatus(workflowStatus);
-
- // 实验结束,关闭 SSE
- if (
- workflowStatus.phase !== ExperimentStatus.Pending &&
- workflowStatus.phase !== ExperimentStatus.Running
- ) {
- closeSSE();
- getExperimentInsInfo(true);
- }
- }
- }
- }
- };
- 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: '基本信息',
- icon: <KFIcon type="icon-jibenxinxi" />,
- children: (
- <AutoMLBasic
- className={styles['auto-ml-instance__basic']}
- info={autoMLInfo}
- runStatus={workflowStatus}
- isInstance
- />
- ),
- },
- {
- key: TabKeys.Log,
- label: '日志',
- icon: <KFIcon type="icon-rizhi1" />,
- children: (
- <div className={styles['auto-ml-instance__log']}>
- {instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
- </div>
- ),
- },
- ];
-
- const resultTabItems = [
- {
- key: TabKeys.Result,
- label: '实验结果',
- icon: <KFIcon type="icon-shiyanjieguo1" />,
- children: (
- <ExperimentResult
- type={type}
- fileUrl={instanceInfo?.result_path}
- imageUrl={instanceInfo?.img_path}
- modelPath={instanceInfo?.model_path}
- />
- ),
- },
- type === AutoMLType.Text
- ? {
- key: TabKeys.Visual,
- label: '可视化结果',
- icon: <KFIcon type="icon-Trialliebiao" />,
- children: (
- <TensorBoard
- path={instanceInfo?.run_history_path}
- namespace={instanceInfo?.argo_ins_ns}
- />
- ),
- }
- : {
- key: TabKeys.History,
- label: '试验列表',
- icon: <KFIcon type="icon-Trialliebiao" />,
- children: (
- <ExperimentHistory
- calcMetrics={autoMLInfo?.scoring_functions}
- 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']}>
- <Tabs className={styles['auto-ml-instance__tabs']} items={tabItems} />
- </div>
- );
- }
-
- export default AutoMLInstance;
|