import KFIcon from '@/components/KFIcon'; import { ExperimentStatus } from '@/enums'; import { getRayInsReq } from '@/services/hyperParameter'; 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 ExperimentHistory from '../components/ExperimentHistory'; import ExperimentLog from '../components/ExperimentLog'; import ExperimentResult from '../components/ExperimentResult'; import HyperParameterBasic from '../components/HyperParameterBasic'; import { HyperParameterData, HyperParameterInstanceData } from '../types'; import styles from './index.less'; enum TabKeys { Params = 'params', Log = 'log', Result = 'result', History = 'history', } const NodePrefix = 'workflow'; function HyperParameterInstance() { const [experimentInfo, setExperimentInfo] = useState(undefined); const [instanceInfo, setInstanceInfo] = useState( undefined, ); // 超参数寻优运行有3个节点,运行状态取工作流状态,而不是 auto-hpo 节点状态 const [workflowStatus, setWorkflowStatus] = useState(undefined); const [nodes, setNodes] = useState | undefined>(undefined); const params = useParams(); const instanceId = safeInvoke(Number)(params.id); const evtSourceRef = useRef(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(getRayInsReq(instanceId)); if (res && res.data) { const info = res.data as HyperParameterInstanceData; const { param, node_status, argo_ins_name, argo_ins_ns, status } = info; // 解析配置参数 const paramJson = parseJsonText(param).data; if (paramJson) { // 实例详情返回的参数是字符串,需要转换 if (typeof paramJson.parameters === 'string') { paramJson.parameters = parseJsonText(paramJson.parameters); } if (!Array.isArray(paramJson.parameters)) { paramJson.parameters = []; } // 实例详情返回的运行参数是字符串,需要转换 if (typeof paramJson.points_to_evaluate === 'string') { paramJson.points_to_evaluate = parseJsonText(paramJson.points_to_evaluate); } if (!Array.isArray(paramJson.points_to_evaluate)) { paramJson.points_to_evaluate = []; } setExperimentInfo({ ...paramJson, }); } 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) { // 设置节点 setNodes(nodes); // 设置总 workflow 状态 const workflowStatus = Object.values(nodes).find((node: any) => node.displayName.startsWith(NodePrefix), ) as NodeStatus; 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: , children: ( ), }, { key: TabKeys.Log, label: '日志', icon: , children: (
{instanceInfo && nodes && }
), }, ]; const resultTabItems = [ { key: TabKeys.Result, label: '实验结果', icon: , children: , }, { key: TabKeys.History, label: '寻优列表', icon: , children: , }, ]; const tabItems = instanceInfo?.status === ExperimentStatus.Succeeded ? [...basicTabItems, ...resultTabItems] : basicTabItems; return (
); } export default HyperParameterInstance;