Browse Source

feat: 完成模型演化

pull/128/head
cp3hnu 1 year ago
parent
commit
ccfe0d981a
14 changed files with 159 additions and 225 deletions
  1. +2
    -1
      react-ui/src/components/IFramePage/index.tsx
  2. +9
    -1
      react-ui/src/components/ParameterSelect/config.tsx
  3. +4
    -1
      react-ui/src/components/ResourceSelect/index.tsx
  4. +7
    -0
      react-ui/src/pages/Dataset/components/ResourceInfo/index.less
  5. +28
    -16
      react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx
  6. +3
    -3
      react-ui/src/pages/Dataset/components/ResourceList/index.tsx
  7. +2
    -7
      react-ui/src/pages/Model/components/ModelEvolution/index.less
  8. +5
    -8
      react-ui/src/pages/Model/components/ModelEvolution/index.tsx
  9. +71
    -53
      react-ui/src/pages/Model/components/ModelEvolution/utils.tsx
  10. +2
    -0
      react-ui/src/pages/Model/components/NodeTooltips/index.less
  11. +14
    -18
      react-ui/src/pages/Model/components/NodeTooltips/index.tsx
  12. +4
    -1
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  13. +0
    -115
      react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx
  14. +8
    -1
      react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx

+ 2
- 1
react-ui/src/components/IFramePage/index.tsx View File

@@ -9,6 +9,7 @@ import {
} from '@/utils/sessionStorage';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import './index.less';

