diff --git a/react-ui/src/components/BasicInfo/index.less b/react-ui/src/components/BasicInfo/index.less
index dd4d1ab1..53fcb46c 100644
--- a/react-ui/src/components/BasicInfo/index.less
+++ b/react-ui/src/components/BasicInfo/index.less
@@ -16,6 +16,7 @@
&__label {
position: relative;
+ flex: none;
color: @text-color-secondary;
text-align: justify;
text-align-last: justify;
@@ -26,6 +27,13 @@
}
}
+ &__list-value {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ gap: 5px 0;
+ }
+
&__value {
flex: 1;
margin-left: 16px;
diff --git a/react-ui/src/components/BasicInfo/index.tsx b/react-ui/src/components/BasicInfo/index.tsx
index 8cfee0ae..c3358af2 100644
--- a/react-ui/src/components/BasicInfo/index.tsx
+++ b/react-ui/src/components/BasicInfo/index.tsx
@@ -1,14 +1,17 @@
-import { isEmptyString } from '@/utils';
import { Link } from '@umijs/max';
import classNames from 'classnames';
import './index.less';
+export type BasicInfoLink = {
+ value: string;
+ link?: string;
+ url?: string;
+};
+
export type BasicInfoData = {
label: string;
value?: any;
- link?: string;
- externalLink?: string;
- format?: (_value?: any) => string | undefined;
+ format?: (_value?: any) => string | BasicInfoLink | BasicInfoLink[] | undefined;
};
type BasicInfoProps = {
@@ -33,32 +36,23 @@ type BasicInfoItemProps = {
labelWidth?: number;
};
function BasicInfoItem({ data, labelWidth = 100 }: BasicInfoItemProps) {
- const { label, value, externalLink, link, format } = data;
- const showValue = format ? format(value) : value;
+ const { label, value, format } = data;
+ const formatValue = format ? format(value) : value;
let valueComponent = undefined;
- if (externalLink && showValue) {
+ if (Array.isArray(formatValue)) {
valueComponent = (
-
- {showValue}
-
+
+ {formatValue.map((item: BasicInfoLink) => (
+
+ ))}
+
);
- } else if (link && showValue) {
+ } else if (typeof formatValue === 'object' && formatValue) {
valueComponent = (
-
- {showValue}
-
+
);
} else {
- valueComponent = (
-
- {isEmptyString(showValue) ? '--' : showValue}
-
- );
+ valueComponent = ;
}
return (
@@ -70,4 +64,35 @@ function BasicInfoItem({ data, labelWidth = 100 }: BasicInfoItemProps) {
);
}
+type BasicInfoItemValueProps = {
+ value: string;
+ link?: string;
+ url?: string;
+};
+
+function BasicInfoItemValue({ value, link, url }: BasicInfoItemValueProps) {
+ if (url && value) {
+ return (
+
+ {value}
+
+ );
+ } else if (link && value) {
+ return (
+
+ {value}
+
+ );
+ } else {
+ return (
+
{value || '--'}
+ );
+ }
+}
+
export default BasicInfo;
diff --git a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
index 08549ab6..7489666b 100644
--- a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
+++ b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
@@ -1,6 +1,14 @@
import BasicInfo, { BasicInfoData } from '@/components/BasicInfo';
import SubAreaTitle from '@/components/SubAreaTitle';
-import { DatasetData, ModelData, ResourceType } from '@/pages/Dataset/config';
+import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo';
+import {
+ DataSource,
+ DatasetData,
+ ModelData,
+ ProjectDependency,
+ ResourceType,
+ TrainTask,
+} from '@/pages/Dataset/config';
import styles from './index.less';
type ResourceIntroProps = {
@@ -8,29 +16,80 @@ type ResourceIntroProps = {
info: DatasetData | ModelData;
};
-// const formatArray = (arr?: ResourceData[]) => {
-// if (!arr || arr.length === 0) {
-// return '--';
-// }
-// return arr.map((item) => item.name).join('\n');
-// };
+const formatDataset = (datasets?: DatasetData[]) => {
+ if (!datasets || datasets.length === 0) {
+ return undefined;
+ }
+ return datasets.map((item) => ({
+ value: item.name,
+ url: `${origin}/dataset/dataset/info/${item.id}?tab=${ResourceInfoTabKeys.Version}&version=${item.version}&name=${item.name}&owner=${item.owner}&identifier=${item.identifier}`,
+ }));
+};
-const formatDataset = (arr?: DatasetData[]) => {
- if (!arr || arr.length === 0) {
+const formatParams = (map?: Record
, space: string = '') => {
+ if (!map || Object.keys(map).length === 0) {
return undefined;
}
- return arr.map((item) => item.name).join('\n');
+ return Object.entries(map)
+ .map(([key, value]) => `${space}${key} : ${value}`)
+ .join('\n');
};
-const formatMap = (map?: Record) => {
+const formatMetrics = (map?: Record) => {
if (!map || Object.keys(map).length === 0) {
return undefined;
}
return Object.entries(map)
- .map(([key, value]) => `${key} = ${value}`)
+ .map(([key, value]) => {
+ if (typeof value === 'object' && value !== null) {
+ return `${key} : \n${formatParams(value, ' ')}`;
+ }
+ return `${key} : ${value}`;
+ })
.join('\n');
};
+const getProjectUrl = (project?: ProjectDependency) => {
+ if (!project || !project.url || !project.branch) {
+ return undefined;
+ }
+ const { url, branch } = project;
+ if (url.endsWith('.git')) {
+ return `${url.substring(0, url.length - 4)}/tree/${branch}`;
+ }
+};
+
+const formatProject = (project?: ProjectDependency) => {
+ if (!project) {
+ return undefined;
+ }
+ return {
+ value: project.name,
+ url: getProjectUrl(project),
+ };
+};
+
+const formatTrainTask = (task?: TrainTask) => {
+ if (!task) {
+ return undefined;
+ }
+ return {
+ value: task.name,
+ url: `${origin}/pipeline/experiment/instance/${task.workflow_id}/${task.ins_id}`,
+ };
+};
+
+const formatSource = (source?: string) => {
+ if (source === DataSource.Create) {
+ return '用户上传';
+ } else if (source === DataSource.HandExport) {
+ return '手动导入';
+ } else if (source === DataSource.AtuoExport) {
+ return '自动导入';
+ }
+ return source;
+};
+
const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [
{
label: '数据集名称',
@@ -51,6 +110,12 @@ const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [
{
label: '数据来源',
value: data.dataset_source,
+ format: formatSource,
+ },
+ {
+ label: '训练任务',
+ value: data.train_task,
+ format: formatTrainTask,
},
{
label: '处理代码',
@@ -97,7 +162,8 @@ const getModelDatas = (data: ModelData): BasicInfoData[] => [
},
{
label: '训练代码',
- value: data.code,
+ value: data.project_depency,
+ format: formatProject,
},
{
label: '训练数据集',
@@ -112,24 +178,22 @@ const getModelDatas = (data: ModelData): BasicInfoData[] => [
{
label: '参数',
value: data.params,
- format: formatMap,
+ format: formatParams,
},
{
label: '指标',
value: data.metrics,
- format: formatMap,
- },
- {
- label: '训练任务',
- value: data.train_task,
- format: (value?: any) => value?.name,
- externalLink: data.train_task
- ? `${location.origin}/pipeline/experiment/instance/${data.train_task.task_id}/${data.train_task.ins_id}`
- : '',
+ format: formatMetrics,
},
{
label: '模型来源',
value: data.model_source,
+ format: formatSource,
+ },
+ {
+ label: '训练任务',
+ value: data.train_task,
+ format: formatTrainTask,
},
{
label: '模型框架',
diff --git a/react-ui/src/pages/Dataset/config.tsx b/react-ui/src/pages/Dataset/config.tsx
index 56c88832..9c4d6e47 100644
--- a/react-ui/src/pages/Dataset/config.tsx
+++ b/react-ui/src/pages/Dataset/config.tsx
@@ -22,7 +22,8 @@ export enum ResourceType {
}
export enum DataSource {
- Export = 'export', // 导出
+ AtuoExport = 'auto_export', // 自动导出
+ HandExport = 'hand_export', // 手动导出
Create = 'add', // 新增
}
@@ -137,6 +138,7 @@ export type CategoryData = {
// 数据集、模型列表数据
export interface ResourceData {
+ resourceType: ResourceType.Dataset | ResourceType.Model; // 用于 ts 类型判断
id: number;
name: string;
identifier: string;
@@ -150,7 +152,7 @@ export interface ResourceData {
version_desc?: string;
usage?: string;
relative_paths?: string;
- resourceType: ResourceType.Dataset | ResourceType.Model;
+ train_task?: TrainTask; // 训练任务
}
// 数据集数据
@@ -174,7 +176,7 @@ export interface ModelData extends ResourceData {
test_datasets?: DatasetData[]; // 测试数据集
params?: Record; // 参数
metrics?: Record; // 指标
- train_task?: TrainTask; // 训练任务
+ project_depency?: ProjectDependency; // 项目依赖
model_source?: string; // 模型来源
model_version_vos?: ResourceFileData[];
}
@@ -199,5 +201,13 @@ export type ResourceFileData = {
export type TrainTask = {
ins_id: number;
name: string;
- task_id: string;
+ experiment_id: number;
+ workflow_id: number;
+};
+
+// 项目依赖
+export type ProjectDependency = {
+ url: string;
+ name: string;
+ branch: string;
};
diff --git a/react-ui/src/pages/Experiment/Info/index.jsx b/react-ui/src/pages/Experiment/Info/index.jsx
index 43ca2a87..d02ae8f8 100644
--- a/react-ui/src/pages/Experiment/Info/index.jsx
+++ b/react-ui/src/pages/Experiment/Info/index.jsx
@@ -504,6 +504,9 @@ function ExperimentText() {
key={experimentNodeData.id}
open={propsDrawerOpen}
onClose={closePropsDrawer}
+ pipelineId={Number(locationParams.workflowId)}
+ experimentId={experimentIns.experiment_id}
+ experimentName={experimentIns.experiment_name}
instanceId={experimentIns.id}
instanceName={experimentIns.argo_ins_name}
instanceNamespace={experimentIns.argo_ins_ns}
diff --git a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
index f93950fa..48cd0064 100644
--- a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
+++ b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
@@ -13,6 +13,9 @@ import styles from './index.less';
type ExperimentDrawerProps = {
open: boolean;
onClose: () => void;
+ pipelineId: number; // 流水线 id
+ experimentId: number; // 实验 id
+ experimentName: string; // 实验 name
instanceId: number; // 实验实例 id
instanceName: string; // 实验实例 name
instanceNamespace: string; // 实验实例 namespace
@@ -26,6 +29,9 @@ type ExperimentDrawerProps = {
const ExperimentDrawer = ({
open,
onClose,
+ pipelineId,
+ experimentId,
+ experimentName,
instanceId,
instanceName,
instanceNamespace,
@@ -64,6 +70,9 @@ const ExperimentDrawer = ({
label: '输出结果',
children: (
diff --git a/react-ui/src/pages/Experiment/components/ExperimentResult/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentResult/index.tsx
index 046c2afe..6c770618 100644
--- a/react-ui/src/pages/Experiment/components/ExperimentResult/index.tsx
+++ b/react-ui/src/pages/Experiment/components/ExperimentResult/index.tsx
@@ -8,6 +8,9 @@ import ExportModelModal from '../ExportModelModal';
import styles from './index.less';
type ExperimentResultProps = {
+ pipelineId: number; // 流水线 id
+ experimentId: number; // 实验 id
+ experimentName: string; // 实验 name
experimentInsId: number; // 实验实例 id
pipelineNodeId: string; // 流水线节点 id
};
@@ -22,7 +25,13 @@ type ExperimentResultData = {
}[];
};
-function ExperimentResult({ experimentInsId, pipelineNodeId }: ExperimentResultProps) {
+function ExperimentResult({
+ pipelineId,
+ experimentId,
+ experimentName,
+ experimentInsId,
+ pipelineNodeId,
+}: ExperimentResultProps) {
const { message } = App.useApp();
const [experimentResults, setExperimentResults] = useState([]);
@@ -46,6 +55,11 @@ function ExperimentResult({ experimentInsId, pipelineNodeId }: ExperimentResultP
// 导出到模型库
const exportToModel = (path: string) => {
const { close } = openAntdModal(ExportModelModal, {
+ pipelineId,
+ experimentId,
+ experimentName,
+ experimentInsId,
+ pipelineNodeId,
path,
onOk: () => {
message.success('导出成功');
diff --git a/react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx b/react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx
index cb3eda77..5667a388 100644
--- a/react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx
+++ b/react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx
@@ -1,12 +1,7 @@
import editExperimentIcon from '@/assets/img/edit-experiment.png';
import KFModal from '@/components/KFModal';
-import { ResourceVersionData, type ResourceData } from '@/pages/Dataset/config';
-import {
- addModelVersion,
- exportModelReq,
- getModelList,
- getModelVersionList,
-} from '@/services/dataset';
+import { DataSource, ResourceVersionData, type ResourceData } from '@/pages/Dataset/config';
+import { addModelVersion, getModelList, getModelVersionList } from '@/services/dataset';
import { to } from '@/utils/promise';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Form, Input, ModalProps, Select } from 'antd';
@@ -15,34 +10,48 @@ import { useEffect, useState } from 'react';
import styles from './index.less';
type FormData = {
- models_id: string;
+ id: number;
version: string;
- description: string;
+ version_desc: string;
};
-type ExportModelResponce = {
- fileName: string;
- fileSize: string;
- url: string;
-};
+// type ExportModelResponce = {
+// fileName: string;
+// fileSize: string;
+// url: string;
+// };
-type CreateModelVersionParams = FormData & {
- file_name: string;
- file_size: string;
- url: string;
- // name: string;
-};
+// type CreateModelVersionParams = FormData & {
+// file_name: string;
+// file_size: string;
+// url: string;
+// // name: string;
+// };
interface ExportModelModalProps extends Omit {
- path: string;
+ pipelineId: number; // 流水线 id
+ experimentId: number; // 实验 id
+ experimentName: string; // 实验 name
+ experimentInsId: number; // 实验实例 id
+ pipelineNodeId: string; // 流水线节点 id
+ path: string; // 文件路径
onOk: () => void;
}
-function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) {
+function ExportModelModal({
+ pipelineId,
+ experimentId,
+ experimentName,
+ experimentInsId,
+ pipelineNodeId,
+ path,
+ onOk,
+ ...rest
+}: ExportModelModalProps) {
const [form] = Form.useForm();
const [models, setModels] = useState([]);
const [versions, setVersions] = useState([]);
- const [uuid] = useState(Date.now());
+ // const [uuid] = useState(Date.now());
const layout = {
labelCol: { span: 24 },
@@ -53,10 +62,19 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) {
requestModelList();
}, []);
- // 模型版本tooltip
+ // 获取选中的模型
+ const getSelectedModel = (id: number | undefined) => {
+ if (id) {
+ return models.find((item) => item.id === id);
+ }
+ return undefined;
+ };
+
+ // 模型版本 tooltip
const getTooltip = () => {
- const id = form.getFieldValue('models_id');
- const name = models.find((item) => item.id === id)?.name ?? '';
+ const id = form.getFieldValue('id');
+ const model = getSelectedModel(id);
+ const name = model?.name ?? '';
const versionNames = versions.map((item: ResourceVersionData) => item.name).join('、');
const tooltip =
versions.length > 0 ? `${name}有以下版本:\n${versionNames}\n注意不能重复` : undefined;
@@ -87,7 +105,7 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) {
// 获取模型版本列表
const getModelVersions = async (id: number) => {
- const model = models.find((item) => item.id === id);
+ const model = getSelectedModel(id);
if (!model) {
return;
}
@@ -104,26 +122,26 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) {
// 导出到模型
const exportToModel = async (formData: FormData) => {
+ const id = form.getFieldValue('id');
+ const model = getSelectedModel(id);
const params = {
- uuid: String(uuid),
- path,
+ ...formData,
+ identifier: model?.identifier,
+ name: model?.name,
+ model_source: DataSource.HandExport,
+ train_task: {
+ workflow_id: pipelineId,
+ experiment_id: experimentId,
+ name: experimentName,
+ ins_id: experimentInsId,
+ task_id: pipelineNodeId,
+ },
+ model_version_vos: [
+ {
+ url: path,
+ },
+ ],
};
- const [res] = await to(exportModelReq(params));
- if (res && res.data) {
- const files = res.data as ExportModelResponce[];
- const params: CreateModelVersionParams[] = files.map((item) => ({
- ...formData,
- file_name: item.fileName,
- file_size: item.fileSize,
- url: item.url,
- }));
-
- createModelVersion(params);
- }
- };
-
- // 创建模型版本
- const createModelVersion = async (params: CreateModelVersionParams[]) => {
const [res] = await to(addModelVersion(params));
if (res) {
onOk();
@@ -152,11 +170,7 @@ function ExportModelModal({ path, onOk, ...rest }: ExportModelModalProps) {
labelAlign="left"
labelWrap
>
-
+
-
+ {/*
模型大小:
{data.model_meta.file_size || '--'}
-
+
*/}
创建时间:
@@ -126,8 +126,12 @@ function DatasetInfo({ data }: { data: TrainDataset }) {
function ProjectInfo({ data }: { data: ProjectDependency }) {
const gotoProjectPage = () => {
- const { url } = data;
- window.open(url, '_blank');
+ const { url, branch } = data;
+ let projectUrl = url;
+ if (url.endsWith('.git')) {
+ projectUrl = `${url.substring(0, url.length - 4)}/tree/${branch}`;
+ }
+ window.open(projectUrl, '_blank');
};
return (