Browse Source

feat: 模型、数据集版本自增

dev-zw-big-model
zhaowei 6 months ago
parent
commit
7251927ccb
9 changed files with 164 additions and 324 deletions
  1. +1
    -1
      react-ui/config/proxy.ts
  2. +7
    -121
      react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
  3. +7
    -113
      react-ui/src/pages/Dataset/components/AddModelModal/index.tsx
  4. +20
    -2
      react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx
  5. +7
    -0
      react-ui/src/pages/Dataset/components/ResourceInfo/index.less
  6. +85
    -54
      react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx
  7. +5
    -0
      react-ui/src/pages/Dataset/config.tsx
  8. +13
    -32
      react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx
  9. +19
    -1
      react-ui/src/services/dataset/index.js

+ 1
- 1
react-ui/config/proxy.ts View File

@@ -22,7 +22,7 @@ export default {
// 要代理的地址
target: 'http://172.20.32.197:31213', // 开发环境
// target: 'http://172.20.32.235:31213', // 测试环境
// target: 'http://172.20.32.44:8082',
// target: 'http://172.20.32.127:8082',
// target: 'http://172.20.32.164:8082',
// 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie


+ 7
- 121
react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx View File

@@ -1,30 +1,8 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { CategoryData, DataSource } from '@/pages/Dataset/config';
import { addDataset } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import {
getFileListFromEvent,
limitUploadFileType,
removeUploadedFile,
validateUploadFiles,
} from '@/utils/ui';
import {
Button,
Form,
Input,
Radio,
Select,
Upload,
UploadFile,
message,
type ModalProps,
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import styles from './index.less';
import { Form, Input, Radio, Select, message, type ModalProps } from 'antd';

interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
typeList: CategoryData[];
@@ -33,20 +11,6 @@ interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
}

function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) {
const [uuid] = useState(Date.now());

// 上传组件参数
const uploadProps: UploadProps = {
action: resourceConfig[ResourceType.Dataset].uploadAction,
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
accept: '.zip,.tgz',
beforeUpload: limitUploadFileType('zip,tgz'),
onRemove: removeUploadedFile,
};

// 上传请求
const createDataset = async (params: any) => {
const [res] = await to(addDataset(params));
@@ -58,22 +22,11 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr

// 提交
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const params = {
...omit(formData, ['fileList']),
dataset_source: DataSource.Create,
dataset_version_vos: fileList.map((item) => {
const data = item.response?.data?.[0] ?? {};
return {
file_name: data.fileName,
file_size: data.fileSize,
url: data.url,
};
}),
};
createDataset(params);
}
const params = {
...formData,
dataset_source: DataSource.Create,
};
createDataset(params);
};