export enum IframePageType {
@@ -61,7 +62,7 @@ function IframePage({ type, className, style }: IframePageProps) {

return (
<div className={classNames('kf-iframe-page', className)} style={style}>
{loading && <KFSpin size="large" />}
{loading && createPortal(<KFSpin size="large" />, document.body)}
<FullScreenFrame url={iframeUrl} onload={hideLoading} onerror={hideLoading} />
</div>
);


+ 9
- 1
react-ui/src/components/ParameterSelect/config.tsx View File

@@ -14,7 +14,15 @@ const filterResourceStandard: SelectProps<string, ComputingResource>['filterOpti
};

// id 从 number 转换为 string
const convertId = (item: any) => ({ ...item, id: `${item.id}-${item.identifier}` });
const convertId = (item: any) => ({
...item,
id: JSON.stringify({
id: item.id,
name: item.name,
identifier: item.identifier,
owner: item.owner,
}),
});

export type SelectPropsConfig = {
getOptions: () => Promise<any>; // 获取下拉数据


+ 4
- 1
react-ui/src/components/ResourceSelect/index.tsx View File

@@ -37,7 +37,7 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps)
onOk: (res) => {
setSelectedResource(res);
if (res) {
const { activeTab, id, name, version, path } = res;
const { activeTab, id, name, version, path, identifier, owner } = res;
if (type === ResourceSelectorType.Mirror) {
onChange?.({
value: path,
@@ -50,8 +50,11 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps)
} else {
const jsonObj = {
id,
name,
version,
path,
identifier,
owner,
};
const jsonObjStr = JSON.stringify(jsonObj);
const showValue = `${name}:${version}`;


+ 7
- 0
react-ui/src/pages/Dataset/components/ResourceInfo/index.less View File

@@ -34,12 +34,19 @@
}

&__bottom {
position: relative;
height: calc(100% - 135px);
padding: 8px 30px 20px;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09);

&__legend {
position: absolute;
top: 20px;
right: 30px;
}

:global {
.ant-tabs {
height: 100%;


+ 28
- 16
react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx View File

@@ -11,14 +11,13 @@ import {
ResourceVersionData,
resourceConfig,
} from '@/pages/Dataset/config';
import GraphLegend from '@/pages/Model/components/GraphLegend';
import ModelEvolution from '@/pages/Model/components/ModelEvolution';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { getSessionStorageItem, resourceItemKey } from '@/utils/sessionStorage';
import { modalConfirm } from '@/utils/ui';
import { useParams, useSearchParams } from '@umijs/max';
import { App, Button, Flex, Select, Tabs } from 'antd';
import { pick } from 'lodash';
import { useEffect, useState } from 'react';
import AddVersionModal from '../AddVersionModal';
import ResourceIntro from '../ResourceIntro';
@@ -40,30 +39,32 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
const [info, setInfo] = useState<ResourceData>({} as ResourceData);
const locationParams = useParams();
const [searchParams] = useSearchParams();
const resourceId = Number(locationParams.id);
// 模型演化传入的 tab
const defaultTab = searchParams.get('tab') || ResourceInfoTabKeys.Introduction;
// 模型演化传入的版本
let versionParam = searchParams.get('version');
const name = searchParams.get('name') || '';
const owner = searchParams.get('owner') || '';
const identifier = searchParams.get('identifier') || '';
const [versionList, setVersionList] = useState<ResourceVersionData[]>([]);
const [version, setVersion] = useState<string | undefined>(undefined);
const [activeTab, setActiveTab] = useState<string>(defaultTab);
const resourceId = Number(locationParams.id);
const config = resourceConfig[resourceType];
const typeName = config.name; // 数据集/模型
const { message } = App.useApp();

useEffect(() => {
const info = getSessionStorageItem(resourceItemKey, true);
if (info) {
setInfo(info);
getVersionList(pick(info, ['owner', 'identifier']));
}
}, [resourceId]);
getVersionList();
}, [resourceId, owner, identifier]);

useEffect(() => {
if (version) {
getResourceDetail({
...pick(info, ['owner', 'name', 'id', 'identifier']),
id: resourceId,
owner,
name,
identifier,
version,
});
}
@@ -85,9 +86,14 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
};

// 获取版本列表
const getVersionList = async (params: { owner: string; identifier: string }) => {
const getVersionList = async () => {
const request = config.getVersions;
const [res] = await to(request(params));
const [res] = await to(
request({
owner,
identifier,
}),
);
if (res && res.data && res.data.length > 0) {
setVersionList(res.data);
if (
@@ -112,7 +118,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
resoureName: info.name,
identifier: info.identifier,
onOk: () => {
getVersionList(pick(info, ['owner', 'identifier']));
getVersionList();
close();
},
});
@@ -127,13 +133,16 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
const deleteVersion = async () => {
const request = config.deleteVersion;
const params = {
...pick(info, ['id', 'owner', 'identifier', 'relative_paths']),
id: resourceId,
owner,
identifier,
relative_paths: info.relative_paths,
version,
};
const [res] = await to(request(params));
if (res) {
message.success('删除成功');
getVersionList(pick(info, ['owner', 'identifier']));
getVersionList();
}
};

@@ -174,7 +183,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
<ModelEvolution
resourceId={resourceId}
version={version}
identifier={info.identifier}
identifier={identifier}
isActive={activeTab === ResourceInfoTabKeys.Evolution}
onVersionChange={handleVersionChange}
></ModelEvolution>
@@ -228,6 +237,9 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
</div>
<div className={styles['resource-info__bottom']}>
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
<div className={styles['resource-info__bottom__legend']}>
{activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />}
</div>
</div>
</div>
);


+ 3
- 3
react-ui/src/pages/Dataset/components/ResourceList/index.tsx View File

@@ -4,7 +4,6 @@ import { CommonTabKeys } from '@/enums';
import AddModelModal from '@/pages/Dataset/components/AddModelModal';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { resourceItemKey, setSessionStorageItem } from '@/utils/sessionStorage';
import { modalConfirm } from '@/utils/ui';
import { useNavigate } from '@umijs/max';
import { App, Button, Input, Pagination, PaginationProps } from 'antd';
@@ -138,8 +137,9 @@ function ResourceList(
activeTag: dataTag,
});
const prefix = config.prefix;
setSessionStorageItem(resourceItemKey, record, true);
navigate(`/dataset/${prefix}/info/${record.id}`);
navigate(
`/dataset/${prefix}/info/${record.id}?name=${record.name}&owner=${record.owner}&identifier=${record.identifier}`,
);
};

// 分页切换


+ 2
- 7
react-ui/src/pages/Model/components/ModelEvolution/index.less View File

@@ -1,16 +1,11 @@
.model-evolution {
width: 100%;
height: 100%;
overflow-x: hidden;
background-color: white;

&__top {
padding: 30px 0;
color: @text-color;
font-size: @font-size-content;
}

&__graph {
height: calc(100% - 92px);
height: calc(100%);
background-color: @background-color;
background-image: url(@/assets/img/pipeline-canvas-bg.png);
background-size: 100% 100%;


+ 5
- 8
react-ui/src/pages/Model/components/ModelEvolution/index.tsx View File

@@ -9,9 +9,8 @@ import { getModelAtlasReq } from '@/services/dataset/index.js';
import themes from '@/styles/theme.less';
import { to } from '@/utils/promise';
import G6, { G6GraphEvent, Graph, INode } from '@antv/g6';
import { Flex } from 'antd';
import { useEffect, useRef, useState } from 'react';
import GraphLegend from '../GraphLegend';
import NodeTooltips from '../NodeTooltips';
import styles from './index.less';
import type { ModelDepsData, ProjectDependency, TrainDataset } from './utils';
@@ -145,14 +144,15 @@ function ModelEvolution({
// 更加缩放,调整 tooltip 位置
const offsetX = (nodeWidth * zoom) / 4;
const offsetY = (nodeHeight * zoom) / 4;
point.x += offsetX;

const canvasWidth = graphRef.current!.clientWidth;
if (point.x + 300 > canvasWidth) {
point.x = canvasWidth - 300;
if (point.x + 300 > canvasWidth + 30) {
point.x = canvasWidth + 30 - 300;
}

setHoverNodeData(model);
setNodeToolTipX(point.x + offsetX);
setNodeToolTipX(point.x);
setNodeToolTipY(graphRef.current!.clientHeight - point.y + offsetY);
setShowNodeTooltip(true);
});
@@ -248,9 +248,6 @@ function ModelEvolution({

return (
<div className={styles['model-evolution']}>
<Flex align="center" className={styles['model-evolution__top']}>
<GraphLegend style={{ marginRight: 0, marginLeft: 'auto' }}></GraphLegend>
</Flex>
<div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div>
{(showNodeTooltip || enterTooltip) && (
<NodeTooltips


+ 71
- 53
react-ui/src/pages/Model/components/ModelEvolution/utils.tsx View File

@@ -38,9 +38,11 @@ export type TrainTask = {
};

export interface TrainDataset extends NodeConfig {
dataset_id: number;
dataset_name: string;
dataset_version: string;
repo_id: number;
name: string;
version: string;
identifier: string;
owner: string;
model_type: NodeType.TestDataset | NodeType.TrainDataset;
}

@@ -51,34 +53,33 @@ export interface ProjectDependency extends NodeConfig {
model_type: NodeType.Project;
}

export type ModalDetail = {
export type ModelMeta = {
train_datasets?: TrainDataset[];
test_datasets?: TrainDataset[];
project_depency?: ProjectDependency;
train_task?: TrainTask;
name: string;
available_range: number;
file_name: string;
file_size: string;
description: string;
model_type_name: string;
model_tag_name: string;
version: string;
model_source: string;
model_type: string;
create_time: string;
file_size: string;
is_public: boolean;
};

export interface ModelDepsAPIData {
current_model_id: number;
repo_id: number;
model_name: string;
version: string;
workflow_id: number;
exp_ins_id: number;
model_type: NodeType.Children | NodeType.Current | NodeType.Parent;
current_model_name: string;
project_dependency?: ProjectDependency;
test_dataset: TrainDataset[];
train_dataset: TrainDataset[];
train_task: TrainTask;
model_version_dependcy_vo: ModalDetail;
children_models: ModelDepsAPIData[];
parent_models: ModelDepsAPIData[];
model_meta: ModelMeta;
child_model_list: ModelDepsAPIData[];
parent_model_vo?: ModelDepsAPIData;
}

export interface ModelDepsData extends Omit<ModelDepsAPIData, 'children_models'>, TreeGraphData {
export interface ModelDepsData extends Omit<ModelDepsAPIData, 'child_model_list'>, TreeGraphData {
children: ModelDepsData[];
expanded: boolean; // 是否展开
level: number; // 层级,从 0 开始
@@ -92,8 +93,11 @@ export function normalizeChildren(data: ModelDepsData[]) {
item.model_type = NodeType.Children;
item.expanded = false;
item.level = 0;
item.datasetLen = item.train_dataset.length + item.test_dataset.length;
item.id = `$M_${item.current_model_id}_${item.version}`;
item.datasetLen = getDatasetLen(
item.model_meta.train_datasets,
item.model_meta.test_datasets,
);
item.id = `$M_${item.repo_id}_${item.version}`;
item.label = getLabel(item);
item.style = getStyle(NodeType.Children);
normalizeChildren(item.children);
@@ -104,16 +108,17 @@ export function normalizeChildren(data: ModelDepsData[]) {
// 获取 label
export function getLabel(node: ModelDepsData | ModelDepsAPIData) {
return (
fittingString(
`${node.model_version_dependcy_vo.name ?? ''}`,
nodeWidth - labelPadding,
nodeFontSize,
) +
fittingString(`${node.model_name ?? ''}`, nodeWidth - labelPadding, nodeFontSize) +
'\n' +
fittingString(`${node.version}`, nodeWidth - labelPadding, nodeFontSize)
);
}

// 获取数据集数量
export function getDatasetLen(train?: TrainDataset[], test?: TrainDataset[]) {
return (train?.length || 0) + (test?.length || 0);
}

// 获取 style
export function getStyle(model_type: NodeType) {
let fill = '';
@@ -148,41 +153,43 @@ export function getStyle(model_type: NodeType) {
export function normalizeTreeData(apiData: ModelDepsAPIData): ModelDepsData {
// 将 children_models 转换成 children
let normalizedData = changePropertyName(apiData, {
children_models: 'children',
child_model_list: 'children',
}) as ModelDepsData;

// 设置当前模型的数据
normalizedData.model_type = NodeType.Current;
normalizedData.id = `$M_${normalizedData.current_model_id}_${normalizedData.version}`;
normalizedData.id = `$M_${normalizedData.repo_id}_${normalizedData.version}`;
normalizedData.label = getLabel(normalizedData);
normalizedData.style = getStyle(NodeType.Current);
normalizedData.expanded = true;
normalizedData.datasetLen =
normalizedData.train_dataset.length + normalizedData.test_dataset.length;
normalizedData.datasetLen = getDatasetLen(
normalizedData.model_meta.train_datasets,
normalizedData.model_meta.test_datasets,
);
normalizeChildren(normalizedData.children as ModelDepsData[]);
normalizedData.level = 0;

// 将 parent_models 转换成树形结构
let parent_models = normalizedData.parent_models || [];
while (parent_models.length > 0) {
const parent = parent_models[0];
let parent_model = normalizedData.parent_model_vo;
while (parent_model) {
const parent = parent_model;
normalizedData = {
...parent,
expanded: false,
level: 0,
datasetLen: parent.train_dataset.length + parent.test_dataset.length,
datasetLen: getDatasetLen(parent.model_meta.train_datasets, parent.model_meta.test_datasets),
model_type: NodeType.Parent,
id: `$M_${parent.current_model_id}_${parent.version}`,
id: `$M_${parent.repo_id}_${parent.version}`,
label: getLabel(parent),
style: getStyle(NodeType.Parent),
children: [
{
...normalizedData,
parent_models: [],
parent_model: null,
},
],
};
parent_models = normalizedData.parent_models || [];
parent_model = normalizedData.parent_model_vo;
}
return normalizedData;
}
@@ -195,11 +202,12 @@ export function getGraphData(data: ModelDepsData, hierarchyNodes: ModelDepsData[
getWidth: () => nodeWidth,
getVGap: (node: NodeConfig) => {
const model = node as ModelDepsData;
const { model_type, expanded, project_dependency } = model;
const { model_type, expanded, model_meta } = model;
const { project_depency } = model_meta;
if (model_type === NodeType.Current || model_type === NodeType.Parent) {
return vGap / 2;
}
const selfGap = expanded && project_dependency?.url ? nodeHeight + vGap : 0;
const selfGap = expanded && project_depency?.url ? nodeHeight + vGap : 0;
const nextNode = getSameHierarchyNextNode(model, hierarchyNodes);
if (!nextNode) {
return vGap / 2;
@@ -254,28 +262,35 @@ const addDatasetDependency = (
nodes: NodeConfig[],
edges: EdgeConfig[],
) => {
const { train_dataset, test_dataset, id } = data;
train_dataset.forEach((item) => {
item.id = `$DTrain_${id}_${item.dataset_id}_${item.dataset_version}`;
const { repo_id, model_meta } = data;
const { train_datasets, test_datasets } = model_meta;
train_datasets?.forEach((item) => {
if (!item.repo_id) {
item.repo_id = item.id;
}
item.id = `$DTrain_${repo_id}_${item.repo_id}_${item.version}`;
item.model_type = NodeType.TrainDataset;
item.style = getStyle(NodeType.TrainDataset);
});
test_dataset.forEach((item) => {
item.id = `$DTest_${id}_${item.dataset_id}_${item.dataset_version}`;
test_datasets?.forEach((item) => {
if (!item.repo_id) {
item.repo_id = item.id;
}
item.id = `$DTest_${repo_id}_${item.repo_id}_${item.version}`;
item.model_type = NodeType.TestDataset;
item.style = getStyle(NodeType.TestDataset);
});

datasetNodes.length = 0;
const len = train_dataset.length + test_dataset.length;
[...train_dataset, ...test_dataset].forEach((item, index) => {
const len = getDatasetLen(train_datasets, test_datasets);
[...(train_datasets ?? []), ...(test_datasets ?? [])].forEach((item, index) => {
const node = { ...item };
node.type = 'ellipse';
node.size = [ellipseWidth, nodeHeight];
node.label =
fittingString(node.dataset_name, ellipseWidth - labelPadding, nodeFontSize) +
fittingString(node.name, ellipseWidth - labelPadding, nodeFontSize) +
'\n' +
fittingString(node.dataset_version, ellipseWidth - labelPadding, nodeFontSize);
fittingString(node.version, ellipseWidth - labelPadding, nodeFontSize);

const half = len / 2 - 0.5;
node.x = currentNode.x! - (half - index) * (ellipseWidth + datasetHGap);
@@ -299,10 +314,11 @@ const addProjectDependency = (
nodes: NodeConfig[],
edges: EdgeConfig[],
) => {
const { project_dependency, id } = data;
if (project_dependency?.url) {
const node = { ...project_dependency };
node.id = `$P_${id}_${node.url}_${node.branch}`;
const { repo_id, model_meta } = data;
const { project_depency } = model_meta;
if (project_depency?.url) {
const node = { ...project_depency };
node.id = `$P_${repo_id}_${node.url}_${node.branch}`;
node.model_type = NodeType.Project;
node.type = 'rect';
node.label = fittingString(node.name, nodeWidth - labelPadding, nodeFontSize);
@@ -322,6 +338,7 @@ const addProjectDependency = (
}
};

/*
// 判断两个矩形是否相交
function isRectanglesOverlap(rect1: Rect, rect2: Rect) {
const a2x = rect1.x + rect1.width / 2;
@@ -366,6 +383,7 @@ function adjustDatasetPosition(node: NodeConfig) {
});
}
}
*/

// 层级遍历树结构
export function traverseHierarchically(data: ModelDepsData | undefined): ModelDepsData[] {


+ 2
- 0
react-ui/src/pages/Model/components/NodeTooltips/index.less View File

@@ -2,6 +2,7 @@
position: absolute;
bottom: -100px;
left: -300px;
z-index: 10;
width: 300px;
padding: 10px;
background: white;
@@ -50,6 +51,7 @@
flex: 1;
min-width: 0;
font-weight: 500;
word-break: break-all;

&:hover {
text-decoration: underline @underline-color;


+ 14
- 18
react-ui/src/pages/Model/components/NodeTooltips/index.tsx View File

@@ -14,9 +14,9 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) {
const navigate = useNavigate();

const gotoExperimentPage = () => {
if (data.train_task?.ins_id) {
if (data.model_meta.train_task?.ins_id) {
const { origin } = location;
const url = `${origin}/pipeline/experiment/instance/${data.workflow_id}/${data.train_task.ins_id}`;
const url = `${origin}/pipeline/experiment/instance/${data.model_meta.train_task.task_id}/${data.model_meta.train_task.ins_id}`;
window.open(url, '_blank');
}
};
@@ -25,10 +25,10 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) {
if (data.model_type === NodeType.Current) {
return;
}
if (data.current_model_id === resourceId) {
if (data.repo_id === resourceId) {
onVersionChange?.(data.version);
} else {
const path = `/dataset/model/info/${data.current_model_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}`;
const path = `/dataset/model/info/${data.repo_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}&name=${data.model_name}&owner=${data.owner}&identifier=${data.identifier}`;
navigate(path);
}
};
@@ -40,12 +40,10 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) {
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>模型名称:</span>
{data.model_type === NodeType.Current ? (
<span className={styles['node-tooltips__row__value']}>
{data.model_version_dependcy_vo?.name || '--'}
</span>
<span className={styles['node-tooltips__row__value']}>{data.model_name || '--'}</span>
) : (
<ValueLink
value={data.model_version_dependcy_vo?.name}
value={data.model_name}
className={styles['node-tooltips__row__link']}
nullClassName={styles['node-tooltips__row__value']}
onClick={gotoModelPage}
@@ -59,25 +57,25 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) {
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>模型框架:</span>
<span className={styles['node-tooltips__row__value']}>
{data.model_version_dependcy_vo?.model_type_name || '--'}
{data.model_meta.model_type || '--'}
</span>
</div>
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>模型大小:</span>
<span className={styles['node-tooltips__row__value']}>
{data.model_version_dependcy_vo?.file_size || '--'}
{data.model_meta.file_size || '--'}
</span>
</div>
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>创建时间:</span>
<span className={styles['node-tooltips__row__value']}>
{formatDate(data.model_version_dependcy_vo?.create_time)}
{formatDate(data.model_meta.create_time || '--')}
</span>
</div>
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>模型权限:</span>
<span className={styles['node-tooltips__row__value']}>
{data.model_version_dependcy_vo?.available_range === 1 ? '公开' : '私有'}
{data.model_meta.is_public ? '公开' : '私有'}
</span>
</div>
</div>
@@ -86,7 +84,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) {
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>训练任务:</span>
<ValueLink
value={data.train_task?.name}
value={data.model_meta.train_task?.name}
className={styles['node-tooltips__row__link']}
nullClassName={styles['node-tooltips__row__value']}
onClick={gotoExperimentPage}
@@ -100,7 +98,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) {
function DatasetInfo({ data }: { data: TrainDataset }) {
const gotoDatasetPage = () => {
const { origin } = location;
const url = `${origin}/dataset/dataset/info/${data.dataset_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.dataset_version}`;
const url = `${origin}/dataset/dataset/info/${data.repo_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.version}&name=${data.name}&owner=${data.owner}&identifier=${data.identifier}`;
window.open(url, '_blank');
};

@@ -111,7 +109,7 @@ function DatasetInfo({ data }: { data: TrainDataset }) {
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>数据集名称:</span>
<ValueLink
value={data.dataset_name}
value={data.name}
className={styles['node-tooltips__row__link']}
nullClassName={styles['node-tooltips__row__value']}
onClick={gotoDatasetPage}
@@ -119,9 +117,7 @@ function DatasetInfo({ data }: { data: TrainDataset }) {
</div>
<div className={styles['node-tooltips__row']}>
<span className={styles['node-tooltips__row__title']}>数据集版本:</span>
<span className={styles['node-tooltips__row__value']}>
{data.dataset_version || '--'}
</span>
<span className={styles['node-tooltips__row__value']}>{data.version || '--'}</span>
</div>
</div>
</>


+ 4
- 1
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -204,11 +204,14 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
});
}
} else {
const { activeTab, id, name, version, path } = res;
const { activeTab, id, name, version, path, identifier, owner } = res;
const value = JSON.stringify({
id,
name,
version,
path,
identifier,
owner,
});
const showValue = `${name}:${version}`;
form.setFieldValue(formItemName, {


+ 0
- 115
react-ui/src/pages/Pipeline/components/ResourceSelectorModal/config.tsx View File

@@ -74,121 +74,6 @@ const convertMirrorVersionToTreeData = (
}));
};

// 从树形数据节点 id 中获取数据集版本列表的参数
// const parseDatasetVersionId = (id: string) => {
// const list = id.split('-');
// return {
// id: Number(list[0]),
// name: list[1],
// owner: list[2],
// identifier: list[3],
// version: list[4],
// };
// };

// 从树形数据节点 id 中获取数据集版本列表的参数
// const parseMirrorVersionId = (id: string) => {
// const list = id.split('-');
// return {
// parentId: Number(list[0]),
// id: list[1],
// url: list[2],
// };
// };

// export type MirrorVersion = {
// id: number; // 镜像版本 id
// status: MirrorVersionStatus; // 镜像版本状态
// tag_name: string; // 镜像版本 name
// url: string; // 镜像版本路径
// };

// export type SelectorTypeInfo = {
// getList: (params: any) => Promise<any>; // 获取资源列表
// getVersions: (params: any) => Promise<any>; // 获取资源版本列表
// getFiles: (params: any) => Promise<any>; // 获取资源文件列表
// handleVersionResponse: (res: any) => any[]; // 处理版本列表接口数据
// dataToTreeData: (data: any) => TreeDataNode[]; // 数据转树形结构
// parseTreeNodeId: (id: string) => any; // 获取版本列表请求参数
// modalIcon: string; // modal icon
// buttonIcon: string; // button icon
// name: string; // 名称
// litReqParamKey: 'available_range' | 'image_type'; // 表示是公开还是私有的参数名称,获取资源列表接口使用
// tabItems: TabsProps['items']; // tab 列表
// buttontTitle: string; // 按钮 title
// };

// export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = {
// [ResourceSelectorType.Model]: {
// getList: getModelList,
// getVersions: getModelVersionList,
// getFiles: getModelVersionIdList,

// name: '模型',
// modalIcon: modelImg,
// buttonIcon: 'icon-xuanzemoxing',
// litReqParamKey: 'available_range',
// tabItems: [
// {
// key: CommonTabKeys.Private,
// label: '我的模型',
// },
// {
// key: CommonTabKeys.Public,
// label: '公开模型',
// },
// ],
// buttontTitle: '选择模型',
// },
// [ResourceSelectorType.Dataset]: {
// getList: getDatasetList,
// getVersions: getDatasetVersionList,
// getFiles: getDatasetInfo,

// name: '数据集',
// modalIcon: datasetImg,
// buttonIcon: 'icon-xuanzeshujuji',
// litReqParamKey: 'available_range',
// tabItems: [
// {
// key: CommonTabKeys.Private,
// label: '我的数据集',
// },
// {
// key: CommonTabKeys.Public,
// label: '公开数据集',
// },
// ],
// buttontTitle: '选择数据集',
// },
// [ResourceSelectorType.Mirror]: {
// getList: getMirrorListReq,
// getVersions: (id: number) => getMirrorVersionListReq({ image_id: id, page: 0, size: 200 }),
// getFiles: getMirrorFilesReq,
// handleVersionResponse: (res) =>
// res.data?.content?.filter(
// (v: MirrorVersionData) => v.status === MirrorVersionStatus.Available,
// ) || [],
// dataToTreeData: convertMirrorToTreeData,
// parseTreeNodeId: (id: string) => id,
// name: '镜像',
// modalIcon: mirrorImg,
// buttonIcon: 'icon-xuanzejingxiang',
// litReqParamKey: 'image_type',
// tabItems: [
// {
// key: CommonTabKeys.Private,
// label: '我的镜像',
// },
// {
// key: CommonTabKeys.Public,
// label: '公开镜像',
// },
// ],
// buttontTitle: '选择镜像',
// },
// };

interface SelectorTypeInfo {
getList: (isPublic: boolean) => Promise<any>; // 获取资源列表
getVersions: (parentKey: string, parentNode: any) => Promise<any>; // 获取资源版本列表


+ 8
- 1
react-ui/src/pages/Pipeline/components/ResourceSelectorModal/index.tsx View File

@@ -22,6 +22,8 @@ export type ResourceSelectorResponse = {
name: string; // 数据集\模型\镜像 name
version: string; // 数据集\模型\镜像版本
path: string; // 数据集\模型\镜像版本路径
identifier: string; // 数据集\模型 identifier
owner: string; // 数据集\模型 owner
activeTab: CommonTabKeys; // 是我的还是公开的
};

@@ -221,12 +223,17 @@ function ResourceSelectorModal({
if (checkedKeys.length > 0) {
const last = checkedKeys[0] as string;
const { id, version } = getIdAndVersion(last);
const name = (treeData.find((v) => v.key === id)?.title ?? '') as string;
const treeNode = treeData.find((v) => v.key === id) as any;
const name = (treeNode?.title ?? '') as string;
const identifier = (treeNode?.identifier ?? '') as string;
const owner = (treeNode?.owner ?? '') as string;
const res = {
id,
name,
path: versionPath,
version,
identifier,
owner,
activeTab: activeTab as CommonTabKeys,
};
onOk?.(res);


Loading…
Cancel
Save