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.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import KFIcon from '@/components/KFIcon';
  2. import { AutoMLTaskType, AutoMLType, ExperimentStatus } from '@/enums';
  3. import { getExperimentInsReq } from '@/services/autoML';
  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 AutoMLBasic from '../components/AutoMLBasic';
  12. import ExperimentHistory from '../components/ExperimentHistory';
  13. import ExperimentLog from '../components/ExperimentLog';
  14. import ExperimentResult from '../components/ExperimentResult';
  15. import TensorBoard from '../components/TensorBoard';
  16. import { AutoMLData, AutoMLInstanceData } from '../types';
  17. import styles from './index.less';
  18. enum TabKeys {
  19. Params = 'params',
  20. Log = 'log',
  21. Result = 'result',
  22. History = 'history',
  23. Visual = 'Visual',
  24. }
  25. const NodePrefix = 'workflow';
  26. function AutoMLInstance() {
  27. const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined);
  28. const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(undefined);
  29. const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
  30. const [nodes, setNodes] = useState<Record<string, NodeStatus> | undefined>(undefined);
  31. const [type, setType] = useState<string | undefined>(undefined);
  32. const params = useParams();
  33. const instanceId = safeInvoke(Number)(params.id);
  34. const evtSourceRef = useRef<EventSource | null>(null);
  35. useEffect(() => {
  36. if (instanceId) {
  37. getExperimentInsInfo(false);
  38. }
  39. return () => {
  40. closeSSE();
  41. };
  42. // eslint-disable-next-line react-hooks/exhaustive-deps
  43. }, [instanceId]);
  44. // 获取实验实例详情
  45. const getExperimentInsInfo = async (isStatusDetermined: boolean) => {
  46. const [res] = await to(getExperimentInsReq(instanceId));
  47. if (res && res.data) {
  48. const info = res.data as AutoMLInstanceData;
  49. const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time, type } = info;
  50. setType(type);
  51. // 解析配置参数
  52. const paramJson = parseJsonText(param);
  53. if (paramJson) {
  54. setAutoMLInfo({
  55. ...paramJson.data,
  56. create_time,
  57. type,
  58. });
  59. }
  60. setInstanceInfo(info);
  61. // 这个接口返回的状态有延时,SSE 返回的状态是最新的
  62. // SSE 调用时,不需要解析 node_status, 也不要重新建立 SSE
  63. if (isStatusDetermined) {
  64. return;
  65. }
  66. // 进行节点状态
  67. const nodeStatusJson = parseJsonText(node_status);
  68. if (nodeStatusJson) {
  69. setNodes(nodeStatusJson);
  70. // 工作流
  71. Object.keys(nodeStatusJson).some((key) => {
  72. if (key.startsWith(NodePrefix)) {
  73. const workflowStatus = nodeStatusJson[key];
  74. setWorkflowStatus(workflowStatus);
  75. return true;
  76. }
  77. return false;
  78. });
  79. }
  80. // 运行中或者等待中,开启 SSE
  81. if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) {
  82. setupSSE(argo_ins_name, argo_ins_ns);
  83. }
  84. }
  85. };
  86. const setupSSE = (name: string, namespace: string) => {
  87. const { origin } = location;
  88. const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
  89. const evtSource = new EventSource(
  90. `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
  91. { withCredentials: false },
  92. );
  93. evtSource.onmessage = (event) => {
  94. const data = event?.data;
  95. if (!data) {
  96. return;
  97. }
  98. const dataJson = parseJsonText(data);
  99. if (dataJson) {
  100. const nodes = dataJson?.result?.object?.status?.nodes;
  101. if (nodes) {
  102. const workflowStatus = Object.values(nodes).find((node: any) =>
  103. node.displayName.startsWith(NodePrefix),
  104. ) as NodeStatus;
  105. // 节点
  106. setNodes(nodes);
  107. if (workflowStatus) {
  108. setWorkflowStatus(workflowStatus);
  109. // 实验结束,关闭 SSE
  110. if (
  111. workflowStatus.phase !== ExperimentStatus.Pending &&
  112. workflowStatus.phase !== ExperimentStatus.Running
  113. ) {
  114. closeSSE();
  115. getExperimentInsInfo(true);
  116. }
  117. }
  118. }
  119. }
  120. };
  121. evtSource.onerror = (error) => {
  122. console.error('SSE error: ', error);
  123. };
  124. evtSourceRef.current = evtSource;
  125. };
  126. const closeSSE = () => {
  127. if (evtSourceRef.current) {
  128. evtSourceRef.current.close();
  129. evtSourceRef.current = null;
  130. }
  131. };
  132. const basicTabItems = [
  133. {
  134. key: TabKeys.Params,
  135. label: '基本信息',
  136. icon: <KFIcon type="icon-jibenxinxi" />,
  137. children: (
  138. <AutoMLBasic
  139. className={styles['auto-ml-instance__basic']}
  140. info={autoMLInfo}
  141. runStatus={workflowStatus}
  142. isInstance
  143. />
  144. ),
  145. },
  146. {
  147. key: TabKeys.Log,
  148. label: '日志',
  149. icon: <KFIcon type="icon-rizhi1" />,
  150. children: (
  151. <div className={styles['auto-ml-instance__log']}>
  152. {instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
  153. </div>
  154. ),
  155. },
  156. ];
  157. const resultTabItems = [
  158. {
  159. key: TabKeys.Result,
  160. label: '实验结果',
  161. icon: <KFIcon type="icon-shiyanjieguo1" />,
  162. children: (
  163. <ExperimentResult
  164. type={type}
  165. fileUrl={instanceInfo?.result_path}
  166. imageUrl={instanceInfo?.img_path}
  167. modelPath={instanceInfo?.model_path}
  168. />
  169. ),
  170. },
  171. type === AutoMLType.Text
  172. ? {
  173. key: TabKeys.Visual,
  174. label: '可视化结果',
  175. icon: <KFIcon type="icon-Trialliebiao" />,
  176. children: (
  177. <TensorBoard
  178. path={instanceInfo?.run_history_path}
  179. namespace={instanceInfo?.argo_ins_ns}
  180. />
  181. ),
  182. }
  183. : {
  184. key: TabKeys.History,
  185. label: '试验列表',
  186. icon: <KFIcon type="icon-Trialliebiao" />,
  187. children: (
  188. <ExperimentHistory
  189. calcMetrics={autoMLInfo?.scoring_functions}
  190. fileUrl={instanceInfo?.run_history_path}
  191. isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification}
  192. />
  193. ),
  194. },
  195. ];
  196. const tabItems =
  197. instanceInfo?.status === ExperimentStatus.Succeeded
  198. ? [...basicTabItems, ...resultTabItems]
  199. : basicTabItems;
  200. return (
  201. <div className={styles['auto-ml-instance']}>
  202. <Tabs className={styles['auto-ml-instance__tabs']} items={tabItems} />
  203. </div>
  204. );
  205. }
  206. export default AutoMLInstance;