| @@ -16,6 +16,7 @@ export enum IframePageType { | |||
| GitLink = 'GitLink', // git link | |||
| Aim = 'Aim', // 实验对比 | |||
| Knowledge = 'Knowledge', // 知识图谱 | |||
| TensorBoard = 'TensorBoard', // 可视化结果 | |||
| } | |||
| const getRequestAPI = (type: IframePageType): (() => Promise<any>) => { | |||
| @@ -36,12 +37,18 @@ const getRequestAPI = (type: IframePageType): (() => Promise<any>) => { | |||
| return () => | |||
| Promise.resolve({ | |||
| code: 200, | |||
| data: SessionStorage.getItem(SessionStorage.aimUrlKey) || '', | |||
| data: SessionStorage.getItem(SessionStorage.aimUrlKey), | |||
| }); | |||
| case IframePageType.Knowledge: { | |||
| case IframePageType.Knowledge: | |||
| // 知识图谱 | |||
| return () => Promise.resolve({ code: 200, data: `http://172.168.15.197:32701` }); | |||
| } | |||
| case IframePageType.TensorBoard: | |||
| // 知识图谱 | |||
| return () => | |||
| Promise.resolve({ | |||
| code: 200, | |||
| data: SessionStorage.getItem(SessionStorage.editorUrlKey), | |||
| }); | |||
| } | |||
| }; | |||
| @@ -54,7 +54,7 @@ function ExperimentHistory({ trialList, isClassification }: ExperimentHistoryPro | |||
| icon={<KFIcon type="icon-xiazai" />} | |||
| onClick={() => { | |||
| if (query_idx) { | |||
| const fileName = query_idx.split('/').slice(-1)[0]; | |||
| const fileName = query_idx.split('/').pop(); | |||
| let url = query_idx; | |||
| if (process.env.NODE_ENV === 'development') { | |||
| url = query_idx.replace('172.168.15.197:31213', 'localhost:8000'); | |||
| @@ -1,5 +1,5 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | |||
| import { AutoMLTaskType, AutoMLType, ExperimentStatus } from '@/enums'; | |||
| import { getExperimentInsReq } from '@/services/autoML'; | |||
| import { NodeStatus } from '@/types'; | |||
| import { parseJsonText } from '@/utils'; | |||
| @@ -12,6 +12,7 @@ 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'; | |||
| @@ -20,6 +21,7 @@ enum TabKeys { | |||
| Log = 'log', | |||
| Result = 'result', | |||
| History = 'history', | |||
| Visual = 'Visual', | |||
| } | |||
| const NodePrefix = 'workflow'; | |||
| @@ -29,6 +31,7 @@ function AutoMLInstance() { | |||
| 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); | |||
| @@ -49,6 +52,8 @@ function AutoMLInstance() { | |||
| 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) { | |||
| @@ -174,24 +179,37 @@ function AutoMLInstance() { | |||
| icon: <KFIcon type="icon-shiyanjieguo1" />, | |||
| children: ( | |||
| <ExperimentResult | |||
| type={type} | |||
| fileUrl={instanceInfo?.result_path} | |||
| imageUrl={instanceInfo?.img_path} | |||
| modelPath={instanceInfo?.model_path} | |||
| /> | |||
| ), | |||
| }, | |||
| { | |||
| 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} | |||
| /> | |||
| ), | |||
| }, | |||
| 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 = | |||
| @@ -9,7 +9,7 @@ | |||
| &__download { | |||
| padding-top: 16px; | |||
| padding-bottom: 16px; | |||
| margin-top: 16px; | |||
| padding-left: @content-padding; | |||
| color: @text-color; | |||
| font-size: 13px; | |||
| @@ -1,4 +1,5 @@ | |||
| import InfoGroup from '@/components/InfoGroup'; | |||
| import { AutoMLType } from '@/enums'; | |||
| import { getFileReq } from '@/services/file'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Button, Image } from 'antd'; | |||
| @@ -9,9 +10,10 @@ type ExperimentResultProps = { | |||
| fileUrl?: string; | |||
| imageUrl?: string; | |||
| modelPath?: string; | |||
| type?: string; | |||
| }; | |||
| function ExperimentResult({ fileUrl, imageUrl, modelPath }: ExperimentResultProps) { | |||
| function ExperimentResult({ fileUrl, imageUrl, modelPath, type }: ExperimentResultProps) { | |||
| const [result, setResult] = useState<string | undefined>(''); | |||
| const images = useMemo(() => { | |||
| @@ -40,31 +42,33 @@ function ExperimentResult({ fileUrl, imageUrl, modelPath }: ExperimentResultProp | |||
| <InfoGroup title="实验结果" height={420} width="100%"> | |||
| <div className={styles['experiment-result__text']}>{result}</div> | |||
| </InfoGroup> | |||
| <InfoGroup title="可视化结果" style={{ margin: '16px 0' }}> | |||
| <div className={styles['experiment-result__images']}> | |||
| <Image.PreviewGroup | |||
| preview={{ | |||
| onChange: (current, prev) => | |||
| console.log(`current index: ${current}, prev index: ${prev}`), | |||
| }} | |||
| > | |||
| {images.map((item) => ( | |||
| <Image | |||
| key={item} | |||
| className={styles['experiment-result__images__item']} | |||
| src={item} | |||
| height={248} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| ))} | |||
| </Image.PreviewGroup> | |||
| </div> | |||
| </InfoGroup> | |||
| {type === AutoMLType.Table && ( | |||
| <InfoGroup title="可视化结果" style={{ margin: '16px 0' }}> | |||
| <div className={styles['experiment-result__images']}> | |||
| <Image.PreviewGroup | |||
| preview={{ | |||
| onChange: (current, prev) => | |||
| console.log(`current index: ${current}, prev index: ${prev}`), | |||
| }} | |||
| > | |||
| {images.map((item) => ( | |||
| <Image | |||
| key={item} | |||
| className={styles['experiment-result__images__item']} | |||
| src={item} | |||
| height={248} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| ))} | |||
| </Image.PreviewGroup> | |||
| </div> | |||
| </InfoGroup> | |||
| )} | |||
| {modelPath && ( | |||
| <div className={styles['experiment-result__download']}> | |||
| <span style={{ marginRight: '12px', color: '#606b7a' }}>文件名</span> | |||
| <span>save_model.joblib</span> | |||
| <span>{modelPath.split('/').pop()} </span> | |||
| <Button | |||
| type="primary" | |||
| className={styles['experiment-result__download__btn']} | |||
| @@ -0,0 +1,42 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-09-02 08:42:57 | |||
| * @Description: 可视化 | |||
| */ | |||
| import IframePage, { IframePageType } from '@/components/IFramePage'; | |||
| import { runTensorBoardReq } from '@/services/experiment/index.js'; | |||
| import { to } from '@/utils/promise'; | |||
| import SessionStorage from '@/utils/sessionStorage'; | |||
| import { useEffect, useState } from 'react'; | |||
| type TensorBoardProps = { | |||
| namespace?: string; | |||
| path?: string; | |||
| }; | |||
| function TensorBoard({ namespace, path }: TensorBoardProps) { | |||
| const [tensorboardUrl, setTensorboardUrl] = useState(''); | |||
| useEffect(() => { | |||
| // 运行 TensorBoard | |||
| const runTensorBoard = async () => { | |||
| const params = { | |||
| namespace: namespace, | |||
| path: path, | |||
| }; | |||
| const [res] = await to(runTensorBoardReq(params)); | |||
| if (res && res.data) { | |||
| SessionStorage.setItem(SessionStorage.tensorBoardUrlKey, res.data); | |||
| setTensorboardUrl(res.data); | |||
| } | |||
| }; | |||
| if (namespace && path) { | |||
| runTensorBoard(); | |||
| } | |||
| }, [namespace, path]); | |||
| return <>{tensorboardUrl && <IframePage type={IframePageType.TensorBoard}></IframePage>}</>; | |||
| } | |||
| export default TensorBoard; | |||
| @@ -17,7 +17,6 @@ import { createEditorReq } from '@/services/developmentEnvironment'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { App, Button, Col, Form, Input, Row } from 'antd'; | |||
| import { omit, pick } from 'lodash'; | |||
| import styles from './index.less'; | |||
| type FormData = { | |||
| @@ -54,15 +53,7 @@ function EditorCreate() { | |||
| // 创建编辑器 | |||
| const createEditor = async (formData: FormData) => { | |||
| // 根据后台要求,修改表单数据 | |||
| const model = formData['model']; | |||
| const dataset = formData['dataset']; | |||
| const params = { | |||
| ...omit(formData, ['model', 'dataset']), | |||
| model: model && pick(model, ['id', 'version', 'path', 'showValue']), | |||
| dataset: dataset && pick(dataset, ['id', 'version', 'path', 'showValue']), | |||
| }; | |||
| const [res] = await to(createEditorReq(params)); | |||
| const [res] = await to(createEditorReq(formData)); | |||
| if (res) { | |||
| message.success('创建成功'); | |||
| navigate(-1); | |||
| @@ -11,6 +11,8 @@ export default class SessionStorage { | |||
| static readonly clientInfoKey = 'client-info'; | |||
| /** aim url */ | |||
| static readonly aimUrlKey = 'aim-url'; | |||
| /** tensorBoard url */ | |||
| static readonly tensorBoardUrlKey = 'tensor-board-url'; | |||
| /** | |||
| * 获取 SessionStorage 值 | |||