You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 6.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import KFIcon from '@/components/KFIcon';
  2. import { ExperimentStatus } from '@/enums';
  3. import { getActiveLearnInsReq } from '@/services/activeLearn';
  4. import { NodeStatus } from '@/types';
  5. import { parseJsonText } from '@/utils';
  6. import { safeInvoke } from '@/utils/functional';
  7. import { to } from '@/utils/promise';
  8. import { useParams } from '@umijs/max';
  9. import { Tabs } from 'antd';
  10. import { useEffect, useRef, useState } from 'react';
  11. import ActiveLearnBasic from '../components/ActiveLearnBasic';
  12. import ExperimentHistory from '../components/ExperimentHistory';
  13. import ExperimentLog from '../components/ExperimentLog';
  14. import ExperimentResult from '../components/ExperimentResult';
  15. import { ActiveLearnData, ActiveLearnInstanceData } from '../types';
  16. import styles from './index.less';
  17. enum TabKeys {
  18. Params = 'params',
  19. Log = 'log',
  20. Result = 'result',
  21. History = 'history',
  22. }
  23. const NodePrefix = 'workflow';
  24. function ActiveLearnInstance() {
  25. const [experimentInfo, setExperimentInfo] = useState<ActiveLearnData | undefined>(undefined);
  26. const [instanceInfo, setInstanceInfo] = useState<ActiveLearnInstanceData | undefined>(undefined);
  27. // 超参数寻优运行有3个节点,运行状态取工作流状态,而不是 auto-hpo 节点状态
  28. const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
  29. const [nodes, setNodes] = useState<Record<string, NodeStatus> | undefined>(undefined);
  30. const params = useParams();
  31. const instanceId = safeInvoke(Number)(params.id);
  32. const evtSourceRef = useRef<EventSource | null>(null);
  33. useEffect(() => {
  34. if (instanceId) {
  35. getExperimentInsInfo(false);
  36. }
  37. return () => {
  38. closeSSE();
  39. };
  40. // eslint-disable-next-line react-hooks/exhaustive-deps
  41. }, [instanceId]);
  42. // 获取实验实例详情
  43. const getExperimentInsInfo = async (isStatusDetermined: boolean) => {
  44. const [res] = await to(getActiveLearnInsReq(instanceId));
  45. if (res && res.data) {
  46. const info = res.data as ActiveLearnInstanceData;
  47. const { param, node_status, argo_ins_name, argo_ins_ns, status } = info;
  48. // 解析配置参数
  49. const paramJson = parseJsonText(param);
  50. if (paramJson) {
  51. setExperimentInfo(paramJson.data);
  52. }
  53. setInstanceInfo(info);
  54. // 这个接口返回的状态有延时,SSE 返回的状态是最新的
  55. // SSE 调用时,不需要解析 node_status,也不要重新建立 SSE
  56. if (isStatusDetermined) {
  57. return;
  58. }
  59. // 进行节点状态
  60. const nodeStatusJson = parseJsonText(node_status);
  61. if (nodeStatusJson) {
  62. setNodes(nodeStatusJson);
  63. Object.keys(nodeStatusJson).some((key) => {
  64. if (key.startsWith(NodePrefix)) {
  65. const workflowStatus = nodeStatusJson[key];
  66. setWorkflowStatus(workflowStatus);
  67. return true;
  68. }
  69. return false;
  70. });
  71. }
  72. // 运行中或者等待中,开启 SSE
  73. if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) {
  74. setupSSE(argo_ins_name, argo_ins_ns);
  75. }
  76. }
  77. };
  78. const setupSSE = (name: string, namespace: string) => {
  79. const { origin } = location;
  80. const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
  81. const evtSource = new EventSource(
  82. `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
  83. { withCredentials: false },
  84. );
  85. evtSource.onmessage = (event) => {
  86. const data = event?.data;
  87. if (!data) {
  88. return;
  89. }
  90. const dataJson = parseJsonText(data);
  91. if (dataJson) {
  92. const nodes = dataJson?.result?.object?.status?.nodes;
  93. if (nodes) {
  94. const workflowStatus = Object.values(nodes).find((node: any) =>
  95. node.displayName.startsWith(NodePrefix),
  96. ) as NodeStatus;
  97. // 节点
  98. setNodes(nodes);
  99. // 设置工作流状态
  100. if (workflowStatus) {
  101. setWorkflowStatus(workflowStatus);
  102. // 实验结束,关闭 SSE
  103. if (
  104. workflowStatus.phase !== ExperimentStatus.Pending &&
  105. workflowStatus.phase !== ExperimentStatus.Running
  106. ) {
  107. closeSSE();
  108. getExperimentInsInfo(true);
  109. }
  110. }
  111. }
  112. }
  113. };
  114. evtSource.onerror = (error) => {
  115. console.error('SSE error: ', error);
  116. };
  117. evtSourceRef.current = evtSource;
  118. };
  119. const closeSSE = () => {
  120. if (evtSourceRef.current) {
  121. evtSourceRef.current.close();
  122. evtSourceRef.current = null;
  123. }
  124. };
  125. const basicTabItems = [
  126. {
  127. key: TabKeys.Params,
  128. label: '基本信息',
  129. icon: <KFIcon type="icon-jibenxinxi" />,
  130. children: (
  131. <ActiveLearnBasic
  132. className={styles['active-learn-instance__basic']}
  133. info={experimentInfo}
  134. runStatus={workflowStatus}
  135. isInstance
  136. />
  137. ),
  138. },
  139. {
  140. key: TabKeys.Log,
  141. label: '日志',
  142. icon: <KFIcon type="icon-rizhi1" />,
  143. children: (
  144. <div className={styles['active-learn-instance__log']}>
  145. {instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
  146. </div>
  147. ),
  148. },
  149. ];
  150. const resultTabItems = [
  151. {
  152. key: TabKeys.Result,
  153. label: '实验结果',
  154. icon: <KFIcon type="icon-shiyanjieguo1" />,
  155. children: <ExperimentResult fileUrl={instanceInfo?.result_txt} />,
  156. },
  157. {
  158. key: TabKeys.History,
  159. label: '寻优列表',
  160. icon: <KFIcon type="icon-Trialliebiao" />,
  161. children: <ExperimentHistory trialList={instanceInfo?.trial_list ?? []} />,
  162. },
  163. ];
  164. const tabItems =
  165. instanceInfo?.status === ExperimentStatus.Succeeded
  166. ? [...basicTabItems, ...resultTabItems]
  167. : basicTabItems;
  168. return (
  169. <div className={styles['active-learn-instance']}>
  170. <Tabs className={styles['active-learn-instance__tabs']} items={tabItems} />
  171. </div>
  172. );
  173. }
  174. export default ActiveLearnInstance;