return (
@@ -108,32 +61,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
>
<Input placeholder="请输入数据名称" showCount allowClear maxLength={40} />
</Form.Item>
<Form.Item
label="数据集版本"
name="version"
rules={[
{
required: true,
message: '请输入数据集版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '数据集版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`数据集版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`数据集版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入数据集版本" showCount allowClear maxLength={64} />
</Form.Item>
<Form.Item label="数据集分类" name="data_type">
<Select
allowClear
@@ -172,24 +99,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"
@@ -200,29 +109,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
<Radio value={true}>公开</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="数据集文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
rules={[
{
required: true,
message: '请上传数据集文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }}>
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
<div className={styles['upload-tip']}>只允许上传 .zip 和 .tgz 格式文件</div>
</Upload>
</Form.Item>
</Form>
</KFModal>
);


+ 7
- 113
react-ui/src/pages/Dataset/components/AddModelModal/index.tsx View File

@@ -1,25 +1,8 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { CategoryData, DataSource } from '@/pages/Dataset/config';
import { addModel } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import { getFileListFromEvent, removeUploadedFile, validateUploadFiles } from '@/utils/ui';
import {
Button,
Form,
Input,
Radio,
Select,
Upload,
UploadFile,
message,
type ModalProps,
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import styles from '../AddDatasetModal/index.less';
import { Form, Input, Radio, Select, message, type ModalProps } from 'antd';

interface AddModelModalProps extends Omit<ModalProps, 'onOk'> {
typeList: CategoryData[];
@@ -28,18 +11,6 @@ interface AddModelModalProps extends Omit<ModalProps, 'onOk'> {
}

function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) {
const [uuid] = useState(Date.now());

// 上传组件参数
const uploadProps: UploadProps = {
action: resourceConfig[ResourceType.Model].uploadAction,
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
onRemove: removeUploadedFile,
};

// 上传请求
const createModel = async (params: any) => {
const [res] = await to(addModel(params));
@@ -51,22 +22,11 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)

// 提交
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const params = {
...omit(formData, ['fileList']),
model_source: DataSource.Create,
model_version_vos: fileList.map((item) => {
const data = item.response?.data?.[0] ?? {};
return {
file_name: data.fileName,
file_size: data.fileSize,
url: data.url,
};
}),
};
createModel(params);
}
const params = {
...formData,
model_source: DataSource.Create,
};
createModel(params);
};

return (
@@ -99,32 +59,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
>
<Input placeholder="请输入模型名称" showCount allowClear maxLength={40} />
</Form.Item>
<Form.Item
label="模型版本"
name="version"
rules={[
{
required: true,
message: '请输入模型版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '模型版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`模型版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`模型版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入模型版本" showCount allowClear maxLength={64} />
</Form.Item>
<Form.Item label="模型框架" name="model_type">
<Select
allowClear
@@ -163,24 +97,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"
@@ -191,28 +107,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
<Radio value={true}>公开</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="模型文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
rules={[
{
required: true,
message: '请上传模型文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }}>
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
</Upload>
</Form.Item>
</Form>
</KFModal>
);


+ 20
- 2
react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx View File

@@ -15,7 +15,7 @@ import {
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import styles from '../AddDatasetModal/index.less';

interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
@@ -40,6 +40,23 @@ function AddVersionModal({
}: AddVersionModalProps) {
const [uuid] = useState(Date.now());
const config = resourceConfig[resourceType];
const [form] = Form.useForm();

useEffect(() => {
const getNextVersion = async () => {
const request = config.getNextVersion;
const params = {
identifier,
owner,
};
const [res] = await to(request(params));
if (res && res.data) {
const nextVersion = res.data;
form.setFieldValue('version', nextVersion);
}
};
getNextVersion();
}, [identifier, owner, config, form]);

// 上传组件参数
const uploadProps: UploadProps = {
@@ -109,6 +126,7 @@ function AddVersionModal({
}}
onFinish={onFinish}
autoComplete="off"
form={form}
>
<Form.Item
label={`${name}名称`}
@@ -146,7 +164,7 @@ function AddVersionModal({
},
]}
>
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear />
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear disabled />
</Form.Item>
<Form.Item
label="版本描述"


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

@@ -28,8 +28,15 @@
border-radius: 4px;
}

&__desc {
margin-bottom: 0 !important;
color: @text-color;
font-size: @font-size;
}

&__praise {
display: flex;
flex: none;
align-items: center;
justify-content: center;
width: 70px;


+ 85
- 54
react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx View File

@@ -4,6 +4,7 @@
* @Description: 数据集、模型详情
*/

import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import KFIcon from '@/components/KFIcon';
import {
ResourceData,
@@ -19,7 +20,7 @@ import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { useParams, useSearchParams } from '@umijs/max';
import { App, Button, Flex, Select, Tabs } from 'antd';
import { App, Button, Flex, Select, Tabs, Typography } from 'antd';
import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react';
import AddVersionModal from '../AddVersionModal';
@@ -61,21 +62,24 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
const { message } = App.useApp();

// 获取详情
const getResourceDetail = useCallback(async () => {
const params = {
id: resourceId,
owner,
name,
identifier,
version,
is_public,
};
const request = config.getInfo;
const [res] = await to(request(params));
if (res && res.data) {
setInfo(res.data);
}
}, [config, resourceId, owner, name, identifier, version, is_public]);
const getResourceDetail = useCallback(
async (version: string | undefined) => {
const params = {
id: resourceId,
owner,
name,
identifier,
version,
is_public,
};
const request = config.getInfo;
const [res] = await to(request(params));
if (res && res.data) {
setInfo(res.data);
}
},
[config, resourceId, owner, name, identifier, is_public],
);

// 获取版本列表
const getVersionList = useCallback(
@@ -100,14 +104,15 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}
} else {
setVersion(undefined);
getResourceDetail(undefined);
}
},
[config, owner, identifier, versionParam],
[config, owner, identifier, versionParam, getResourceDetail],
);

useEffect(() => {
if (version) {
getResourceDetail();
getResourceDetail(version);
}
}, [version, getResourceDetail]);

@@ -116,7 +121,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}, [getVersionList]);

// 新建版本
const showModal = () => {
const showAddVersionModal = () => {
const { close } = openAntdModal(AddVersionModal, {
resourceType: resourceType,
resourceId: resourceId,
@@ -278,44 +283,70 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
<span>{info.praises_count}</span>
</div>
</Flex>
<Flex align="center">
<span style={{ marginRight: '10px' }}>版本号:</span>
<Select
placeholder="请选择版本号"
style={{ width: '160px', marginRight: '20px' }}
value={version}
onChange={handleVersionChange}
fieldNames={{ label: 'name', value: 'name' }}
options={versionList}
/>
<Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}>
创建新版本
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-banbenduibi" />}
onClick={showVersionSelector}
>
版本对比
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
onClick={handleDelete}
icon={<KFIcon type="icon-shanchu" />}
disabled={!version}
danger
{version ? (
<Flex align="center">
<span style={{ marginRight: '10px' }}>版本号:</span>
<Select
placeholder="请选择版本号"
style={{ width: '160px', marginRight: '20px' }}
value={version}
onChange={handleVersionChange}
fieldNames={{ label: 'name', value: 'name' }}
options={versionList}
/>
<Button
type="default"
onClick={showAddVersionModal}
icon={<KFIcon type="icon-xinjian2" />}
>
创建新版本
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-banbenduibi" />}
onClick={showVersionSelector}
>
版本对比
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
onClick={handleDelete}
icon={<KFIcon type="icon-shanchu" />}
danger
>
删除版本
</Button>
</Flex>
) : (
<Typography.Paragraph
className={styles['resource-info__top__desc']}
ellipsis={{ tooltip: info.description }}
>
删除版本
</Button>
</Flex>
{info.description ?? '暂无描述'}
</Typography.Paragraph>
)}
</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>
{version ? (
<>
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
<div className={styles['resource-info__bottom__legend']}>
{activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />}
</div>
</>
) : (
<KFEmpty
style={{ height: '100%' }}
type={EmptyType.NoData}
title="暂无版本"
content={`请创建${config.name}版本`}
hasFooter={true}
buttonTitle="创建版本"
onButtonClick={showAddVersionModal}
/>
)}
</div>
</div>
);


+ 5
- 0
react-ui/src/pages/Dataset/config.tsx View File

@@ -11,9 +11,11 @@ import {
deleteModelVersion,
getDatasetInfo,
getDatasetList,
getDatasetNextVersionReq,
getDatasetVersionList,
getModelInfo,
getModelList,
getModelNextVersionReq,
getModelVersionList,
} from '@/services/dataset/index.js';
import { limitUploadFileType } from '@/utils/ui';
@@ -39,6 +41,7 @@ type ResourceTypeInfo = {
deleteVersion: (params: any) => Promise<any>; // 删除版本
getInfo: (params: any) => Promise<any>; // 获取详情
compareVersion: (params: any) => Promise<any>; // 版本对比
getNextVersion: (params: any) => Promise<any>; // 获取下一个版本
name: string; // 名称
typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用
tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用
@@ -68,6 +71,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
deleteVersion: deleteDatasetVersion,
getInfo: getDatasetInfo,
compareVersion: compareDatasetVersion,
getNextVersion: getDatasetNextVersionReq,
name: '数据集',
typeParamKey: 'data_type',
tagParamKey: 'data_tag',
@@ -106,6 +110,7 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
deleteVersion: deleteModelVersion,
getInfo: getModelInfo,
compareVersion: compareModelVersion,
getNextVersion: getModelNextVersionReq,
name: '模型',
typeParamKey: 'model_type',
tagParamKey: 'model_tag',


+ 13
- 32
react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx View File

@@ -3,12 +3,10 @@ import KFModal from '@/components/KFModal';
import {
DataSource,
ResourceType,
ResourceVersionData,
resourceConfig,
type ResourceData,
} from '@/pages/Dataset/config';
import { to } from '@/utils/promise';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Form, Input, ModalProps, Select } from 'antd';
import { pick } from 'lodash';
import { useEffect, useState } from 'react';
@@ -44,7 +42,6 @@ function ExportModelModal({
}: ExportModelModalProps) {
const [form] = Form.useForm();
const [resources, setResources] = useState<ResourceData[]>([]);
const [versions, setVersions] = useState<ResourceVersionData[]>([]);
const config = resourceConfig[resourceType];

const layout = {
@@ -77,35 +74,24 @@ function ExportModelModal({
return undefined;
};

// 版本 tooltip
const getTooltip = () => {
const id = form.getFieldValue('id');
const resource = getSelectedResource(id);
const name = resource?.name ?? '';
const versionNames = versions.map((item: ResourceVersionData) => item.name).join('、');
const tooltip =
versions.length > 0 ? `${name}有以下版本:\n${versionNames}\n注意不能重复` : undefined;
return tooltip;
};

// 处理数据集、模型选择变化
const handleResourceChange = (id: number | undefined) => {
if (id) {
getRecourceVersions(id);
getRecourceNextVersion(id);
} else {
setVersions([]);
form.setFieldValue('version', '');
}
};

// 获取数据集、模型版本列表
const getRecourceVersions = async (id: number) => {
// 获取数据集、模型下一个版本
const getRecourceNextVersion = async (id: number) => {
const resource = getSelectedResource(id);
if (!resource) {
return;
}
const [res] = await to(config.getVersions(pick(resource, ['identifier', 'owner'])));
const [res] = await to(config.getNextVersion(pick(resource, ['identifier', 'owner'])));
if (res && res.data) {
setVersions(res.data);
form.setFieldValue('version', res.data);
}
};

@@ -184,15 +170,6 @@ function ExportModelModal({
<Form.Item
label={`${config.name}版本`}
name="version"
tooltip={
getTooltip()
? {
overlayClassName: styles['export-model-modal__tooltip'],
title: getTooltip(),
icon: <InfoCircleOutlined />,
}
: undefined
}
rules={[
{ required: true, message: `请输入${config.name}版本` },
{
@@ -205,8 +182,6 @@ function ExportModelModal({
return Promise.reject(`${config.name}版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`${config.name}版本不能为 origin`);
} else if (value && versions.map((item) => item.name).includes(value)) {
return Promise.reject(`${config.name}版本已存在`);
} else {
return Promise.resolve();
}
@@ -214,7 +189,13 @@ function ExportModelModal({
},
]}
>
<Input placeholder={`请输入${config.name}版本`} maxLength={64} showCount allowClear />
<Input
placeholder={`请输入${config.name}版本`}
maxLength={64}
showCount
allowClear
disabled
/>
</Form.Item>
<Form.Item
label="版本描述"


+ 19
- 1
react-ui/src/services/dataset/index.js View File

@@ -82,6 +82,15 @@ export function compareDatasetVersion(data) {
});
}

// 获取数据集下个版本号
export function getDatasetNextVersionReq(data) {
return request(`/api/mmp/newdataset/queryNextVersion`, {
method: 'POST',
data,
});
}


// ----------------------------模型---------------------------------

// 分页查询模型列表
@@ -140,6 +149,14 @@ export function deleteModelVersion(params) {
});
}

// 获取模型下个版本号
export function getModelNextVersionReq(data) {
return request(`/api/mmp/newmodel/queryNextVersion`, {
method: 'POST',
data,
});
}

// 获取模型依赖
export function getModelAtlasReq(params) {
return request(`/api/mmp/newmodel/getModelDependencyTree`, {
@@ -201,4 +218,5 @@ export function unpraiseResourceReq(id) {
return request(`/api/mmp/newmodel/unpraise/${id}`, {
method: 'DELETE',
});
}
}


Loading…
Cancel
Save