|
- 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<HyperParameterData | undefined>(undefined);
- const [instanceInfo, setInstanceInfo] = useState<HyperParameterInstanceData | undefined>(
- undefined,
- );
- // 超参数寻优运行有3个节点,运行状态取工作流状态,而不是 auto-hpo 节点状态
- const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
- const [nodes, setNodes] = useState<Record<string, NodeStatus> | 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(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: <KFIcon type="icon-jibenxinxi" />,
- children: (
- <HyperParameterBasic
- className={styles['hyper-parameter-instance__basic']}
- info={experimentInfo}
- workflowStatus={workflowStatus}
- instanceStatus={instanceInfo?.status as ExperimentStatus}
- isInstance
- />
- ),
- },
- {
- key: TabKeys.Log,
- label: '日志',
- icon: <KFIcon type="icon-rizhi1" />,
- children: (
- <div className={styles['hyper-parameter-instance__log']}>
- {instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
- </div>
- ),
- },
- ];
-
- const resultTabItems = [
- {
- key: TabKeys.Result,
- label: '实验结果',
- icon: <KFIcon type="icon-shiyanjieguo1" />,
- children: <ExperimentResult fileUrl={instanceInfo?.result_txt} />,
- },
- {
- key: TabKeys.History,
- label: '寻优列表',
- icon: <KFIcon type="icon-Trialliebiao" />,
- children: <ExperimentHistory trialList={instanceInfo?.trial_list ?? []} />,
- },
- ];
-
- const tabItems =
- instanceInfo?.status === ExperimentStatus.Succeeded
- ? [...basicTabItems, ...resultTabItems]
- : basicTabItems;
-
- return (
- <div className={styles['hyper-parameter-instance']}>
- <Tabs className={styles['hyper-parameter-instance__tabs']} items={tabItems} />
- </div>
- );
- }
-
- export default HyperParameterInstance;
|