Browse Source

Merge remote-tracking branch 'origin/dev' into dev

dev-active_learn
chenzhihang 9 months ago
parent
commit
9685cf35ec
96 changed files with 4245 additions and 166 deletions
  1. +3
    -3
      react-ui/config/proxy.ts
  2. +50
    -2
      react-ui/config/routes.ts
  3. +1
    -1
      react-ui/package.json
  4. +1
    -1
      react-ui/src/app.tsx
  5. +8
    -0
      react-ui/src/components/IFramePage/index.tsx
  6. +1
    -1
      react-ui/src/components/KFSpin/index.less
  7. +21
    -0
      react-ui/src/components/PageTitle/index.less
  8. +13
    -2
      react-ui/src/components/PageTitle/index.tsx
  9. +6
    -0
      react-ui/src/components/ResourceSelectorModal/index.less
  10. +3
    -1
      react-ui/src/components/RightContent/AvatarDropdown.tsx
  11. +55
    -0
      react-ui/src/pages/ActiveLearn/Create/index.less
  12. +142
    -0
      react-ui/src/pages/ActiveLearn/Create/index.tsx
  13. +40
    -0
      react-ui/src/pages/ActiveLearn/Info/index.less
  14. +45
    -0
      react-ui/src/pages/ActiveLearn/Info/index.tsx
  15. +42
    -0
      react-ui/src/pages/ActiveLearn/Instance/index.less
  16. +194
    -0
      react-ui/src/pages/ActiveLearn/Instance/index.tsx
  17. +13
    -0
      react-ui/src/pages/ActiveLearn/List/index.tsx
  18. +13
    -0
      react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.less
  19. +278
    -0
      react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx
  20. +54
    -0
      react-ui/src/pages/ActiveLearn/components/CreateForm/BasicConfig.tsx
  21. +518
    -0
      react-ui/src/pages/ActiveLearn/components/CreateForm/ExecuteConfig.tsx
  22. +92
    -0
      react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts
  23. +14
    -0
      react-ui/src/pages/ActiveLearn/components/ExperimentHistory/index.less
  24. +132
    -0
      react-ui/src/pages/ActiveLearn/components/ExperimentHistory/index.tsx
  25. +16
    -0
      react-ui/src/pages/ActiveLearn/components/ExperimentLog/index.less
  26. +106
    -0
      react-ui/src/pages/ActiveLearn/components/ExperimentLog/index.tsx
  27. +52
    -0
      react-ui/src/pages/ActiveLearn/components/ExperimentResult/index.less
  28. +83
    -0
      react-ui/src/pages/ActiveLearn/components/ExperimentResult/index.tsx
  29. +120
    -0
      react-ui/src/pages/ActiveLearn/data.json
  30. +85
    -0
      react-ui/src/pages/ActiveLearn/types.ts
  31. +24
    -0
      react-ui/src/pages/AutoML/components/ExperimentList/config.ts
  32. +3
    -3
      react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
  33. +1
    -3
      react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx
  34. +2
    -1
      react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
  35. +1
    -4
      react-ui/src/pages/Experiment/components/LogGroup/index.tsx
  36. +1
    -1
      react-ui/src/pages/HyperParameter/Info/index.tsx
  37. +0
    -3
      react-ui/src/pages/HyperParameter/components/ExperimentLog/index.tsx
  38. +1
    -1
      react-ui/src/pages/HyperParameter/types.ts
  39. +12
    -0
      react-ui/src/pages/Knowledge/index.tsx
  40. +1
    -3
      react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx
  41. +1
    -1
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  42. +1
    -1
      react-ui/src/pages/Pipeline/Info/index.less
  43. +93
    -0
      react-ui/src/services/activeLearn/index.js
  44. +23
    -0
      react-ui/src/stories/PageTitle.stories.tsx
  45. +1
    -1
      react-ui/src/utils/date.ts
  46. +13
    -0
      react-ui/src/utils/promise.ts
  47. +1
    -1
      react-ui/src/utils/table.tsx
  48. +4
    -0
      ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/constant/Constant.java
  49. +62
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/activeLearn/ActiveLearnController.java
  50. +60
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/activeLearn/ActiveLearnInsController.java
  51. +1
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java
  52. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayController.java
  53. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/service/ServiceController.java
  54. +113
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ActiveLearn.java
  55. +39
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ActiveLearnIns.java
  56. +5
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ResourceOccupy.java
  57. +29
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ActiveLearnDao.java
  58. +23
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ActiveLearnInsDao.java
  59. +8
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/DevEnvironmentDao.java
  60. +3
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ResourceOccupyDao.java
  61. +6
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ServiceDao.java
  62. +116
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ActiveLearnInsStatusTask.java
  63. +28
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ActiveLearnInsService.java
  64. +22
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ActiveLearnService.java
  65. +1
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/RayInsService.java
  66. +2
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ResourceOccupyService.java
  67. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ServiceService.java
  68. +263
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ActiveLearnInsServiceImpl.java
  69. +201
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ActiveLearnServiceImpl.java
  70. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/AutoMlInsServiceImpl.java
  71. +18
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CodeConfigServiceImpl.java
  72. +17
    -11
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DevEnvironmentServiceImpl.java
  73. +5
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.java
  74. +4
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentServiceImpl.java
  75. +36
    -7
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
  76. +26
    -7
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageVersionServiceImpl.java
  77. +43
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ModelsServiceImpl.java
  78. +29
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/NewDatasetServiceImpl.java
  79. +5
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayInsServiceImpl.java
  80. +6
    -4
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayServiceImpl.java
  81. +5
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ResourceOccupyServiceImpl.java
  82. +15
    -6
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ServiceServiceImpl.java
  83. +17
    -4
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/JsonUtils.java
  84. +124
    -65
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java
  85. +67
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ActiveLearnParamVo.java
  86. +114
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ActiveLearnVo.java
  87. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/DevEnvironmentVo.java
  88. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
  89. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/serviceVos/ServiceVersionVo.java
  90. +187
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ActiveLearnDaoMapper.xml
  91. +79
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ActiveLearnInsDaoMapper.xml
  92. +1
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/AutoMlDao.xml
  93. +27
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/DevEnvironmentDaoMapper.xml
  94. +3
    -2
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/RayDaoMapper.xml
  95. +11
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ResourceOccupy.xml
  96. +30
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ServiceDaoMapper.xml

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

@@ -20,13 +20,13 @@ export default {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': {
// 要代理的地址
target: 'http://172.20.32.181:31213', // 开发环境
// target: 'http://172.20.32.98:8082',
// target: 'http://172.20.32.181:31213', // 开发环境
target: 'http://172.168.15.80:8082',
// target: 'http://172.20.32.150:8082',
// 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie
changeOrigin: true,
// pathRewrite: { '^/api': '' },
pathRewrite: { '^/api': '' },
},
'/profile/avatar/': {
target: 'http://172.20.32.185:31213',


+ 50
- 2
react-ui/config/routes.ts View File

@@ -181,7 +181,7 @@ export default [
},
{
name: '实验实例详情',
path: 'instance/:autoMLId/:id',
path: 'instance/:experimentId/:id',
component: './AutoML/Instance/index',
},
],
@@ -217,11 +217,47 @@ export default [
},
{
name: '实验实例详情',
path: 'instance/:autoMLId/:id',
path: 'instance/:experimentId/:id',
component: './HyperParameter/Instance/index',
},
],
},
{
name: '主动学习',
path: 'active-learn',
routes: [
{
name: '超参数寻优',
path: '',
component: './ActiveLearn/List/index',
},
{
name: '实验详情',
path: 'info/:id',
component: './ActiveLearn/Info/index',
},
{
name: '创建实验',
path: 'create',
component: './ActiveLearn/Create/index',
},
{
name: '编辑实验',
path: 'edit/:id',
component: './ActiveLearn/Create/index',
},
{
name: '复制实验',
path: 'copy/:id',
component: './ActiveLearn/Create/index',
},
{
name: '实验实例详情',
path: 'instance/:experimentId/:id',
component: './ActiveLearn/Instance/index',
},
],
},
],
},
{
@@ -525,6 +561,18 @@ export default [
},
],
},
{
name: '知识图谱',
path: '/knowledge',
routes: [
{
name: '知识图谱',
path: '',
key: 'knowledge',
component: './Knowledge/index',
},
],
},
{
path: '*',
layout: false,


+ 1
- 1
react-ui/package.json View File

@@ -140,7 +140,7 @@
"umi-presets-pro": "^2.0.0"
},
"engines": {
"node": ">=16.14.0"
"node": ">=18.18.0"
},
"create-umi": {
"ignoreScript": [


+ 1
- 1
react-ui/src/app.tsx View File

@@ -25,7 +25,7 @@ export { requestConfig as request } from './requestConfig';

/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
*/
export async function getInitialState(): Promise<GlobalInitialState> {
const fetchUserInfo = async () => {
try {


+ 8
- 0
react-ui/src/components/IFramePage/index.tsx View File

@@ -15,6 +15,7 @@ export enum IframePageType {
DevEnv = 'DevEnv', // 开发环境
GitLink = 'GitLink', // git link
Aim = 'Aim', // 实验对比
Knowledge = 'Knowledge', // 知识图谱
}

const getRequestAPI = (type: IframePageType): (() => Promise<any>) => {
@@ -37,6 +38,10 @@ const getRequestAPI = (type: IframePageType): (() => Promise<any>) => {
code: 200,
data: SessionStorage.getItem(SessionStorage.aimUrlKey) || '',
});
case IframePageType.Knowledge: { // 知识图谱
const { origin } = location;
return () => Promise.resolve({ code: 200, data: `http://${origin}:32701` });
}
}
};

@@ -58,9 +63,12 @@ function IframePage({ type, openInTab = false, className, style }: IframePagePro

useEffect(() => {
const requestIframeUrl = async () => {
setLoading(true);
const [res] = await to(getRequestAPI(type)());
if (res && res.data) {
setIframeUrl(res.data);
} else {
setLoading(false);
}
};



+ 1
- 1
react-ui/src/components/KFSpin/index.less View File

@@ -4,7 +4,7 @@
right: 0;
bottom: 0;
left: 0;
z-index: 1001;
z-index: 1001; // 设置大于 Modal 的 z-index
display: flex;
flex-direction: column;
align-items: center;


+ 21
- 0
react-ui/src/components/PageTitle/index.less View File

@@ -7,4 +7,25 @@
background-repeat: no-repeat;
background-position: top center;
background-size: 100% 100%;

&__tips {
position: relative;
margin-left: 18px;
padding: 3px 15px;
color: @primary-color;
background: .addAlpha(@primary-color, 0.1) [];
border-radius: 4px;

&::before {
position: absolute;
top: 10px;
left: -6px;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-right: 6px solid .addAlpha(@primary-color, 0.1) [];
border-bottom: 4px solid transparent;
content: '';
}
}
}

+ 13
- 2
react-ui/src/components/PageTitle/index.tsx View File

@@ -3,6 +3,7 @@
* @Date: 2024-04-17 14:01:46
* @Description: 页面标题
*/
import KFIcon from '@/components/KFIcon';
import classNames from 'classnames';
import React from 'react';
import './index.less';
@@ -10,19 +11,29 @@ import './index.less';
type PageTitleProps = {
/** 标题 */
title: React.ReactNode;
/** 图标 */
tooltip?: string;
/** 自定义类名 */
className?: string;
/** 自定义样式 */
style?: React.CSSProperties;
/** 自定义标题 */
titleRender?: () => React.ReactNode;
};

/**
* 页面标题
*/
function PageTitle({ title, style, className = '' }: PageTitleProps) {
function PageTitle({ title, style, className, tooltip }: PageTitleProps) {
return (
<div className={classNames('kf-page-title', className)} style={style}>
{title}
<div>{title}</div>
{tooltip && (
<div className="kf-page-title__tips">
<KFIcon type="icon-tishi" font={14} />
<span style={{ marginLeft: '8px', fontSize: '14px' }}>{tooltip}</span>
</div>
)}
</div>
);
}


+ 6
- 0
react-ui/src/components/ResourceSelectorModal/index.less View File

@@ -26,6 +26,12 @@
border: 1px solid rgba(22, 100, 255, 0.3);
border-radius: 8px;

:global {
.ant-tree-list-holder {
overflow-x: hidden;
}
}

&__search {
margin-bottom: 14px;
padding-left: 0;


+ 3
- 1
react-ui/src/components/RightContent/AvatarDropdown.tsx View File

@@ -2,6 +2,7 @@ import { clearSessionToken } from '@/access';
import { setRemoteMenu } from '@/services/session';
import { logout } from '@/services/system/auth';
import { ClientInfo } from '@/types';
import { sleep } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import { gotoLoginPage, oauthLogout } from '@/utils/ui';
import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
@@ -63,7 +64,8 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
*/
const loginOut = async () => {
oauthLogout('http://172.20.32.197:31209/oauth/logout');
await logout();
// 至少 1 秒后跳转,希望子系统能完成注销
await Promise.all([logout(), sleep(1000)]);
clearSessionToken();
setRemoteMenu(null);
gotoLoginPage();


+ 55
- 0
react-ui/src/pages/ActiveLearn/Create/index.less View File

@@ -0,0 +1,55 @@
.create-hyperparameter {
height: 100%;

&__content {
height: calc(100% - 60px);
margin-top: 10px;
padding: 30px 30px 10px;
overflow: auto;
color: @text-color;
font-size: @font-size-content;
background-color: white;
border-radius: 10px;

&__type {
color: @text-color;
font-size: @font-size-input-lg;
}

:global {
.ant-input-number {
width: 100%;
}

.ant-form-item {
margin-bottom: 20px;
}

.image-url {
margin-top: -15px;
.ant-form-item-label > label::after {
content: '';
}
}

.ant-btn-variant-text:disabled {
color: @text-disabled-color;
}

.ant-btn-variant-text {
color: #565658;
}

.ant-btn.ant-btn-icon-only .anticon {
font-size: 20px;
}

.anticon-question-circle {
margin-top: -12px;
margin-left: 1px !important;
color: @text-color-tertiary !important;
font-size: 12px !important;
}
}
}
}

+ 142
- 0
react-ui/src/pages/ActiveLearn/Create/index.tsx View File

@@ -0,0 +1,142 @@
/*
* @Author: 赵伟
* @Date: 2024-04-16 13:58:08
* @Description: 创建实验
*/
import PageTitle from '@/components/PageTitle';
import { AutoMLTaskType } from '@/enums';
import {
addActiveLearnReq,
getActiveLearnInfoReq,
updateActiveLearnReq,
} from '@/services/activeLearn';
import { safeInvoke } from '@/utils/functional';
import { to } from '@/utils/promise';
import { useLocation, useNavigate, useParams } from '@umijs/max';
import { App, Button, Form } from 'antd';
import { useEffect } from 'react';
import BasicConfig from '../components/CreateForm/BasicConfig';
import ExecuteConfig from '../components/CreateForm/ExecuteConfig';
import { ActiveLearnData, FormData } from '../types';
import styles from './index.less';

function CreateActiveLearn() {
const navigate = useNavigate();
const [form] = Form.useForm();
const { message } = App.useApp();
const params = useParams();
const id = safeInvoke(Number)(params.id);
const { pathname } = useLocation();
const isCopy = pathname.includes('copy');

useEffect(() => {
// 编辑,复制
if (id && !Number.isNaN(id)) {
getActiveLearnInfo(id);
}
}, [id]);

// 获取服务详情
const getActiveLearnInfo = async (id: number) => {
const [res] = await to(getActiveLearnInfoReq({ id }));
if (res && res.data) {
const info: ActiveLearnData = res.data;
const { name: name_str, ...rest } = info;
const name = isCopy ? `${name_str}-copy` : name_str;
const formData = {
...rest,
name,
};

form.setFieldsValue(formData);
}
};

// 创建、更新、复制实验
const createExperiment = async (formData: FormData) => {
// 根据后台要求,修改表单数据
const object = {
...formData,
};

const params =
id && !isCopy
? {
id: id,
...object,
}
: object;

const request = id && !isCopy ? updateActiveLearnReq : addActiveLearnReq;
const [res] = await to(request(params));
if (res) {
message.success('操作成功');
navigate(-1);
}
};

// 提交
const handleSubmit = (values: FormData) => {
createExperiment(values);
};

// 取消
const cancel = () => {
navigate(-1);
};

let buttonText = '新建';
let title = '新建实验';
if (id) {
if (isCopy) {
title = '复制实验';
buttonText = '确定';
} else {
title = '编辑实验';
buttonText = '更新';
}
}

return (
<div className={styles['create-hyperparameter']}>
<PageTitle title={title}></PageTitle>
<div className={styles['create-hyperparameter__content']}>
<div>
<Form
name="create-active-learn"
labelCol={{ flex: '160px' }}
labelAlign="left"
form={form}
onFinish={handleSubmit}
size="large"
autoComplete="off"
scrollToFirstError
initialValues={{
task_type: AutoMLTaskType.Classification,
shuffle: false,
}}
>
<BasicConfig />
<ExecuteConfig />

<Form.Item wrapperCol={{ offset: 0, span: 16 }} style={{ marginTop: '40px' }}>
<Button type="primary" htmlType="submit">
{buttonText}
</Button>
<Button
type="default"
htmlType="button"
onClick={cancel}
style={{ marginLeft: '20px' }}
>
取消
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
);
}

export default CreateActiveLearn;

+ 40
- 0
react-ui/src/pages/ActiveLearn/Info/index.less View File

@@ -0,0 +1,40 @@
.auto-ml-info {
position: relative;
height: 100%;
&__tabs {
height: 50px;
padding-left: 25px;
background-image: url(@/assets/img/page-title-bg.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100% 100%;
}

&__content {
height: calc(100% - 60px);
margin-top: 10px;
}

&__tips {
position: absolute;
top: 11px;
left: 256px;
padding: 3px 12px;
color: #565658;
font-size: @font-size-content;
background: .addAlpha(@primary-color, 0.09) [];
border-radius: 4px;

&::before {
position: absolute;
top: 10px;
left: -6px;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-right: 6px solid .addAlpha(@primary-color, 0.09) [];
border-bottom: 4px solid transparent;
content: '';
}
}
}

+ 45
- 0
react-ui/src/pages/ActiveLearn/Info/index.tsx View File

@@ -0,0 +1,45 @@
/*
* @Author: 赵伟
* @Date: 2024-04-16 13:58:08
* @Description: 主动学习实验详情
*/
import PageTitle from '@/components/PageTitle';
import { getActiveLearnInfoReq } from '@/services/activeLearn';
import { safeInvoke } from '@/utils/functional';
import { to } from '@/utils/promise';
import { useParams } from '@umijs/max';
import { useEffect, useState } from 'react';
import ActiveLearnBasic from '../components/ActiveLearnBasic';
import { ActiveLearnData } from '../types';
import styles from './index.less';

function ActiveLearnInfo() {
const params = useParams();
const id = safeInvoke(Number)(params.id);
const [info, setInfo] = useState<ActiveLearnData | undefined>(undefined);

useEffect(() => {
if (id) {
getActiveLearnInfo();
}
}, []);

// 获取详情
const getActiveLearnInfo = async () => {
const [res] = await to(getActiveLearnInfoReq({ id: id }));
if (res && res.data) {
setInfo(res.data);
}
};

return (
<div className={styles['auto-ml-info']}>
<PageTitle title="实验详情"></PageTitle>
<div className={styles['auto-ml-info__content']}>
<ActiveLearnBasic info={info} />
</div>
</div>
);
}

export default ActiveLearnInfo;

+ 42
- 0
react-ui/src/pages/ActiveLearn/Instance/index.less View File

@@ -0,0 +1,42 @@
.active-learn-instance {
height: 100%;

&__tabs {
height: 100%;
:global {
.ant-tabs-nav-list {
width: 100%;
height: 50px;
padding-left: 15px;
background-image: url(@/assets/img/page-title-bg.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100% 100%;
}

.ant-tabs-content-holder {
height: calc(100% - 50px);
.ant-tabs-content {
height: 100%;
.ant-tabs-tabpane {
height: 100%;
}
}
}
}
}

&__basic {
height: calc(100% - 10px);
margin-top: 10px;
}

&__log {
height: calc(100% - 10px);
margin-top: 10px;
padding: 20px calc(@content-padding - 8px);
overflow-y: visible;
background-color: white;
border-radius: 10px;
}
}

+ 194
- 0
react-ui/src/pages/ActiveLearn/Instance/index.tsx View File

@@ -0,0 +1,194 @@
import KFIcon from '@/components/KFIcon';
import { ExperimentStatus } from '@/enums';
import { getActiveLearnInsReq } from '@/services/activeLearn';
import { NodeStatus } from '@/types';
import { parseJsonText } from '@/utils';
import { safeInvoke } from '@/utils/functional';
import { to } from '@/utils/promise';
import { useParams } from '@umijs/max';
import { Tabs } from 'antd';
import { useEffect, useRef, useState } from 'react';
import ActiveLearnBasic from '../components/ActiveLearnBasic';
import ExperimentHistory from '../components/ExperimentHistory';
import ExperimentLog from '../components/ExperimentLog';
import ExperimentResult from '../components/ExperimentResult';
import { ActiveLearnData, ActiveLearnInstanceData } from '../types';
import styles from './index.less';

enum TabKeys {
Params = 'params',
Log = 'log',
Result = 'result',
History = 'history',
}

const NodePrefix = 'workflow';

function ActiveLearnInstance() {
const [experimentInfo, setExperimentInfo] = useState<ActiveLearnData | undefined>(undefined);
const [instanceInfo, setInstanceInfo] = useState<ActiveLearnInstanceData | undefined>(undefined);
// 超参数寻优运行有3个节点,运行状态取工作流状态,而不是 auto-hpo 节点状态
const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
const [nodes, setNodes] = useState<Record<string, NodeStatus> | undefined>(undefined);
const params = useParams();
const instanceId = safeInvoke(Number)(params.id);
const evtSourceRef = useRef<EventSource | null>(null);

useEffect(() => {
if (instanceId) {
getExperimentInsInfo(false);
}
return () => {
closeSSE();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [instanceId]);

// 获取实验实例详情
const getExperimentInsInfo = async (isStatusDetermined: boolean) => {
const [res] = await to(getActiveLearnInsReq(instanceId));
if (res && res.data) {
const info = res.data as ActiveLearnInstanceData;
const { param, node_status, argo_ins_name, argo_ins_ns, status } = info;
// 解析配置参数
const paramJson = parseJsonText(param);
if (paramJson) {
setExperimentInfo(paramJson.data);
}

setInstanceInfo(info);

// 这个接口返回的状态有延时,SSE 返回的状态是最新的
// SSE 调用时,不需要解析 node_status,也不要重新建立 SSE
if (isStatusDetermined) {
return;
}

// 进行节点状态
const nodeStatusJson = parseJsonText(node_status);
if (nodeStatusJson) {
setNodes(nodeStatusJson);
Object.keys(nodeStatusJson).some((key) => {
if (key.startsWith(NodePrefix)) {
const workflowStatus = nodeStatusJson[key];
setWorkflowStatus(workflowStatus);
return true;
}
return false;
});
}

// 运行中或者等待中,开启 SSE
if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) {
setupSSE(argo_ins_name, argo_ins_ns);
}
}
};

const setupSSE = (name: string, namespace: string) => {
const { origin } = location;
const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
const evtSource = new EventSource(
`${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
{ withCredentials: false },
);
evtSource.onmessage = (event) => {
const data = event?.data;
if (!data) {
return;
}
const dataJson = parseJsonText(data);
if (dataJson) {
const nodes = dataJson?.result?.object?.status?.nodes;
if (nodes) {
const workflowStatus = Object.values(nodes).find((node: any) =>
node.displayName.startsWith(NodePrefix),
) as NodeStatus;

// 节点
setNodes(nodes);

// 设置工作流状态
if (workflowStatus) {
setWorkflowStatus(workflowStatus);

// 实验结束,关闭 SSE
if (
workflowStatus.phase !== ExperimentStatus.Pending &&
workflowStatus.phase !== ExperimentStatus.Running
) {
closeSSE();
getExperimentInsInfo(true);
}
}
}
}
};
evtSource.onerror = (error) => {
console.error('SSE error: ', error);
};

evtSourceRef.current = evtSource;
};

const closeSSE = () => {
if (evtSourceRef.current) {
evtSourceRef.current.close();
evtSourceRef.current = null;
}
};

const basicTabItems = [
{
key: TabKeys.Params,
label: '基本信息',
icon: <KFIcon type="icon-jibenxinxi" />,
children: (
<ActiveLearnBasic
className={styles['active-learn-instance__basic']}
info={experimentInfo}
runStatus={workflowStatus}
isInstance
/>
),
},
{
key: TabKeys.Log,
label: '日志',
icon: <KFIcon type="icon-rizhi1" />,
children: (
<div className={styles['active-learn-instance__log']}>
{instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
</div>
),
},
];

const resultTabItems = [
{
key: TabKeys.Result,
label: '实验结果',
icon: <KFIcon type="icon-shiyanjieguo1" />,
children: <ExperimentResult fileUrl={instanceInfo?.result_txt} />,
},
{
key: TabKeys.History,
label: '寻优列表',
icon: <KFIcon type="icon-Trialliebiao" />,
children: <ExperimentHistory trialList={instanceInfo?.trial_list ?? []} />,
},
];

const tabItems =
instanceInfo?.status === ExperimentStatus.Succeeded
? [...basicTabItems, ...resultTabItems]
: basicTabItems;

return (
<div className={styles['active-learn-instance']}>
<Tabs className={styles['active-learn-instance__tabs']} items={tabItems} />
</div>
);
}

export default ActiveLearnInstance;

+ 13
- 0
react-ui/src/pages/ActiveLearn/List/index.tsx View File

@@ -0,0 +1,13 @@
/*
* @Author: 赵伟
* @Date: 2024-04-16 13:58:08
* @Description: 超参数自动寻优
*/

import ExperimentList, { ExperimentListType } from '@/pages/AutoML/components/ExperimentList';

function ActiveLearn() {
return <ExperimentList type={ExperimentListType.ActiveLearn} />;
}

export default ActiveLearn;

+ 13
- 0
react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.less View File

@@ -0,0 +1,13 @@
.active-learn-basic {
height: 100%;
padding: 20px @content-padding;
overflow-y: auto;
background-color: white;
border-radius: 10px;

:global {
.kf-basic-info__item__value__text {
white-space: pre;
}
}
}

+ 278
- 0
react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx View File

@@ -0,0 +1,278 @@
import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo';
import { AutoMLTaskType, autoMLTaskTypeOptions } from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import {
classifierAlgorithms,
FrameworkType,
frameworkTypeOptions,
queryStrategies,
regressorAlgorithms,
} from '@/pages/ActiveLearn/components/CreateForm/utils';
import { ActiveLearnData } from '@/pages/ActiveLearn/types';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import { type NodeStatus } from '@/types';
import { elapsedTime } from '@/utils/date';
import {
formatBoolean,
formatCodeConfig,
formatDataset,
formatDate,
formatEnum,
formatMirror,
formatModel,
} from '@/utils/format';
import { Flex } from 'antd';
import classNames from 'classnames';
import { useMemo } from 'react';
import styles from './index.less';

type BasicInfoProps = {
info?: ActiveLearnData;
className?: string;
isInstance?: boolean;
runStatus?: NodeStatus;
};

function BasicInfo({ info, className, runStatus, isInstance = false }: BasicInfoProps) {
const getResourceDescription = useComputingResource()[1];
const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];
}

return [
{
label: '实验名称',
value: info.name,
},
{
label: '实验描述',
value: info.description,
},
{
label: '创建人',
value: info.create_by,
},
{
label: '创建时间',
value: info.create_time,
format: formatDate,
},
{
label: '更新时间',
value: info.update_time,
format: formatDate,
},
];
}, [info]);

const configDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];
}

const modelInfo = [
{
label: '预训练模型',
value: info.model,
format: formatModel,
},
{
label: '模型文件路径',
value: info.model_py,
},
{
label: '模型类名称',
value: info.model_class_name,
},
{
label: 'epochs',
value: info.epochs,
},
];

const lossInfo = [
{
label: 'loss文件路径',
value: info.loss_py,
},
{
label: 'loss类名',
value: info.loss_class_name,
},
{
label: '学习率',
value: info.lr,
},
];

const algorithmInfo = [
{
label: info.task_type === AutoMLTaskType.Regression ? '回归算法' : '分类算法',
value:
info.task_type === AutoMLTaskType.Regression ? info.regressor_alg : info.classifier_alg,
format: formatEnum(
info.task_type === AutoMLTaskType.Regression ? regressorAlgorithms : classifierAlgorithms,
),
},
];

const diffInfo =
info.framework_type === FrameworkType.Pytorch
? [...modelInfo, ...lossInfo]
: info.framework_type === FrameworkType.Keras
? modelInfo
: algorithmInfo;

return [
{
label: '任务类型',
value: info.task_type,
format: formatEnum(autoMLTaskTypeOptions),
},
{
label: '框架类型',
value: info.framework_type,
format: formatEnum(frameworkTypeOptions),
},
...diffInfo,
{
label: '代码配置',
value: info.code_config,
format: formatCodeConfig,
},
{
label: '数据集',
value: info.dataset,
format: formatDataset,
},
{
label: '数据集处理文件路径',
value: info.dataset_py,
},
{
label: '数据集类名',
value: info.dataset_class_name,
},
{
label: '镜像',
value: info.image,
format: formatMirror,
},
{
label: '资源规格',
value: info.computing_resource_id,
format: getResourceDescription,
},
{
label: '是否打乱',
value: info.shuffle,
format: formatBoolean,
},
{
label: '数据量',
value: info.data_size,
},
{
label: '训练集数据量',
value: info.train_size,
},
{
label: '初始训练数据量',
value: info.initial_num,
},
{
label: '查询次数',
value: info.queries_num,
},
{
label: '每次查询数据量',
value: info.instances_num,
},
{
label: '查询策略',
value: info.query_strategy,
format: formatEnum(queryStrategies),
},
{
label: '检查点轮数',
value: info.checkpoint_num,
},
{
label: 'batch_size',
value: info.batch_size,
},
];
}, [info, getResourceDescription]);

const instanceDatas = useMemo(() => {
if (!runStatus) {
return [];
}

return [
{
label: '启动时间',
value: formatDate(runStatus.startedAt),
ellipsis: true,
},
{
label: '执行时长',
value: elapsedTime(runStatus.startedAt, runStatus.finishedAt),
ellipsis: true,
},
{
label: '状态',
value: (
<Flex align="center">
<img
style={{ width: '17px', marginRight: '7px' }}
src={experimentStatusInfo[runStatus.phase]?.icon}
draggable={false}
alt=""
/>
<div
style={{
color: experimentStatusInfo[runStatus?.phase]?.color,
fontSize: '15px',
lineHeight: 1.6,
}}
>
{experimentStatusInfo[runStatus?.phase]?.label}
</div>
</Flex>
),
ellipsis: true,
},
];
}, [runStatus]);

return (
<div className={classNames(styles['active-learn-basic'], className)}>
{isInstance && runStatus && (
<ConfigInfo
title="运行信息"
datas={instanceDatas}
labelWidth={70}
style={{ marginBottom: '20px' }}
/>
)}
{!isInstance && (
<ConfigInfo
title="基本信息"
datas={basicDatas}
labelWidth={70}
style={{ marginBottom: '20px' }}
/>
)}
<ConfigInfo
title="配置信息"
datas={configDatas}
labelWidth={120}
style={{ marginBottom: '20px' }}
></ConfigInfo>
</div>
);
}

export default BasicInfo;

+ 54
- 0
react-ui/src/pages/ActiveLearn/components/CreateForm/BasicConfig.tsx View File

@@ -0,0 +1,54 @@
import SubAreaTitle from '@/components/SubAreaTitle';
import { Col, Form, Input, Row } from 'antd';

function BasicConfig() {
return (
<>
<SubAreaTitle
title="基本信息"
image={require('@/assets/img/mirror-basic.png')}
style={{ marginBottom: '26px' }}
></SubAreaTitle>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="实验名称"
name="name"
rules={[
{
required: true,
message: '请输入实验名称',
},
]}
>
<Input placeholder="请输入实验名称" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={20}>
<Form.Item
label="实验描述"
name="description"
rules={[
{
required: true,
message: '请输入实验描述',
},
]}
>
<Input.TextArea
autoSize={{ minRows: 2, maxRows: 6 }}
placeholder="请输入实验描述"
maxLength={256}
showCount
allowClear
/>
</Form.Item>
</Col>
</Row>
</>
);
}

export default BasicConfig;

+ 518
- 0
react-ui/src/pages/ActiveLearn/components/CreateForm/ExecuteConfig.tsx View File

@@ -0,0 +1,518 @@
import CodeSelect from '@/components/CodeSelect';
import ParameterSelect from '@/components/ParameterSelect';
import ResourceSelect, {
requiredValidator,
ResourceSelectorType,
} from '@/components/ResourceSelect';
import SubAreaTitle from '@/components/SubAreaTitle';
import { AutoMLTaskType, autoMLTaskTypeOptions } from '@/enums';
import { Col, Form, Input, InputNumber, Radio, Row, Select, Switch } from 'antd';
import {
classifierAlgorithms,
FrameworkType,
frameworkTypeOptions,
queryStrategies,
regressorAlgorithms,
} from './utils';

function ExecuteConfig() {
const form = Form.useFormInstance();
return (
<>
<SubAreaTitle
title="配置信息"
image={require('@/assets/img/model-deployment.png')}
style={{ marginTop: '20px', marginBottom: '24px' }}
></SubAreaTitle>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="任务类型"
name="task_type"
rules={[{ required: true, message: '请选择任务类型' }]}
>
<Radio.Group
options={autoMLTaskTypeOptions}
onChange={() => form.resetFields(['metrics'])}
></Radio.Group>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="框架类型"
name="framework_type"
rules={[
{
required: true,
message: '请选择框架类型',
},
]}
>
<Select
placeholder="请选择框架类型"
options={frameworkTypeOptions}
showSearch
allowClear
/>
</Form.Item>
</Col>
</Row>

<Form.Item dependencies={['task_type', 'framework_type']} noStyle>
{({ getFieldValue }) => {
const taskType = getFieldValue('task_type');
const frameworkType = getFieldValue('framework_type');
if (frameworkType === FrameworkType.Keras || frameworkType === FrameworkType.Pytorch) {
return (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="预训练模型" name="model">
<ResourceSelect
type={ResourceSelectorType.Model}
placeholder="请选择模型"
canInput={false}
size="large"
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="模型文件路径"
name="model_py"
rules={[
{
required: true,
message: '请输入模型文件路径',
},
]}
>
<Input placeholder="请输入模型文件路径" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="模型类名称"
name="model_class_name"
rules={[
{
required: true,
message: '请输入模型类名称',
},
]}
>
<Input placeholder="请输入模型类名称" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="epochs"
name="epochs"
rules={[
{
required: true,
message: '请输入epochs',
},
]}
>
<InputNumber placeholder="请输入epochs" min={0} precision={0} />
</Form.Item>
</Col>
</Row>
{frameworkType === FrameworkType.Pytorch ? (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="loss 文件路径"
name="loss_py"
rules={[
{
required: true,
message: '请输入 loss 文件路径',
},
]}
>
<Input
placeholder="请输入 loss 文件路径"
maxLength={64}
showCount
allowClear
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="loss 类名"
name="loss_class_name"
rules={[
{
required: true,
message: '请输入 loss 类名',
},
]}
>
<Input
placeholder="请输入 loss 类名"
maxLength={64}
showCount
allowClear
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="学习率"
name="lr"
rules={[
{
required: true,
message: '请输入学习率',
},
]}
>
<InputNumber placeholder="请输入学习率" min={0} />
</Form.Item>
</Col>
</Row>
</>
) : null}
</>
);
} else if (frameworkType === FrameworkType.Sklearn) {
if (taskType === AutoMLTaskType.Classification) {
return (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="分类算法"
name="classifier_alg"
rules={[
{
required: true,
message: '请选择分类算法',
},
]}
>
<Select
placeholder="请选择分类算法"
options={classifierAlgorithms}
showSearch
allowClear
/>
</Form.Item>
</Col>
</Row>
</>
);
} else {
return (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="回归算法"
name="regressor_alg"
rules={[
{
required: true,
message: '请选择回归算法',
},
]}
>
<Select
placeholder="请选择回归算法"
options={regressorAlgorithms}
showSearch
allowClear
/>
</Form.Item>
</Col>
</Row>
</>
);
}
} else {
return null;
}
}}
</Form.Item>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="代码配置"
name="code_config"
rules={[
{
validator: requiredValidator,
message: '请选择代码配置',
},
]}
required
>
<CodeSelect placeholder="请选择代码配置" canInput={false} size="large" />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="数据集"
name="dataset"
rules={[
{
validator: requiredValidator,
message: '请选择数据集',
},
]}
required
>
<ResourceSelect
type={ResourceSelectorType.Dataset}
placeholder="请选择数据集"
canInput={false}
size="large"
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="数据集处理文件路径"
name="dataset_py"
rules={[
{
required: true,
message: '请输入数据集处理文件路径',
},
]}
>
<Input placeholder="请输入数据集处理文件路径" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="数据集类名"
name="dataset_class_name"
rules={[
{
required: true,
message: '请输入数据集类名',
},
]}
>
<Input placeholder="请输入数据集类名" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="数据量"
name="data_size"
rules={[
{
required: true,
message: '请输入数据量',
},
]}
>
<InputNumber placeholder="请输入数据量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="镜像"
name="image"
rules={[
{
validator: requiredValidator,
message: '请选择镜像',
},
]}
required
>
<ResourceSelect
type={ResourceSelectorType.Mirror}
placeholder="请选择镜像"
canInput={false}
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="资源规格"
name="computing_resource_id"
rules={[
{
required: true,
message: '请选择资源规格',
},
]}
>
<ParameterSelect dataType="resource" placeholder="请选择资源规格" />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="是否随机打乱" name="shuffle" valuePropName="checked">
<Switch />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="训练集数据量"
name="train_size"
rules={[
{
required: true,
message: '请输入训练集数据量',
},
]}
>
<InputNumber placeholder="请输入训练集数据量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="初始训练数据量"
name="initial_num"
rules={[
{
required: true,
message: '请输入初始训练数据量',
},
]}
>
<InputNumber placeholder="请输入初始训练数据量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="查询次数"
name="queries_num"
rules={[
{
required: true,
message: '请输入查询次数量',
},
]}
>
<InputNumber placeholder="请输入查询次数" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="每次查询数据量"
name="instances_num"
rules={[
{
required: true,
message: '请输入每次查询数据量',
},
]}
>
<InputNumber placeholder="请输入每次查询数据量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="查询策略"
name="query_strategy"
rules={[
{
required: true,
message: '请选择查询策略',
},
]}
>
<Select placeholder="请选择查询策略" options={queryStrategies} showSearch allowClear />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="检查点轮数"
name="checkpoint_num"
rules={[
{
required: true,
message: '请输入检查点轮数',
},
]}
tooltip="多少轮查询保存一次模型参数"
>
<InputNumber placeholder="请输入检查点轮数" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="batch_size"
name="batch_size"
rules={[
{
required: true,
message: '请输入 batch_size',
},
]}
>
<InputNumber placeholder="请输入 batch_size" min={0} precision={0} />
</Form.Item>
</Col>
</Row>
</>
);
}

export default ExecuteConfig;

+ 92
- 0
react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts View File

@@ -0,0 +1,92 @@
// 分类算法
export const classifierAlgorithms = [
{
label: 'logistic_regression(逻辑回归)',
value: 'logistic_regression',
},
{
label: 'decision_tree(决策树)',
value: 'decision_tree',
},
{
label: 'random_forest(随机森林)',
value: 'random_forest',
},
{
label: 'SVM(支持向量机)',
value: 'SVM',
},
{
label: 'naive_bayes(朴素贝叶斯)',
value: 'naive_bayes',
},
{
label: 'GBM(梯度提升树)',
value: 'GBM',
},
];

// 回归算法
export const regressorAlgorithms = [
{
label: 'bayesian_ridge(岭回归)',
value: 'bayesian_ridge',
},
{
label: 'ARD_regression(自动相关性确定回归)',
value: 'ARD_regression',
},
{
label: 'gaussian_process(高斯回归)',
value: 'gaussian_process',
}
];

// 框架类型
export enum FrameworkType {
Sklearn = 'sklearn',
Keras = 'keras',
Pytorch = 'pytorch',
}

// 框架类型选项
export const frameworkTypeOptions = [
{
label: FrameworkType.Sklearn,
value: FrameworkType.Sklearn,
},
{
label: FrameworkType.Keras,
value: FrameworkType.Keras,
},
{
label: FrameworkType.Pytorch,
value: FrameworkType.Pytorch,
},
];


// 查询策略
export const queryStrategies = [
{
label: 'uncertainty_sampling',
value: 'uncertainty_sampling',
},
{
label: 'uncertainty_batch_sampling',
value: 'uncertainty_batch_sampling',
},
{
label: 'max_std_sampling',
value: 'max_std_sampling',
},
{
label: 'expected_improvement',
value: 'expected_improvement',
},
{
label: 'upper_confidence_bound',
value: 'upper_confidence_bound',
}
];


+ 14
- 0
react-ui/src/pages/ActiveLearn/components/ExperimentHistory/index.less View File

@@ -0,0 +1,14 @@
.experiment-history {
height: calc(100% - 10px);
margin-top: 10px;
&__content {
height: 100%;
padding: 20px @content-padding;
background-color: white;
border-radius: 10px;

&__table {
height: 100%;
}
}
}

+ 132
- 0
react-ui/src/pages/ActiveLearn/components/ExperimentHistory/index.tsx View File

@@ -0,0 +1,132 @@
import { getFileReq } from '@/services/file';
import { to } from '@/utils/promise';
import tableCellRender from '@/utils/table';
import { Table, type TableProps } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import styles from './index.less';

type ExperimentHistoryProps = {
fileUrl?: string;
isClassification: boolean;
};

type TableData = {
id?: string;
accuracy?: number;
duration?: number;
train_loss?: number;
status?: string;
feature?: string;
althorithm?: string;
};

function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps) {
const [tableData, setTableData] = useState<TableData[]>([]);
useEffect(() => {
if (fileUrl) {
getHistoryFile();
}
}, [fileUrl]);

// 获取实验运行历史记录
const getHistoryFile = async () => {
const [res] = await to(getFileReq(fileUrl));
if (res) {
const data: any[] = res.data;
const list: TableData[] = data.map((item) => {
return {
id: item[0]?.[0],
accuracy: item[1]?.[5]?.accuracy,
duration: item[1]?.[5]?.duration,
train_loss: item[1]?.[5]?.train_loss,
status: item[1]?.[2]?.['__enum__']?.split('.')?.[1],
};
});
list.forEach((item) => {
if (!item.id) return;
const config = (res as any).configs?.[item.id];
item.feature = config?.['feature_preprocessor:__choice__'];
item.althorithm = isClassification
? config?.['classifier:__choice__']
: config?.['regressor:__choice__'];
});
setTableData(list);
}
};

const columns: TableProps<TableData>['columns'] = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
render: tableCellRender(false),
},
{
title: '准确率',
dataIndex: 'accuracy',
key: 'accuracy',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '耗时',
dataIndex: 'duration',
key: 'duration',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '训练损失',
dataIndex: 'train_loss',
key: 'train_loss',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '特征处理',
dataIndex: 'feature',
key: 'feature',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '算法',
dataIndex: 'althorithm',
key: 'althorithm',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
render: tableCellRender(false),
},
];

return (
<div className={styles['experiment-history']}>
<div className={styles['experiment-history__content']}>
<div
className={classNames(
'vertical-scroll-table-no-page',
styles['experiment-history__content__table'],
)}
>
<Table
dataSource={tableData}
columns={columns}
pagination={false}
scroll={{ y: 'calc(100% - 55px)' }}
rowKey="id"
/>
</div>
</div>
</div>
);
}

export default ExperimentHistory;

+ 16
- 0
react-ui/src/pages/ActiveLearn/components/ExperimentLog/index.less View File

@@ -0,0 +1,16 @@
.experiment-log {
height: 100%;
&__tabs {
height: 100%;
:global {
.ant-tabs-nav-list {
padding-left: 0 !important;
background: none !important;
}
}

&__log {
height: 100%;
}
}
}

+ 106
- 0
react-ui/src/pages/ActiveLearn/components/ExperimentLog/index.tsx View File

@@ -0,0 +1,106 @@
import { ExperimentStatus } from '@/enums';
import { ActiveLearnInstanceData } from '@/pages/ActiveLearn/types';
import LogList from '@/pages/Experiment/components/LogList';
import { NodeStatus } from '@/types';
import { Tabs } from 'antd';
import styles from './index.less';

type ExperimentLogProps = {
instanceInfo: ActiveLearnInstanceData;
nodes: Record<string, NodeStatus>;
};

function ExperimentLog({ instanceInfo, nodes }: ExperimentLogProps) {
let hpoNodeStatus: NodeStatus | undefined;
let frameworkCloneNodeStatus: NodeStatus | undefined;
let trainCloneNodeStatus: NodeStatus | undefined;

Object.keys(nodes)
.sort((key1, key2) => {
const node1 = nodes[key1];
const node2 = nodes[key2];
return new Date(node1.startedAt).getTime() - new Date(node2.startedAt).getTime();
})
.forEach((key) => {
const node = nodes[key];
if (node.displayName.startsWith('active-learn')) {
hpoNodeStatus = node;
} else if (node.displayName.startsWith('git-clone') && !frameworkCloneNodeStatus) {
frameworkCloneNodeStatus = node;
} else if (
node.displayName.startsWith('git-clone') &&
frameworkCloneNodeStatus &&
node.displayName !== frameworkCloneNodeStatus?.displayName
) {
trainCloneNodeStatus = node;
}
});

const tabItems = [
// {
// key: 'git-clone-framework',
// label: '框架代码日志',
// // icon: <KFIcon type="icon-rizhi1" />,
// children: (
// <div className={styles['experiment-log__tabs__log']}>
// {frameworkCloneNodeStatus && (
// <LogList
// instanceName={instanceInfo.argo_ins_name}
// instanceNamespace={instanceInfo.argo_ins_ns}
// pipelineNodeId={frameworkCloneNodeStatus.displayName}
// workflowId={frameworkCloneNodeStatus.id}
// instanceNodeStartTime={frameworkCloneNodeStatus.startedAt}
// instanceNodeStatus={frameworkCloneNodeStatus.phase as ExperimentStatus}
// ></LogList>
// )}
// </div>
// ),
// },
{
key: 'git-clone-train',
label: '系统日志',
// icon: <KFIcon type="icon-rizhi1" />,
children: (
<div className={styles['experiment-log__tabs__log']}>
{trainCloneNodeStatus && (
<LogList
instanceName={instanceInfo.argo_ins_name}
instanceNamespace={instanceInfo.argo_ins_ns}
pipelineNodeId={trainCloneNodeStatus.displayName}
workflowId={trainCloneNodeStatus.id}
instanceNodeStartTime={trainCloneNodeStatus.startedAt}
instanceNodeStatus={trainCloneNodeStatus.phase as ExperimentStatus}
></LogList>
)}
</div>
),
},
{
key: 'active-learn',
label: '主动学习日志',
// icon: <KFIcon type="icon-rizhi1" />,
children: (
<div className={styles['experiment-log__tabs__log']}>
{hpoNodeStatus && (
<LogList
instanceName={instanceInfo.argo_ins_name}
instanceNamespace={instanceInfo.argo_ins_ns}
pipelineNodeId={hpoNodeStatus.displayName}
workflowId={hpoNodeStatus.id}
instanceNodeStartTime={hpoNodeStatus.startedAt}
instanceNodeStatus={hpoNodeStatus.phase as ExperimentStatus}
></LogList>
)}
</div>
),
},
];

return (
<div className={styles['experiment-log']}>
<Tabs className={styles['experiment-log__tabs']} items={tabItems} />
</div>
);
}

export default ExperimentLog;

+ 52
- 0
react-ui/src/pages/ActiveLearn/components/ExperimentResult/index.less View File

@@ -0,0 +1,52 @@
.experiment-result {
height: calc(100% - 10px);
margin-top: 10px;
padding: 20px @content-padding;
overflow-y: auto;
background-color: white;
border-radius: 10px;

&__download {
padding-top: 16px;
padding-bottom: 16px;

padding-left: @content-padding;
color: @text-color;
font-size: 13px;
background-color: #f8f8f9;
border-radius: 4px;

&__btn {
display: block;
height: 36px;
margin-top: 15px;
font-size: 14px;
}
}

&__text {
white-space: pre-wrap;
}

&__images {
display: flex;
align-items: flex-start;
width: 100%;
overflow-x: auto;

:global {
.ant-image {
margin-right: 20px;

&:last-child {
margin-right: 0;
}
}
}

&__item {
height: 248px;
border: 1px solid rgba(96, 107, 122, 0.3);
}
}
}

+ 83
- 0
react-ui/src/pages/ActiveLearn/components/ExperimentResult/index.tsx View File

@@ -0,0 +1,83 @@
import InfoGroup from '@/components/InfoGroup';
import { getFileReq } from '@/services/file';
import { to } from '@/utils/promise';
import { Button, Image } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import styles from './index.less';

type ExperimentResultProps = {
fileUrl?: string;
imageUrl?: string;
modelPath?: string;
};

function ExperimentResult({ fileUrl, imageUrl, modelPath }: ExperimentResultProps) {
const [result, setResult] = useState<string | undefined>('');

const images = useMemo(() => {
if (imageUrl) {
return imageUrl.split(',').map((item) => item.trim());
}
return [];
}, [imageUrl]);

useEffect(() => {
if (fileUrl) {
getResultFile();
}
}, [fileUrl]);

// 获取实验运行历史记录
const getResultFile = async () => {
const [res] = await to(getFileReq(fileUrl));
if (res) {
setResult(res as any as string);
}
};

return (
<div className={styles['experiment-result']}>
<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>
{modelPath && (
<div className={styles['experiment-result__download']}>
<span style={{ marginRight: '12px', color: '#606b7a' }}>文件名</span>
<span>save_model.joblib</span>
<Button
type="primary"
className={styles['experiment-result__download__btn']}
onClick={() => {
window.location.href = modelPath;
}}
>
模型下载
</Button>
</div>
)}
</div>
);
}

export default ExperimentResult;

+ 120
- 0
react-ui/src/pages/ActiveLearn/data.json View File

@@ -0,0 +1,120 @@
{
"workflow-xwnb8": {
"id": "workflow-xwnb8",
"name": "workflow-xwnb8",
"type": "DAG",
"phase": "Failed",
"children": [
"workflow-xwnb8-1083129199"
],
"progress": "2/3",
"startedAt": "2025-04-18T06:56:18Z",
"finishedAt": "2025-04-18T06:57:32Z",
"displayName": "workflow-xwnb8",
"templateName": "ml-workflow",
"outboundNodes": [
"workflow-xwnb8-1355608520"
],
"templateScope": "local/workflow-xwnb8",
"resourcesDuration": {
"cpu": 42,
"memory": 851,
"nvidia.com/gpu": 10
}
},
"git-clone-9d0c5965": {
"id": "workflow-xwnb8-514970004",
"name": "workflow-xwnb8.git-clone-9d0c5965",
"type": "Pod",
"phase": "Succeeded",
"outputs": {
"exitCode": "0",
"artifacts": [
{
"s3": {
"key": "workflow-xwnb8/workflow-xwnb8-git-clone-9d0c5965-514970004/main.log"
},
"name": "main-logs"
}
]
},
"children": [
"workflow-xwnb8-1355608520"
],
"progress": "1/1",
"startedAt": "2025-04-18T06:56:38Z",
"boundaryID": "workflow-xwnb8",
"finishedAt": "2025-04-18T06:56:49Z",
"displayName": "git-clone-9d0c5965",
"hostNodeName": "k8s-node01",
"templateName": "git-clone-9d0c5965",
"templateScope": "local/workflow-xwnb8",
"resourcesDuration": {
"cpu": 1,
"memory": 11
}
},
"git-clone-e28c560c": {
"id": "workflow-xwnb8-1083129199",
"name": "workflow-xwnb8.git-clone-e28c560c",
"type": "Pod",
"phase": "Succeeded",
"outputs": {
"exitCode": "0",
"artifacts": [
{
"s3": {
"key": "workflow-xwnb8/workflow-xwnb8-git-clone-e28c560c-1083129199/main.log"
},
"name": "main-logs"
}
]
},
"children": [
"workflow-xwnb8-514970004"
],
"progress": "1/1",
"startedAt": "2025-04-18T06:56:18Z",
"boundaryID": "workflow-xwnb8",
"finishedAt": "2025-04-18T06:56:27Z",
"displayName": "git-clone-e28c560c",
"hostNodeName": "k8s-node01",
"templateName": "git-clone-e28c560c",
"templateScope": "local/workflow-xwnb8",
"resourcesDuration": {
"cpu": 1,
"memory": 11
}
},
"active-learn-b708ed0b": {
"id": "workflow-xwnb8-1355608520",
"name": "workflow-xwnb8.active-learn-b708ed0b",
"type": "Pod",
"phase": "Failed",
"message": "Error (exit code 1)",
"outputs": {
"exitCode": "1",
"artifacts": [
{
"s3": {
"key": "workflow-xwnb8/workflow-xwnb8-active-learn-b708ed0b-1355608520/main.log"
},
"name": "main-logs"
}
]
},
"progress": "0/1",
"startedAt": "2025-04-18T06:57:00Z",
"boundaryID": "workflow-xwnb8",
"finishedAt": "2025-04-18T06:57:27Z",
"displayName": "active-learn-b708ed0b",
"hostNodeName": "k8s-node01",
"templateName": "active-learn-b708ed0b",
"templateScope": "local/workflow-xwnb8",
"resourcesDuration": {
"cpu": 40,
"memory": 829,
"nvidia.com/gpu": 10
}
}
}

+ 85
- 0
react-ui/src/pages/ActiveLearn/types.ts View File

@@ -0,0 +1,85 @@
/*
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2025-04-18 08:40:03
* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @LastEditTime: 2025-04-18 11:30:21
* @FilePath: \ci4s\react-ui\src\pages\ActiveLearn\types.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { type ParameterInputObject } from '@/components/ResourceSelect';
import { type NodeStatus } from '@/types';
import { AutoMLTaskType } from '@/enums';

// 操作类型
export enum OperationType {
Create = 'Create', // 创建
Update = 'Update', // 更新
}

// 表单数据
export type FormData = {
name: string; // 实验名称
description: string; // 实验描述
task_type: AutoMLTaskType; // 任务类型
framework_type: string; // 框架类型
code_config: ParameterInputObject; // 代码配置
model?: ParameterInputObject; // 模型
model_py?: string; // 模型文件路径
model_class_name?: string; // 模型类名称
loss_py?: string; // loss文件路径
loss_class_name?: string; // loss类名
classifier_alg?: string; // 分类算法
regressor_alg?: string; // 回归算法
dataset: ParameterInputObject; // 数据集
dataset_py: string; // dataset处理文件路径
dataset_class_name: string; // dataset类名
data_size: number; // 数据量
train_size: number; // 训练集数据量
initial_num: number; // 初始训练数据量
queries_num: number; // 查询次数
instances_num: number; // 每次查询数据量
computing_resource_id: number; // 资源规格
image: ParameterInputObject; // 镜像
shuffle: boolean; // 是否随机打乱
query_strategy: string; // 查询策略
checkpoint_num: number; // 多少轮查询保存一次模型参数
batch_size: number; // batch_size
epochs: number; // epochs
lr: number; // 学习率
};

// 主动学习
export type ActiveLearnData = {
id: number;
progress: number;
run_state: string;
state: number;
create_by?: string;
create_time?: string;
update_by?: string;
update_time?: string;
status_list: string; // 最近五次运行状态
} & FormData;

// 主动学习实验实例
export type ActiveLearnInstanceData = {
id: number;
auto_ml_id: number;
result_path: string;
model_path: string;
img_path: string;
run_history_path: string;
state: number;
status: string;
node_status: string;
node_result: string;
param: string;
source: string | null;
argo_ins_name: string;
argo_ins_ns: string;
create_time: string;
update_time: string;
finish_time: string;
nodeStatus?: NodeStatus;
};

+ 24
- 0
react-ui/src/pages/AutoML/components/ExperimentList/config.ts View File

@@ -4,6 +4,15 @@
* @Description: 实验列表组件配置
*/

import {
batchDeleteActiveLearnInsReq,
deleteActiveLearnInsReq,
deleteActiveLearnReq,
getActiveLearnInsListReq,
getActiveLearnListReq,
runActiveLearnReq,
stopActiveLearnInsReq,
} from '@/services/activeLearn';
import {
batchDeleteExperimentInsReq,
deleteAutoMLReq,
@@ -26,6 +35,7 @@ import {
export enum ExperimentListType {
AutoML = 'AutoML',
HyperParameter = 'HyperParameter',
ActiveLearn = 'ActiveLearn',
}

type ExperimentListInfo = {
@@ -72,4 +82,18 @@ export const experimentListConfig: Record<ExperimentListType, ExperimentListInfo
descProperty: 'description',
idProperty: 'rayId',
},
[ExperimentListType.ActiveLearn]: {
getListReq: getActiveLearnListReq,
getInsListReq: getActiveLearnInsListReq,
deleteRecordReq: deleteActiveLearnReq,
runRecordReq: runActiveLearnReq,
deleteInsReq: deleteActiveLearnInsReq,
batchDeleteInsReq: batchDeleteActiveLearnInsReq,
stopInsReq: stopActiveLearnInsReq,
title: '自动学习',
pathPrefix: 'active-learn',
nameProperty: 'name',
descProperty: 'description',
idProperty: 'activeLearnId',
},
};

+ 3
- 3
react-ui/src/pages/AutoML/components/ExperimentList/index.tsx View File

@@ -63,7 +63,7 @@ function ExperimentList({ type }: ExperimentListProps) {
const params: Record<string, any> = {
page: pagination.current! - 1,
size: pagination.pageSize,
ml_name: searchText || undefined,
[config.nameProperty]: searchText || undefined,
};
const request = config.getListReq;
const [res] = await to(request(params));
@@ -248,7 +248,7 @@ function ExperimentList({ type }: ExperimentListProps) {
{
title: '实验名称',
dataIndex: config.nameProperty,
key: 'ml_name',
key: 'name',
width: '16%',
render: tableCellRender(false, TableCellValueType.Link, {
onClick: gotoDetail,
@@ -257,7 +257,7 @@ function ExperimentList({ type }: ExperimentListProps) {
{
title: '实验描述',
dataIndex: config.descProperty,
key: 'ml_description',
key: 'description',
render: tableCellRender(true),
},
{


+ 1
- 3
react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx View File

@@ -55,12 +55,10 @@ function EditorCreate() {
// 创建编辑器
const createEditor = async (formData: FormData) => {
// 根据后台要求,修改表单数据
const image = formData['image'];
const model = formData['model'];
const dataset = formData['dataset'];
const params = {
...omit(formData, ['image', 'model', 'dataset']),
image: image.value,
...omit(formData, ['model', 'dataset']),
model: model && pick(model, ['id', 'version', 'path', 'showValue']),
dataset: dataset && pick(dataset, ['id', 'version', 'path', 'showValue']),
};


+ 2
- 1
react-ui/src/pages/DevelopmentEnvironment/List/index.tsx View File

@@ -77,6 +77,7 @@ function EditorList() {
content.forEach((item: EditorData) => {
item.dataset = typeof item.dataset === 'string' ? parseJsonText(item.dataset) : null;
item.model = typeof item.model === 'string' ? parseJsonText(item.model) : null;
item.image = typeof item.image === 'string' ? parseJsonText(item.image) : null;
});
setTableData(content);
setTotal(totalElements);
@@ -224,7 +225,7 @@ function EditorList() {
},
{
title: '镜像',
dataIndex: ['image'],
dataIndex: ['image', 'showValue'],
key: 'image',
width: '15%',
render: tableCellRender(true),


+ 1
- 4
react-ui/src/pages/Experiment/components/LogGroup/index.tsx View File

@@ -54,10 +54,7 @@ function LogGroup({
useEffect(() => {
// 建立 socket 连接
const setupSockect = () => {
let { host } = location;
if (process.env.NODE_ENV === 'development') {
host = '172.20.32.197:31213';
}
const { host } = location;
const socket = new WebSocket(
`ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`,
);


+ 1
- 1
react-ui/src/pages/HyperParameter/Info/index.tsx View File

@@ -20,7 +20,7 @@ function HyperparameterInfo() {
undefined,
);

// 获取自动机器学习详情
// 获取详情
const getHyperparameterInfo = useCallback(async () => {
const [res] = await to(getRayInfoReq({ id: hyperparameterId }));
if (res && res.data) {


+ 0
- 3
react-ui/src/pages/HyperParameter/components/ExperimentLog/index.tsx View File

@@ -3,7 +3,6 @@ import LogList from '@/pages/Experiment/components/LogList';
import { HyperParameterInstanceData } from '@/pages/HyperParameter/types';
import { NodeStatus } from '@/types';
import { Tabs } from 'antd';
import { useEffect } from 'react';
import styles from './index.less';

type ExperimentLogProps = {
@@ -97,8 +96,6 @@ function ExperimentLog({ instanceInfo, nodes }: ExperimentLogProps) {
},
];

useEffect(() => {}, []);

return (
<div className={styles['experiment-log']}>
<Tabs className={styles['experiment-log__tabs']} items={tabItems} />


+ 1
- 1
react-ui/src/pages/HyperParameter/types.ts View File

@@ -41,7 +41,7 @@ export type HyperParameterData = {
status_list: string; // 最近五次运行状态
} & FormData;

// 自动机器学习实验实例
// 实验实例
export type HyperParameterInstanceData = {
id: number;
ray_id: number;


+ 12
- 0
react-ui/src/pages/Knowledge/index.tsx View File

@@ -0,0 +1,12 @@
/*
* @Author: 赵伟
* @Date: 2025-04-21 16:38:59
* @Description: 知识图谱
*/

import IframePage, { IframePageType } from '@/components/IFramePage';

function KnowledgePage() {
return <IframePage type={IframePageType.Knowledge}></IframePage>;
}
export default KnowledgePage;

+ 1
- 3
react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx View File

@@ -117,7 +117,6 @@ function CreateServiceVersion() {
// 创建版本
const createServiceVersion = async (formData: FormData) => {
const envList = formData['env_variables'];
const image = formData['image'];
const model = formData['model'];
const codeConfig = formData['code_config'];
const envVariables = envList?.reduce((acc, cur) => {
@@ -127,10 +126,9 @@ function CreateServiceVersion() {

// 根据后台要求,修改表单数据
const object = {
...omit(formData, ['replicas', 'env_variables', 'image', 'model', 'code_config']),
...omit(formData, ['replicas', 'env_variables', 'model', 'code_config']),
replicas: Number(formData.replicas),
env_variables: envVariables,
image: image.value,
model: changePropertyName(
pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']),
{ showValue: 'show_value' },


+ 1
- 1
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -292,7 +292,7 @@ function ServiceInfo() {
},
{
title: '版本镜像',
dataIndex: 'image',
dataIndex: ['image', 'showValue'],
key: 'image',
width: '20%',
render: tableCellRender(true),


+ 1
- 1
react-ui/src/pages/Pipeline/Info/index.less View File

@@ -11,7 +11,7 @@
&__top {
display: flex;
align-items: center;
justify-content: end;
justify-content: flex-end;
width: 100%;
height: 52px;
padding: 0 20px;


+ 93
- 0
react-ui/src/services/activeLearn/index.js View File

@@ -0,0 +1,93 @@
/*
* @Author: 赵伟
* @Date: 2024-11-18 10:18:27
* @Description: 主动学习
*/

import { request } from '@umijs/max';


// 分页查询超参数自动寻优
export function getActiveLearnListReq(params) {
return request(`/api/mmp/activeLearn`, {
method: 'GET',
params,
});
}

// 查询超参数自动寻优详情
export function getActiveLearnInfoReq(params) {
return request(`/api/mmp/activeLearn/getActiveLearnDetail`, {
method: 'GET',
params,
});
}

// 新增超参数自动寻优
export function addActiveLearnReq(data) {
return request(`/api/mmp/activeLearn`, {
method: 'POST',
data,
});
}

// 编辑超参数自动寻优
export function updateActiveLearnReq(data) {
return request(`/api/mmp/activeLearn`, {
method: 'PUT',
data,
});
}

// 删除超参数自动寻优
export function deleteActiveLearnReq(id) {
return request(`/api/mmp/activeLearn/${id}`, {
method: 'DELETE',
});
}

// 运行超参数自动寻优
export function runActiveLearnReq(id) {
return request(`/api/mmp/activeLearn/run/${id}`, {
method: 'POST',
});
}

// ----------------------- 实验实例 -----------------------
// 获取实验实例列表
export function getActiveLearnInsListReq(params) {
return request(`/api/mmp/activeLearnIns`, {
method: 'GET',
params,
});
}

// 查询实验实例详情
export function getActiveLearnInsReq(id) {
return request(`/api/mmp/activeLearnIns/${id}`, {
method: 'GET',
});
}

// 停止实验实例
export function stopActiveLearnInsReq(id) {
return request(`/api/mmp/activeLearnIns/${id}`, {
method: 'PUT',
});
}

// 删除实验实例
export function deleteActiveLearnInsReq(id) {
return request(`/api/mmp/activeLearnIns/${id}`, {
method: 'DELETE',
});
}

// 批量删除实验实例
export function batchDeleteActiveLearnInsReq(data) {
return request(`/api/mmp/activeLearnIns/batchDelete`, {
method: 'DELETE',
data
});
}


+ 23
- 0
react-ui/src/stories/PageTitle.stories.tsx View File

@@ -1,5 +1,6 @@
import PageTitle from '@/components/PageTitle';
import type { Meta, StoryObj } from '@storybook/react';
import { Tabs } from 'antd';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
@@ -28,3 +29,25 @@ export const Primary: Story = {
title: '数据集列表',
},
};

/* 带有提示信息 */
export const WithTooltip: Story = {
args: {
title: '数据集列表',
tooltip: '其它提示',
},
};

/* Title 可以是 ReactNode */
export const Custom: Story = {
args: {
title: (
<Tabs
items={[
{ label: 'Tab 1', key: '1' },
{ label: 'Tab 2', key: '2' },
]}
></Tabs>
),
},
};

+ 1
- 1
react-ui/src/utils/date.ts View File

@@ -20,7 +20,7 @@ export const elapsedTime = (begin?: string | null, end?: string | null): string

const timestamp = endDate.valueOf() - beginDate.valueOf();
if (timestamp < 0) {
return '时间有误';
return '0秒';
}
const duration = dayjs.duration(timestamp);
const years = duration.years();


+ 13
- 0
react-ui/src/utils/promise.ts View File

@@ -13,3 +13,16 @@ export async function to<T, U = any>(promise: Promise<T>): Promise<[T, null] | [
}

export default to;

/**
* 休眠
* @param ms - The number of milliseconds to sleep.
* @return A promise that resolves after the specified amount of time.
*/
export function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, ms);
});
}

+ 1
- 1
react-ui/src/utils/table.tsx View File

@@ -87,7 +87,7 @@ function formatArray(property?: string): TableCellFormatter {
* @param ellipsis - 是否省略
* @param type - 类型
* @param options - 选项
* @returns React 节点
* @returns Ant Design Table 的 render
*/
function tableCellRender<T>(
ellipsis: boolean | TooltipProps | 'auto' = false,


+ 4
- 0
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/constant/Constant.java View File

@@ -39,6 +39,10 @@ public class Constant {
public final static String Init = "Init";
public final static String Stopped = "Stopped";
public final static String Succeeded = "Succeeded";
public final static String Error = "Error";

public final static String Unknown = "Unknown";
public final static String Available = "available";

public final static String Type_Train = "train";
public final static String Type_Evaluate = "evaluate";


+ 62
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/activeLearn/ActiveLearnController.java View File

@@ -0,0 +1,62 @@
package com.ruoyi.platform.controller.activeLearn;

import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.platform.domain.ActiveLearn;
import com.ruoyi.platform.service.ActiveLearnService;
import com.ruoyi.platform.vo.ActiveLearnVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

import javax.annotation.Resource;
import java.io.IOException;

@RestController
@RequestMapping("activeLearn")
@Api("主动学习")
public class ActiveLearnController extends BaseController {
@Resource
private ActiveLearnService activeLearnService;

@GetMapping
@ApiOperation("分页查询")
public GenericsAjaxResult<Page<ActiveLearn>> queryByPage(@RequestParam("page") int page,
@RequestParam("size") int size,
@RequestParam(value = "name", required = false) String name) {
PageRequest pageRequest = PageRequest.of(page, size);
return genericsSuccess(this.activeLearnService.queryByPage(name, pageRequest));
}

@PostMapping
@ApiOperation("新增主动学习")
public GenericsAjaxResult<ActiveLearn> addActiveLearn(@RequestBody ActiveLearnVo activeLearnVo) throws Exception {
return genericsSuccess(this.activeLearnService.save(activeLearnVo));
}

@PutMapping
@ApiOperation("编辑主动学习")
public GenericsAjaxResult<String> editActiveLearn(@RequestBody ActiveLearnVo activeLearnVo) throws Exception {
return genericsSuccess(this.activeLearnService.edit(activeLearnVo));
}

@GetMapping("/getActiveLearnDetail")
@ApiOperation("获取主动学习详细信息")
public GenericsAjaxResult<ActiveLearnVo> getActiveLearnDetail(@RequestParam("id") Long id) throws IOException {
return genericsSuccess(this.activeLearnService.getActiveLearnDetail(id));
}

@DeleteMapping("{id}")
@ApiOperation("删除主动学习")
public GenericsAjaxResult<String> deleteActiveLearn(@PathVariable("id") Long id) {
return genericsSuccess(this.activeLearnService.delete(id));
}

@PostMapping("/run/{id}")
@ApiOperation("运行主动学习实验")
public GenericsAjaxResult<String> runActiveLearn(@PathVariable("id") Long id) throws Exception {
return genericsSuccess(this.activeLearnService.runActiveLearnIns(id));
}
}

+ 60
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/activeLearn/ActiveLearnInsController.java View File

@@ -0,0 +1,60 @@
package com.ruoyi.platform.controller.activeLearn;

import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.platform.domain.ActiveLearnIns;
import com.ruoyi.platform.service.ActiveLearnInsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Page;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping("activeLearnIns")
@Api("主动学习实验实例")
public class ActiveLearnInsController extends BaseController {
@Resource
private ActiveLearnInsService activeLearnInsService;

@GetMapping
@ApiOperation("分页查询")
public GenericsAjaxResult<Page<ActiveLearnIns>> queryByPage(Long activeLearnId, int page, int size) throws IOException {
PageRequest pageRequest = PageRequest.of(page, size);
return genericsSuccess(this.activeLearnInsService.queryByPage(activeLearnId, pageRequest));
}

@PostMapping
@ApiOperation("新增实验实例")
public GenericsAjaxResult<ActiveLearnIns> add(@RequestBody ActiveLearnIns activeLearnIns) {
return genericsSuccess(this.activeLearnInsService.insert(activeLearnIns));
}

@DeleteMapping("{id}")
@ApiOperation("删除实验实例")
public GenericsAjaxResult<String> deleteById(@PathVariable("id") Long id) {
return genericsSuccess(this.activeLearnInsService.deleteById(id));
}

@DeleteMapping("batchDelete")
@ApiOperation("批量删除实验实例")
public GenericsAjaxResult<String> batchDelete(@RequestBody List<Long> ids) {
return genericsSuccess(this.activeLearnInsService.batchDelete(ids));
}

@PutMapping("{id}")
@ApiOperation("终止实验实例")
public GenericsAjaxResult<Boolean> terminateActiveLearnIns(@PathVariable("id") Long id) throws Exception {
return genericsSuccess(this.activeLearnInsService.terminateActiveLearnIns(id));
}

@GetMapping("{id}")
@ApiOperation("查看实验实例详情")
public GenericsAjaxResult<ActiveLearnIns> getDetailById(@PathVariable("id") Long id) throws Exception {
return genericsSuccess(this.activeLearnInsService.getDetailById(id));
}
}

+ 1
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java View File

@@ -115,6 +115,7 @@ public class ImageController extends BaseController {
* @return 删除是否成功
*/
@DeleteMapping("{id}")
@ApiOperation("删除镜像")
public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception {
return genericsSuccess(this.imageService.removeById(id));
}


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayController.java View File

@@ -50,7 +50,7 @@ public class RayController extends BaseController {

@DeleteMapping("{id}")
@ApiOperation("删除自动超参数寻优")
public GenericsAjaxResult<String> deleteRay(@PathVariable("id") Long id) {
public GenericsAjaxResult<String> deleteRay(@PathVariable("id") Long id) {
return genericsSuccess(this.rayService.delete(id));
}



+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/service/ServiceController.java View File

@@ -85,13 +85,13 @@ public class ServiceController extends BaseController {

@GetMapping("/serviceVersionDetail/{id}")
@ApiOperation("查询服务版本详细信息")
public GenericsAjaxResult<ServiceVersionVo> getServiceVersion(@PathVariable("id") Long id) {
public GenericsAjaxResult<ServiceVersionVo> getServiceVersion(@PathVariable("id") Long id) throws IOException {
return genericsSuccess(serviceService.getServiceVersion(id));
}

@GetMapping("serviceVersionCompare")
@ApiOperation("服务版本版本对比")
public GenericsAjaxResult<Map<String, Object>> serviceVersionCompare(@RequestParam("id1") Long id1, @RequestParam("id2") Long id2) throws IllegalAccessException {
public GenericsAjaxResult<Map<String, Object>> serviceVersionCompare(@RequestParam("id1") Long id1, @RequestParam("id2") Long id2) throws IllegalAccessException, IOException {
return genericsSuccess(serviceService.serviceVersionCompare(id1, id2));
}



+ 113
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ActiveLearn.java View File

@@ -0,0 +1,113 @@
package com.ruoyi.platform.domain;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.Date;

@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@ApiModel(description = "主动学习")
public class ActiveLearn {
private Long id;

@ApiModelProperty(value = "实验名称")
private String name;

@ApiModelProperty(value = "实验描述")
private String description;

@ApiModelProperty(value = "任务类型:classification或regression")
private String taskType;

@ApiModelProperty(value = "框架类型: sklearn, keras, pytorch")
private String frameworkType;

@ApiModelProperty(value = "代码配置")
private String codeConfig;

@ApiModelProperty(value = "预训练的模型")
private String model;

@ApiModelProperty(value = "模型文件路径")
private String modelPy;

@ApiModelProperty(value = "模型类名称")
private String modelClassName;

@ApiModelProperty(value = "分类算法")
private String classifierAlg;

@ApiModelProperty(value = "回归算法")
private String regressorAlg;

@ApiModelProperty(value = "dataset处理文件路径")
private String datasetPy;

@ApiModelProperty(value = "dataset类名")
private String datasetClassName;

@ApiModelProperty(value = "数据集")
private String dataset;

@ApiModelProperty(value = "数据量")
private Integer dataSize;

@ApiModelProperty(value = "镜像")
private String image;

@ApiModelProperty(value = "计算资源id")
private Integer computingResourceId;

@ApiModelProperty(value = "是否随机打乱")
private Boolean shuffle;

@ApiModelProperty(value = "训练集数据量")
private Integer trainSize;

@ApiModelProperty(value = "初始训练数据量")
private Integer initialNum;

@ApiModelProperty(value = "查询次数")
private Integer queriesNum;

@ApiModelProperty(value = "每次查询数据量")
private Integer instancesNum;

@ApiModelProperty(value = "查询策略:uncertainty_sampling, uncertainty_batch_sampling, max_std_sampling, expected_improvement, upper_confidence_bound")
private String queryStrategy;

@ApiModelProperty(value = "loss文件路径")
private String lossPy;

@ApiModelProperty(value = "loss类名")
private String lossClassName;

@ApiModelProperty(value = "多少轮查询保存一次模型参数")
private Integer checkpointNum;

@ApiModelProperty(value = "batch_size")
private Integer batchSize;

@ApiModelProperty(value = "epochs")
private Integer epochs;

@ApiModelProperty(value = "学习率")
private Float lr;

private String createBy;

private String updateBy;

private Date createTime;

private Date updateTime;

private Integer state;

@ApiModelProperty(value = "状态列表")
private String statusList;
}

+ 39
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ActiveLearnIns.java View File

@@ -0,0 +1,39 @@
package com.ruoyi.platform.domain;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.annotations.ApiModel;
import lombok.Data;

import java.util.Date;

@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@ApiModel(description = "主动学习实验实例")
public class ActiveLearnIns {
private Long id;

private Long activeLearnId;

private Integer state;

private String status;

private String param;

private Date createTime;

private Date updateTime;

private Date finishTime;

private String argoInsName;

private String argoInsNs;

private String resultPath;

private String nodeStatus;

private String nodeResult;
}

+ 5
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ResourceOccupy.java View File

@@ -49,9 +49,12 @@ public class ResourceOccupy {
@ApiModelProperty("流水线id")
private Long workflowId;

@ApiModelProperty("流水线节点id")
private String nodeId;

@ApiModelProperty("任务名称")
private String taskName;

@ApiModelProperty("流水线节点id")
private String nodeId;
@ApiModelProperty("任务状态")
private Integer taskState;
}

+ 29
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ActiveLearnDao.java View File

@@ -0,0 +1,29 @@
package com.ruoyi.platform.mapper;

import com.ruoyi.platform.domain.ActiveLearn;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.domain.PageRequest;

import java.util.List;

public interface ActiveLearnDao {
long count(@Param("name") String name);

List<ActiveLearn> queryByPage(@Param("name") String name, @Param("pageable") PageRequest pageRequest);

ActiveLearn getActiveLearnByName(@Param("name") String name);

int save(@Param("activeLearn") ActiveLearn activeLearn);

int edit(@Param("activeLearn") ActiveLearn activeLearn);

ActiveLearn getActiveLearnById(@Param("id") Long id);

List<ActiveLearn> queryByDatasetId(@Param("datasetId") String datasetId);

List<ActiveLearn> queryByModelId(@Param("modelId") String modelId);

List<ActiveLearn> queryByImageId(@Param("imageId") String imageId);

List<ActiveLearn> queryByCodeConfig(@Param("codeConfig") String codeConfig);
}

+ 23
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ActiveLearnInsDao.java View File

@@ -0,0 +1,23 @@
package com.ruoyi.platform.mapper;

import com.ruoyi.platform.domain.ActiveLearnIns;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface ActiveLearnInsDao {
long count(@Param("activeLearnId") Long activeLearnId);

List<ActiveLearnIns> queryAllByLimit(@Param("activeLearnId") Long activeLearnId, @Param("pageable") Pageable pageable);

int insert(@Param("activeLearnIns") ActiveLearnIns activeLearnIns);

int update(@Param("activeLearnIns") ActiveLearnIns activeLearnIns);

ActiveLearnIns queryById(@Param("id") Long id);

List<ActiveLearnIns> getByActiveLearnId(@Param("activeLearnId") Long activeLearnId);

List<ActiveLearnIns> queryActiveLearnInsIsNotTerminated();
}

+ 8
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/DevEnvironmentDao.java View File

@@ -62,6 +62,14 @@ public interface DevEnvironmentDao {
*/
int deleteById(Integer id);

DevEnvironment getByName(@Param("name") String name);

List<DevEnvironment> getRunning();

List<DevEnvironment> queryByDatasetId(@Param("datasetId") String datasetId);

List<DevEnvironment> queryByModelId(@Param("modelId") String modelId);

List<DevEnvironment> queryByImageId(@Param("imageId") String imageId);
}


+ 3
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ResourceOccupyDao.java View File

@@ -14,7 +14,7 @@ public interface ResourceOccupyDao {

int edit(@Param("resourceOccupy") ResourceOccupy resourceOccupy);

List<ResourceOccupy> getResourceOccupyByTask(@Param("taskType") String taskType, @Param("taskId") Long taskId, @Param("taskInsId") Long taskInsId, @Param("nodeId") String nodeId);
List<ResourceOccupy> getResourceOccupyByTask(@Param("taskType") String taskType, @Param("taskId") Long taskId, @Param("taskInsId") Long taskInsId, @Param("nodeId") String nodeId);

int deduceCredit(@Param("credit") Double credit, @Param("userId") Long userId);

@@ -29,4 +29,6 @@ public interface ResourceOccupyDao {
Double getUserCredit(@Param("userId") Long userId);

Double getDeduceCredit(@Param("userId") Long userId);

int deleteTaskState(@Param("taskType") String taskType, @Param("taskId") Long taskId, @Param("taskInsId") Long taskInsId);
}

+ 6
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ServiceDao.java View File

@@ -38,4 +38,10 @@ public interface ServiceDao {
ServiceVersion getSvByVersion(@Param("version") String version, @Param("serviceId") Long serviceId);

List<ServiceVersion> getRunning();

List<String> queryByModelId(@Param("modelId") String modelId);

List<String> queryByImageId(@Param("imageId") String imageId);

List<String> queryByCodeConfig(@Param("codeConfig") String codeConfig);
}

+ 116
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ActiveLearnInsStatusTask.java View File

@@ -0,0 +1,116 @@
package com.ruoyi.platform.scheduling;

import com.ruoyi.platform.domain.ActiveLearn;
import com.ruoyi.platform.domain.ActiveLearnIns;
import com.ruoyi.platform.mapper.ActiveLearnDao;
import com.ruoyi.platform.mapper.ActiveLearnInsDao;
import com.ruoyi.platform.mapper.ResourceOccupyDao;
import com.ruoyi.platform.service.ActiveLearnInsService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@Component()
public class ActiveLearnInsStatusTask {

@Resource
private ActiveLearnInsService activeLearnInsService;
@Resource
private ActiveLearnInsDao activeLearnInsDao;
@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private ResourceOccupyDao resourceOccupyDao;
@Resource
private ResourceOccupyService resourceOccupyService;

private List<Long> activeLearnIds = new ArrayList<>();

@Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次
public void executeActiveLearnInsStatus() {
List<ActiveLearnIns> activeLearnInsList = activeLearnInsService.queryActiveLearnInsIsNotTerminated();

// 去argo查询状态
List<ActiveLearnIns> updateList = new ArrayList<>();
if (activeLearnInsList != null && activeLearnInsList.size() > 0) {
for (ActiveLearnIns activeLearnIns : activeLearnInsList) {
//当原本状态为null或非终止态时才调用argo接口
try {
Long userId = resourceOccupyDao.getResourceOccupyByTask(Constant.TaskType_ActiveLearn, activeLearnIns.getActiveLearnId(), activeLearnIns.getId(), null).get(0).getUserId();
if (resourceOccupyDao.getUserCredit(userId) <= 0) {
activeLearnIns.setStatus(Constant.Failed);
activeLearnInsService.terminateActiveLearnIns(activeLearnIns.getId());
} else {
activeLearnIns = activeLearnInsService.queryStatusFromArgo(activeLearnIns);
// 扣除积分
if (Constant.Running.equals(activeLearnIns.getStatus())) {
resourceOccupyService.deducing(Constant.TaskType_ActiveLearn, null, activeLearnIns.getId(), null, null);
} else if (Constant.Failed.equals(activeLearnIns.getStatus()) || Constant.Terminated.equals(activeLearnIns.getStatus())
|| Constant.Succeeded.equals(activeLearnIns.getStatus())) {
resourceOccupyService.endDeduce(Constant.TaskType_ActiveLearn, null, activeLearnIns.getId(), null, null);
}
}
} catch (Exception e) {
activeLearnIns.setStatus(Constant.Failed);
// 结束扣除积分
resourceOccupyService.endDeduce(Constant.TaskType_ActiveLearn, null, activeLearnIns.getId(), null, null);
}
// 线程安全的添加操作
synchronized (activeLearnIds) {
activeLearnIds.add(activeLearnIns.getActiveLearnId());
}
updateList.add(activeLearnIns);
}
if (updateList.size() > 0) {
for (ActiveLearnIns activeLearnIns : updateList) {
activeLearnInsDao.update(activeLearnIns);
}
}
}
}

@Scheduled(cron = "0/30 * * * * ?") // / 每30S执行一次
public void executeActiveLearnStatus() {
if (activeLearnIds.size() == 0) {
return;
}
// 存储需要更新的实验对象列表
List<ActiveLearn> updateActiveLearns = new ArrayList<>();
for (Long activeLearnId : activeLearnIds) {
// 获取当前实验的所有实例列表
List<ActiveLearnIns> insList = activeLearnInsDao.getByActiveLearnId(activeLearnId);
List<String> statusList = new ArrayList<>();
// 更新实验状态列表
for (int i = 0; i < insList.size(); i++) {
statusList.add(insList.get(i).getStatus());
}
String subStatus = statusList.toString().substring(1, statusList.toString().length() - 1);
ActiveLearn activeLearn = activeLearnDao.getActiveLearnById(activeLearnId);
if (!StringUtils.equals(activeLearn.getStatusList(), subStatus)) {
activeLearn.setStatusList(subStatus);
updateActiveLearns.add(activeLearn);
activeLearnDao.edit(activeLearn);
}
}

if (!updateActiveLearns.isEmpty()) {
// 使用Iterator进行安全的删除操作
Iterator<Long> iterator = activeLearnIds.iterator();
while (iterator.hasNext()) {
Long activeLearnId = iterator.next();
for (ActiveLearn activeLearn : updateActiveLearns) {
if (activeLearn.getId().equals(activeLearnId)) {
iterator.remove();
}
}
}
}
}
}

+ 28
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ActiveLearnInsService.java View File

@@ -0,0 +1,28 @@
package com.ruoyi.platform.service;

import com.ruoyi.platform.domain.ActiveLearnIns;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

import java.io.IOException;
import java.util.List;

public interface ActiveLearnInsService {
Page<ActiveLearnIns> queryByPage(Long activeLearnId, PageRequest pageRequest) throws IOException;

ActiveLearnIns insert(ActiveLearnIns activeLearnIns);

String deleteById(Long id);

String batchDelete(List<Long> ids);

boolean terminateActiveLearnIns(Long id) throws Exception;

ActiveLearnIns getDetailById(Long id) throws Exception;

void updateActiveLearnStatus(Long activeLearnId);

ActiveLearnIns queryStatusFromArgo(ActiveLearnIns ins);

List<ActiveLearnIns> queryActiveLearnInsIsNotTerminated();
}

+ 22
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ActiveLearnService.java View File

@@ -0,0 +1,22 @@
package com.ruoyi.platform.service;

import com.ruoyi.platform.domain.ActiveLearn;
import com.ruoyi.platform.vo.ActiveLearnVo;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Page;

import java.io.IOException;

public interface ActiveLearnService {
Page<ActiveLearn> queryByPage(String name, PageRequest pageRequest);

ActiveLearn save(ActiveLearnVo activeLearnVo) throws Exception;

String edit(ActiveLearnVo activeLearnVo) throws Exception;

ActiveLearnVo getActiveLearnDetail(Long id) throws IOException;

String delete(Long id);

String runActiveLearnIns (Long id) throws Exception;
}

+ 1
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/RayInsService.java View File

@@ -4,6 +4,7 @@ import org.springframework.data.domain.PageRequest;
import com.ruoyi.platform.domain.RayIns;
import java.io.IOException;
import java.util.List;

public interface RayInsService {
Page<RayIns> queryByPage(Long rayId, PageRequest pageRequest) throws IOException;



+ 2
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ResourceOccupyService.java View File

@@ -22,4 +22,6 @@ public interface ResourceOccupyService {
Map<String, Double> queryCredit();

void update(String taskType, Long taskId, Long taskInsId, Integer computingResourceId, Integer replicas);

void deleteTaskState(String taskType, Long taskId, Long taskInsId);
}

+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ServiceService.java View File

@@ -26,9 +26,9 @@ public interface ServiceService {

Service getService(Long id);

ServiceVersionVo getServiceVersion(Long id);
ServiceVersionVo getServiceVersion(Long id) throws IOException;

Map<String, Object> serviceVersionCompare(Long id1, Long id2) throws IllegalAccessException;
Map<String, Object> serviceVersionCompare(Long id1, Long id2) throws IllegalAccessException, IOException;

String deleteService(Long id) throws Exception;



+ 263
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ActiveLearnInsServiceImpl.java View File

@@ -0,0 +1,263 @@
package com.ruoyi.platform.service.impl;

import com.ruoyi.platform.domain.ActiveLearn;
import com.ruoyi.platform.domain.ActiveLearnIns;
import com.ruoyi.platform.mapper.ActiveLearnDao;
import com.ruoyi.platform.mapper.ActiveLearnInsDao;
import com.ruoyi.platform.service.ActiveLearnInsService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.DateUtils;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;

@Service
public class ActiveLearnInsServiceImpl implements ActiveLearnInsService {
@Value("${argo.url}")
private String argoUrl;
@Value("${argo.workflowStatus}")
private String argoWorkflowStatus;
@Value("${argo.workflowTermination}")
private String argoWorkflowTermination;

@Resource
private ActiveLearnInsDao activeLearnInsDao;
@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private ResourceOccupyService resourceOccupyService;

@Override
public Page<ActiveLearnIns> queryByPage(Long activeLearnId, PageRequest pageRequest) throws IOException {
long total = this.activeLearnInsDao.count(activeLearnId);
List<ActiveLearnIns> activeLearnInsList = activeLearnInsDao.queryAllByLimit(activeLearnId, pageRequest);
return new PageImpl<>(activeLearnInsList, pageRequest, total);
}

@Override
public ActiveLearnIns insert(ActiveLearnIns activeLearnIns) {
activeLearnInsDao.insert(activeLearnIns);
return activeLearnIns;
}

@Override
@Transactional
public String deleteById(Long id) {
ActiveLearnIns activeLearnIns = activeLearnInsDao.queryById(id);
if (activeLearnIns == null) {
return "实验实例不存在";
}
if (StringUtils.isEmpty(activeLearnIns.getStatus())) {
activeLearnIns = queryStatusFromArgo(activeLearnIns);
}
if (StringUtils.equals(activeLearnIns.getStatus(), Constant.Running)) {
return "实验实例正在运行,不可删除";
}

activeLearnIns.setState(Constant.State_invalid);
int update = activeLearnInsDao.update(activeLearnIns);
if (update > 0) {
resourceOccupyService.deleteTaskState(Constant.TaskType_ActiveLearn, activeLearnIns.getActiveLearnId(), id);
updateActiveLearnStatus(activeLearnIns.getActiveLearnId());
return "删除成功";
} else {
return "删除失败";
}
}

@Override
public String batchDelete(List<Long> ids) {
for (Long id : ids) {
String result = deleteById(id);
if (!"删除成功".equals(result)) {
return result;
}
}
return "删除成功";
}

@Override
public boolean terminateActiveLearnIns(Long id) throws Exception {
ActiveLearnIns activeLearnIns = activeLearnInsDao.queryById(id);
if (activeLearnIns == null) {
throw new IllegalStateException("实验实例未查询到,id: " + id);
}
String currentStatus = activeLearnIns.getStatus();
String name = activeLearnIns.getArgoInsName();
String namespace = activeLearnIns.getArgoInsNs();

// 获取当前状态,如果为空,则从Argo查询
if (StringUtils.isEmpty(currentStatus)) {
currentStatus = queryStatusFromArgo(activeLearnIns).getStatus();
}

// 只有状态是"Running"时才能终止实例
if (!currentStatus.equalsIgnoreCase(Constant.Running)) {
throw new Exception("终止错误,只有运行状态的实例才能终止"); // 如果不是"Running"状态,则不执行终止操作
}

// 创建请求数据map
Map<String, Object> requestData = new HashMap<>();
requestData.put("namespace", namespace);
requestData.put("name", name);
// 创建发送数据map,将请求数据作为"data"键的值
Map<String, Object> res = new HashMap<>();
res.put("data", requestData);

try {
// 发送POST请求到Argo工作流状态查询接口,并将请求数据转换为JSON
String req = HttpUtils.sendPost(argoUrl + argoWorkflowTermination, null, JsonUtils.mapToJson(res));
// 检查响应是否为空或无内容
if (StringUtils.isEmpty(req)) {
throw new RuntimeException("终止响应内容为空。");

}
// 将响应的JSON字符串转换为Map对象
Map<String, Object> runResMap = JsonUtils.jsonToMap(req);
// 从响应Map中直接获取"errCode"的值
Integer errCode = (Integer) runResMap.get("errCode");
if (errCode != null && errCode == 0) {
//更新autoMlIns,确保状态更新被保存到数据库
ActiveLearnIns ins = queryStatusFromArgo(activeLearnIns);
String nodeStatus = ins.getNodeStatus();
Map<String, Object> nodeMap = JsonUtils.jsonToMap(nodeStatus);

// 遍历 map
for (Map.Entry<String, Object> entry : nodeMap.entrySet()) {
// 获取每个 Map 中的值并强制转换为 Map
Map<String, Object> innerMap = (Map<String, Object>) entry.getValue();
// 检查 phase 的值
if (innerMap.containsKey("phase")) {
String phaseValue = (String) innerMap.get("phase");
// 如果值不等于 Succeeded,则赋值为 Failed
if (!StringUtils.equals(Constant.Succeeded, phaseValue)) {
innerMap.put("phase", Constant.Failed);
}
}
}
ins.setNodeStatus(JsonUtils.mapToJson(nodeMap));
ins.setStatus(Constant.Terminated);
ins.setUpdateTime(new Date());
activeLearnInsDao.update(ins);
updateActiveLearnStatus(ins.getActiveLearnId());
// 结束扣积分
resourceOccupyService.endDeduce(Constant.TaskType_ActiveLearn, null, id, null, null);
return true;
} else {
return false;
}
} catch (Exception e) {
throw new RuntimeException("终止实例错误: " + e.getMessage(), e);
}
}

@Override
public ActiveLearnIns getDetailById(Long id) throws Exception {
ActiveLearnIns activeLearnIns = activeLearnInsDao.queryById(id);
if (Constant.Running.equals(activeLearnIns.getStatus()) || Constant.Pending.equals(activeLearnIns.getStatus())) {
activeLearnIns = queryStatusFromArgo(activeLearnIns);
}
return activeLearnIns;
}

@Override
public void updateActiveLearnStatus(Long activeLearnId) {
List<ActiveLearnIns> insList = activeLearnInsDao.getByActiveLearnId(activeLearnId);
List<String> statusList = new ArrayList<>();
// 更新实验状态列表
for (int i = 0; i < insList.size(); i++) {
statusList.add(insList.get(i).getStatus());
}
String subStatus = statusList.toString().substring(1, statusList.toString().length() - 1);
ActiveLearn activeLearn = activeLearnDao.getActiveLearnById(activeLearnId);
if (!StringUtils.equals(activeLearn.getStatusList(), subStatus)) {
activeLearn.setStatusList(subStatus);
activeLearnDao.edit(activeLearn);
}
}

@Override
public ActiveLearnIns queryStatusFromArgo(ActiveLearnIns ins) {
String namespace = ins.getArgoInsNs();
String name = ins.getArgoInsName();

// 创建请求数据map
Map<String, Object> requestData = new HashMap<>();
requestData.put("namespace", namespace);
requestData.put("name", name);

// 创建发送数据map,将请求数据作为"data"键的值
Map<String, Object> res = new HashMap<>();
res.put("data", requestData);

try {
// 发送POST请求到Argo工作流状态查询接口,并将请求数据转换为JSON
String req = HttpUtils.sendPost(argoUrl + argoWorkflowStatus, null, JsonUtils.mapToJson(res));
// 检查响应是否为空或无内容
if (req == null || StringUtils.isEmpty(req)) {
throw new RuntimeException("工作流状态响应为空。");
}
// 将响应的JSON字符串转换为Map对象
Map<String, Object> runResMap = JsonUtils.jsonToMap(req);
// 从响应Map中获取"data"部分
Map<String, Object> data = (Map<String, Object>) runResMap.get("data");
if (data == null || data.isEmpty()) {
throw new RuntimeException("工作流数据为空.");
}
// 从"data"中获取"status"部分,并返回"phase"的值
Map<String, Object> status = (Map<String, Object>) data.get("status");
if (status == null || status.isEmpty()) {
throw new RuntimeException("工作流状态为空。");
}

//解析流水线结束时间
String finishedAtString = (String) status.get("finishedAt");
if (finishedAtString != null && !finishedAtString.isEmpty()) {
Date finishTime = DateUtils.convertUTCtoShanghaiDate(finishedAtString);
ins.setFinishTime(finishTime);
}

// 解析nodes字段,提取节点状态并转换为JSON字符串
Map<String, Object> nodes = (Map<String, Object>) status.get("nodes");
Map<String, Object> modifiedNodes = new LinkedHashMap<>();
if (nodes != null) {
for (Map.Entry<String, Object> nodeEntry : nodes.entrySet()) {
Map<String, Object> nodeDetails = (Map<String, Object>) nodeEntry.getValue();
String templateName = (String) nodeDetails.get("displayName");
modifiedNodes.put(templateName, nodeDetails);
}
}


String nodeStatusJson = JsonUtils.mapToJson(modifiedNodes);
ins.setNodeStatus(nodeStatusJson);

//终止态为终止不改
if (!StringUtils.equals(ins.getStatus(), Constant.Terminated)) {
ins.setStatus(StringUtils.isNotEmpty((String) status.get("phase")) ? (String) status.get("phase") : Constant.Pending);
}
if (StringUtils.equals(ins.getStatus(), Constant.Error)) {
ins.setStatus(Constant.Failed);
}
return ins;
} catch (Exception e) {
throw new RuntimeException("查询状态失败: " + e.getMessage(), e);
}
}

@Override
public List<ActiveLearnIns> queryActiveLearnInsIsNotTerminated() {
return activeLearnInsDao.queryActiveLearnInsIsNotTerminated();
}
}

+ 201
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ActiveLearnServiceImpl.java View File

@@ -0,0 +1,201 @@
package com.ruoyi.platform.service.impl;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.ActiveLearn;
import com.ruoyi.platform.domain.ActiveLearnIns;
import com.ruoyi.platform.mapper.ActiveLearnDao;
import com.ruoyi.platform.mapper.ActiveLearnInsDao;
import com.ruoyi.platform.service.ActiveLearnInsService;
import com.ruoyi.platform.service.ActiveLearnService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.ActiveLearnParamVo;
import com.ruoyi.platform.vo.ActiveLearnVo;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ActiveLearnServiceImpl implements ActiveLearnService {
@Value("${argo.url}")
private String argoUrl;
@Value("${argo.workflowRun}")
private String argoWorkflowRun;
@Value("${argo.convertActiveLearn}")
String convertActiveLearn;

@Resource
private ResourceOccupyService resourceOccupyService;

@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private ActiveLearnInsDao activeLearnInsDao;
@Resource
private ActiveLearnInsService activeLearnInsService;


@Override
public Page<ActiveLearn> queryByPage(String name, PageRequest pageRequest) {
long total = activeLearnDao.count(name);
List<ActiveLearn> activeLearns = activeLearnDao.queryByPage(name, pageRequest);
return new PageImpl<>(activeLearns, pageRequest, total);
}

@Override
public ActiveLearn save(ActiveLearnVo activeLearnVo) throws Exception {
ActiveLearn activeLearnByName = activeLearnDao.getActiveLearnByName(activeLearnVo.getName());
if (activeLearnByName != null) {
throw new RuntimeException("实验名称已存在");
}
ActiveLearn activeLearn = new ActiveLearn();
BeanUtils.copyProperties(activeLearnVo, activeLearn);
String username = SecurityUtils.getLoginUser().getUsername();
activeLearn.setCreateBy(username);
activeLearn.setUpdateBy(username);
activeLearn.setDataset(JacksonUtil.toJSONString(activeLearnVo.getDataset()));
activeLearn.setCodeConfig(JacksonUtil.toJSONString(activeLearnVo.getCodeConfig()));
activeLearn.setModel(JacksonUtil.toJSONString(activeLearnVo.getModel()));
activeLearn.setImage(JacksonUtil.toJSONString(activeLearnVo.getImage()));
activeLearnDao.save(activeLearn);
return activeLearn;
}

@Override
public String edit(ActiveLearnVo activeLearnVo) throws Exception {
ActiveLearn oldActiveLearn = activeLearnDao.getActiveLearnByName(activeLearnVo.getName());
if (oldActiveLearn != null && !oldActiveLearn.getId().equals(activeLearnVo.getId())) {
throw new RuntimeException("实验名称已存在");
}
ActiveLearn activeLearn = new ActiveLearn();
BeanUtils.copyProperties(activeLearnVo, activeLearn);
activeLearn.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
activeLearn.setDataset(JacksonUtil.toJSONString(activeLearnVo.getDataset()));
activeLearn.setCodeConfig(JacksonUtil.toJSONString(activeLearnVo.getCodeConfig()));
activeLearn.setModel(JacksonUtil.toJSONString(activeLearnVo.getModel()));
activeLearn.setImage(JacksonUtil.toJSONString(activeLearnVo.getImage()));
activeLearnDao.edit(activeLearn);
return "修改成功";
}

@Override
public ActiveLearnVo getActiveLearnDetail(Long id) throws IOException {
ActiveLearn activeLearn = activeLearnDao.getActiveLearnById(id);
ActiveLearnVo activeLearnVo = new ActiveLearnVo();
BeanUtils.copyProperties(activeLearn, activeLearnVo);
if (StringUtils.isNotEmpty(activeLearn.getDataset())) {
activeLearnVo.setDataset(JsonUtils.jsonToMap(activeLearn.getDataset()));
}
if (StringUtils.isNotEmpty(activeLearn.getCodeConfig())) {
activeLearnVo.setCodeConfig(JsonUtils.jsonToMap(activeLearn.getCodeConfig()));
}
if (StringUtils.isNotEmpty(activeLearn.getModel())) {
activeLearnVo.setModel(JsonUtils.jsonToMap(activeLearn.getModel()));
}
if (StringUtils.isNotEmpty(activeLearn.getImage())) {
activeLearnVo.setImage(JsonUtils.jsonToMap(activeLearn.getImage()));
}
return activeLearnVo;
}

@Override
@Transactional
public String delete(Long id) {
ActiveLearn activeLearn = activeLearnDao.getActiveLearnById(id);
if (activeLearn == null) {
throw new RuntimeException("实验不存在");
}
String username = SecurityUtils.getLoginUser().getUsername();
String createBy = activeLearn.getCreateBy();
if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) {
throw new RuntimeException("无权限删除该实验");
}
activeLearn.setState(Constant.State_invalid);
resourceOccupyService.deleteTaskState(Constant.TaskType_ActiveLearn, id, null);
return activeLearnDao.edit(activeLearn) > 0 ? "删除成功" : "删除失败";
}

@Override
public String runActiveLearnIns(Long id) throws Exception {
ActiveLearn activeLearn = activeLearnDao.getActiveLearnById(id);
if (activeLearn == null) {
throw new Exception("主动学习配置不存在");
}

// 记录开始扣积分
if (resourceOccupyService.haveResource(activeLearn.getComputingResourceId(), 1)) {
ActiveLearnParamVo activeLearnParamVo = new ActiveLearnParamVo();
BeanUtils.copyProperties(activeLearn, activeLearnParamVo);
activeLearnParamVo.setCodeConfig(JsonUtils.jsonToMap(activeLearn.getCodeConfig()));
activeLearnParamVo.setDataset(JsonUtils.jsonToMap(activeLearn.getDataset()));
activeLearnParamVo.setModel(JsonUtils.jsonToMap(activeLearn.getModel()));
activeLearnParamVo.setImage(JsonUtils.jsonToMap(activeLearn.getImage()));

String param = JsonUtils.getConvertParam(activeLearnParamVo);
// 调argo转换接口
try {
String convertRes = HttpUtils.sendPost(argoUrl + convertActiveLearn, param);
if (convertRes == null || StringUtils.isEmpty(convertRes)) {
throw new RuntimeException("转换流水线失败");
}
Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes);
// 组装运行接口json
Map<String, Object> output = (Map<String, Object>) converMap.get("output");
Map<String, Object> runReqMap = new HashMap<>();
runReqMap.put("data", converMap.get("data"));
// 调argo运行接口
String runRes = HttpUtils.sendPost(argoUrl + argoWorkflowRun, JsonUtils.mapToJson(runReqMap));
if (runRes == null || StringUtils.isEmpty(runRes)) {
throw new RuntimeException("运行失败");
}
Map<String, Object> runResMap = JsonUtils.jsonToMap(runRes);
Map<String, Object> data = (Map<String, Object>) runResMap.get("data");
//判断data为空
if (data == null || MapUtils.isEmpty(data)) {
throw new RuntimeException("运行失败");
}
Map<String, Object> metadata = (Map<String, Object>) data.get("metadata");

// 插入记录到实验实例表
ActiveLearnIns activeLearnIns = new ActiveLearnIns();
activeLearnIns.setActiveLearnId(id);
activeLearnIns.setArgoInsNs((String) metadata.get("namespace"));
activeLearnIns.setArgoInsName((String) metadata.get("name"));
activeLearnIns.setParam(param);
activeLearnIns.setStatus(Constant.Pending);
//替换argoInsName
String outputString = JsonUtils.mapToJson(output);
activeLearnIns.setNodeResult(outputString.replace("{{workflow.name}}", (String) metadata.get("name")));

Map<String, Object> param_output = (Map<String, Object>) output.get("param_output");
List output1 = (ArrayList) param_output.values().toArray()[0];
Map<String, String> output2 = (Map<String, String>) output1.get(0);
String outputPath = output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")) + "/hpo";
activeLearnIns.setResultPath(outputPath);
activeLearnInsDao.insert(activeLearnIns);
activeLearnInsService.updateActiveLearnStatus(id);
// 记录开始扣除积分
resourceOccupyService.startDeduce(activeLearn.getComputingResourceId(), 1, Constant.TaskType_ActiveLearn, id, activeLearnIns.getId(), null, activeLearn.getName(), null, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return "执行成功";
}
}

+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/AutoMlInsServiceImpl.java View File

@@ -146,7 +146,7 @@ public class AutoMlInsServiceImpl implements AutoMlInsService {
if (!StringUtils.equals(ins.getStatus(), Constant.Terminated)) {
ins.setStatus(StringUtils.isNotEmpty((String) status.get("phase")) ? (String) status.get("phase") : Constant.Pending);
}
if (StringUtils.equals(ins.getStatus(), "Error")) {
if (StringUtils.equals(ins.getStatus(), Constant.Error)) {
ins.setStatus(Constant.Failed);
}
return ins;


+ 18
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CodeConfigServiceImpl.java View File

@@ -2,11 +2,11 @@ package com.ruoyi.platform.service.impl;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.ActiveLearn;
import com.ruoyi.platform.mapper.*;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.platform.domain.AssetWorkflow;
import com.ruoyi.platform.domain.CodeConfig;
import com.ruoyi.platform.mapper.AssetWorkflowDao;
import com.ruoyi.platform.mapper.CodeConfigDao;
import com.ruoyi.platform.service.CodeConfigService;
import com.ruoyi.system.api.model.LoginUser;
import org.apache.commons.lang3.StringUtils;
@@ -15,7 +15,6 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import com.ruoyi.platform.domain.Ray;
import com.ruoyi.platform.mapper.RayDao;

import javax.annotation.Resource;
import java.util.Date;
@@ -32,6 +31,10 @@ public class CodeConfigServiceImpl implements CodeConfigService {
private AssetWorkflowDao assetWorkflowDao;
@Resource
private RayDao rayDao;
@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private ServiceDao serviceDao;

@Override
public Page<CodeConfig> queryByPage(CodeConfig codeConfig, PageRequest pageRequest) {
@@ -102,6 +105,18 @@ public class CodeConfigServiceImpl implements CodeConfigService {
throw new Exception("该代码配置被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByCodeConfig(JSON.toJSONString(map));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
throw new Exception("该代码配置被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<String> serviceVersionList = serviceDao.queryByCodeConfig(JSON.toJSONString(map));
if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
throw new Exception("该代码配置被服务版本:" + serviceVersions + "使用,不能删除,请先删除服务版本。");
}

LoginUser loginUser = SecurityUtils.getLoginUser();
String username = loginUser.getUsername();
String createBy = codeConfig.getCreateBy();


+ 17
- 11
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DevEnvironmentServiceImpl.java View File

@@ -6,9 +6,11 @@ import com.ruoyi.platform.domain.PodStatus;
import com.ruoyi.platform.mapper.DevEnvironmentDao;
import com.ruoyi.platform.service.DevEnvironmentService;
import com.ruoyi.platform.service.JupyterService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.vo.DevEnvironmentVo;
import com.ruoyi.platform.vo.PodStatusVo;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.system.api.model.LoginUser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
@@ -16,6 +18,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
@@ -35,6 +38,8 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService {
@Resource
@Lazy
private JupyterService jupyterService;
@Resource
private ResourceOccupyService resourceOccupyService;

/**
* 通过ID查询单条数据
@@ -89,27 +94,28 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService {
*/
@Override
public DevEnvironment insert(DevEnvironmentVo devEnvironmentVo) throws Exception {
DevEnvironment devByName = devEnvironmentDao.getByName(devEnvironmentVo.getName());
if (devByName != null) {
throw new RuntimeException("开发环境名称已存在");
}
//插入预备,此时不需要判断版本重复
DevEnvironment devEnvironment = new DevEnvironment();
LoginUser loginUser = SecurityUtils.getLoginUser();
devEnvironment.setName(devEnvironmentVo.getName());
//状态先设为未知
devEnvironment.setStatus("Unknown");
devEnvironment.setStatus(Constant.Unknown);
devEnvironment.setComputingResource(devEnvironmentVo.getComputingResource());
devEnvironment.setComputingResourceId(devEnvironmentVo.getComputingResourceId());
devEnvironment.setStandard(devEnvironmentVo.getStandard());
devEnvironment.setEnvVariable(devEnvironmentVo.getEnvVariable());
devEnvironment.setImage(devEnvironmentVo.getImage());
// 将 dataset 和 model 转换成 JSON 字符串
String datasetJson = JacksonUtil.toJSONString(devEnvironmentVo.getDataset());
String modelJson = JacksonUtil.toJSONString(devEnvironmentVo.getModel());
devEnvironment.setDataset(datasetJson);
devEnvironment.setModel(modelJson);
devEnvironment.setImage(JacksonUtil.toJSONString(devEnvironmentVo.getImage()));
devEnvironment.setDataset(JacksonUtil.toJSONString(devEnvironmentVo.getDataset()));
devEnvironment.setModel(JacksonUtil.toJSONString(devEnvironmentVo.getModel()));
devEnvironment.setCreateBy(loginUser.getUsername());
devEnvironment.setUpdateBy(loginUser.getUsername());
devEnvironment.setUpdateTime(new Date());
devEnvironment.setCreateTime(new Date());
devEnvironment.setState(1);
devEnvironment.setState(Constant.State_valid);
this.devEnvironmentDao.insert(devEnvironment);
return devEnvironment;
}
@@ -140,6 +146,7 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService {
}

@Override
@Transactional
public String removeById(Integer id) throws Exception {
DevEnvironment devEnvironment = this.devEnvironmentDao.queryById(id);
if (devEnvironment == null) {
@@ -155,9 +162,8 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService {
}

jupyterService.stopJupyterService(id);
devEnvironment.setState(0);
devEnvironment.setState(Constant.State_invalid);
resourceOccupyService.deleteTaskState(Constant.TaskType_Dev, Long.valueOf(id), null);
return this.devEnvironmentDao.update(devEnvironment) > 0 ? "删除成功" : "删除失败";
}


}

+ 5
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.java View File

@@ -2,7 +2,6 @@ package com.ruoyi.platform.service.impl;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.platform.domain.DatasetTempStorage;
import com.ruoyi.platform.domain.Experiment;
import com.ruoyi.platform.domain.ExperimentIns;
@@ -16,6 +15,7 @@ import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.*;
import com.ruoyi.platform.vo.LogRequestVo;
import com.ruoyi.platform.vo.PodLogVo;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.system.api.model.LoginUser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
@@ -23,6 +23,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
@@ -204,6 +205,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService {
}

@Override
@Transactional
public String removeById(Integer id) {
ExperimentIns experimentIns = experimentInsDao.queryById(id);
if (experimentIns == null) {
@@ -227,6 +229,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService {
experimentIns.setState(0);
int update = this.experimentInsDao.update(experimentIns);
if (update > 0) {
resourceOccupyService.deleteTaskState(Constant.TaskType_Workflow, Long.valueOf(experimentIns.getExperimentId()), Long.valueOf(id));
updateExperimentStatus(experimentIns.getExperimentId());
return "删除成功";
} else {
@@ -323,7 +326,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService {
if (!StringUtils.equals(experimentIns.getStatus(), "Terminated")) {
experimentIns.setStatus(StringUtils.isNotEmpty((String) status.get("phase")) ? (String) status.get("phase") : "Pending");
}
if (StringUtils.equals(experimentIns.getStatus(), "Error")) {
if (StringUtils.equals(experimentIns.getStatus(), Constant.Error)) {
experimentIns.setStatus("Failed");
}



+ 4
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentServiceImpl.java View File

@@ -28,6 +28,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
@@ -184,6 +185,7 @@ public class ExperimentServiceImpl implements ExperimentService {
}

@Override
@Transactional
public String removeById(Integer id) throws Exception {
Experiment experiment = experimentDao.queryById(id);
if (experiment == null) {
@@ -202,7 +204,8 @@ public class ExperimentServiceImpl implements ExperimentService {
if (experimentInsList != null && experimentInsList.size() > 0) {
throw new Exception("该实验存在实例,无法删除");
}
experiment.setState(0);
resourceOccupyService.deleteTaskState(Constant.TaskType_Workflow, Long.valueOf(id), null);
experiment.setState(Constant.State_invalid);
return this.experimentDao.update(experiment) > 0 ? "删除成功" : "删除失败";




+ 36
- 7
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java View File

@@ -11,6 +11,7 @@ import com.ruoyi.platform.service.ImageVersionService;
import com.ruoyi.platform.service.MinioService;
import com.ruoyi.platform.utils.DockerClientUtil;
import com.ruoyi.platform.utils.FileUtil;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.K8sClientUtil;
import com.ruoyi.platform.vo.ImageVo;
import com.ruoyi.system.api.constant.Constant;
@@ -54,8 +55,11 @@ public class ImageServiceImpl implements ImageService {
@Resource
private RayDao rayDao;
@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private ImageVersionService imageVersionService;

@Resource
private ServiceDao serviceDao;
@Resource
private K8sClientUtil k8sClientUtil;
@Resource
@@ -84,8 +88,6 @@ public class ImageServiceImpl implements ImageService {
private String image;
@Value("${dockerpush.mountPath}")
private String mountPath;
@Value("${dockerpush.proxyUrl}")
private String proxyUrl;
@Value("${jupyter.namespace}")
private String namespace;

@@ -184,6 +186,24 @@ public class ImageServiceImpl implements ImageService {
throw new Exception("该镜像被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByImageId(JSON.toJSONString(map));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
throw new Exception("该镜像被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByImageId(JSON.toJSONString(map));
if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
throw new Exception("该镜像被开发环境:" + devEnvironments + "使用,不能删除,请先删除开发环境。");
}

List<String> serviceVersionList = serviceDao.queryByImageId(JSON.toJSONString(map));
if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
throw new Exception("该镜像被服务版本:" + serviceVersions + "使用,不能删除,请先删除服务版本。");
}

//判断权限,只有admin和创建者本身可以删除该数据集
LoginUser loginUser = SecurityUtils.getLoginUser();
String username = loginUser.getUsername();
@@ -302,7 +322,7 @@ public class ImageServiceImpl implements ImageService {
V1Pod pod = k8sClientUtil.getNSPodList(serviceNS, deploymentName);
if (pod == null) {
String podName = deploymentName + "-" + DateUtils.formatYMD10(new Date());
pod = k8sClientUtil.createPodWithEnv(podName, serviceNS, proxyUrl, mountPath, image);
pod = k8sClientUtil.createPodWithEnv(podName, serviceNS, mountPath, image);
}
String loginCmd = "docker login -u " + harborUser + " -p " + harborpassword + " " + harborUrl;
// 执行命令 docker login -u admin -p Harbor12345 172.20.32.187
@@ -345,7 +365,7 @@ public class ImageServiceImpl implements ImageService {
V1Pod pod = k8sClientUtil.getNSPodList(serviceNS, deploymentName);
if (pod == null) {
String podName = deploymentName + "-" + DateUtils.formatYMD10(new Date());
pod = k8sClientUtil.createPodWithEnv(podName, serviceNS, proxyUrl, mountPath, image);
pod = k8sClientUtil.createPodWithEnv(podName, serviceNS, mountPath, image);
}
String loginCmd = "docker login -u " + harborUser + " -p " + harborpassword + " " + harborUrl;
// 执行命令 docker login -u admin -p Harbor12345 172.20.32.187
@@ -435,13 +455,22 @@ public class ImageServiceImpl implements ImageService {
imageVersion.setUpdateTime(new Date());
imageVersion.setCreateTime(new Date());
imageVersion.setState(Constant.State_valid);
imageVersion.setStatus("available");
imageVersion.setStatus(Constant.Available);
imageVersionDao.insert(imageVersion);

//更新dev环境的镜像信息
DevEnvironment devEnvironment = new DevEnvironment();
devEnvironment.setId(imageVo.getDevEnvironmentId());
devEnvironment.setImage(resultMap.get("imageName"));


imageVo.setValue(resultMap.get("imageName"));
imageVo.setVersion(String.valueOf(imageVersion.getId()));

resultMap.put("id", String.valueOf(oldImage.getId()));
resultMap.put("version", String.valueOf(imageVersion.getId()));
resultMap.put("value",resultMap.get("imageName"));

devEnvironment.setImage(JacksonUtil.toJSONString(resultMap));
devEnvironmentDao.update(devEnvironment);
} catch (Exception e) {
throw new RuntimeException("保存镜像失败:" + e);


+ 26
- 7
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageVersionServiceImpl.java View File

@@ -3,12 +3,8 @@ package com.ruoyi.platform.service.impl;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.AssetWorkflow;
import com.ruoyi.platform.domain.ImageVersion;
import com.ruoyi.platform.domain.Ray;
import com.ruoyi.platform.mapper.AssetWorkflowDao;
import com.ruoyi.platform.mapper.ImageVersionDao;
import com.ruoyi.platform.mapper.RayDao;
import com.ruoyi.platform.domain.*;
import com.ruoyi.platform.mapper.*;
import com.ruoyi.platform.service.ImageVersionService;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.system.api.model.LoginUser;
@@ -38,7 +34,12 @@ public class ImageVersionServiceImpl implements ImageVersionService {
private AssetWorkflowDao assetWorkflowDao;
@Resource
private RayDao rayDao;

@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private DevEnvironmentDao devEnvironmentDao;
@Resource
private ServiceDao serviceDao;
/**
* 通过ID查询单条数据
*
@@ -96,6 +97,24 @@ public class ImageVersionServiceImpl implements ImageVersionService {
return GenericsAjaxResult.error("该镜像版本被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByImageId(JSON.toJSONString(map));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
return GenericsAjaxResult.error("该镜像版本被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByImageId(JSON.toJSONString(map));
if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
throw new Exception("该镜像版本被开发环境:" + devEnvironments + "使用,不能删除,请先删除开发环境。");
}

List<String> serviceVersionList = serviceDao.queryByImageId(JSON.toJSONString(map));
if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
throw new Exception("该镜像版本被服务版本:" + serviceVersions + "使用,不能删除,请先删除服务版本。");
}

//判断权限,只有admin和创建者本身可以删除该数据集
LoginUser loginUser = SecurityUtils.getLoginUser();
String username = loginUser.getUsername();


+ 43
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ModelsServiceImpl.java View File

@@ -68,7 +68,12 @@ public class ModelsServiceImpl implements ModelsService {
private RayDao rayDao;
@Resource
private ModelsVersionService modelsVersionService;

@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private DevEnvironmentDao devEnvironmentDao;
@Resource
private ServiceDao serviceDao;
@Resource
private ModelDependency1Dao modelDependency1Dao;

@@ -1167,6 +1172,24 @@ public class ModelsServiceImpl implements ModelsService {
throw new Exception("该模型被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByModelId(JSON.toJSONString(queryMap));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
throw new Exception("该模型被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByModelId(JSON.toJSONString(queryMap));
if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
throw new Exception("该模型被开发环境:" + devEnvironments + "使用,不能删除,请先删除开发环境。");
}

List<String> serviceVersionList = serviceDao.queryByModelId(JSON.toJSONString(queryMap));
if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
throw new Exception("该模型被服务版本:" + serviceVersions + "使用,不能删除,请先删除服务版本。");
}

String token = gitService.checkoutToken();
gitService.deleteProject(token, owner, identifier);
//删除模型依赖
@@ -1196,7 +1219,25 @@ public class ModelsServiceImpl implements ModelsService {
List<Ray> rayList = rayDao.queryByModelId(JSON.toJSONString(queryMap));
if (rayList != null && !rayList.isEmpty()) {
String rays = String.join(",", rayList.stream().map(Ray::getName).collect(Collectors.toSet()));
throw new Exception("该数据集版本被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
throw new Exception("该模型版本被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByModelId(JSON.toJSONString(queryMap));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
throw new Exception("该模型版本被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByModelId(JSON.toJSONString(queryMap));
if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
throw new Exception("该模型版本被开发环境:" + devEnvironments + "使用,不能删除,请先删除开发环境。");
}

List<String> serviceVersionList = serviceDao.queryByModelId(JSON.toJSONString(queryMap));
if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
throw new Exception("该模型版本被服务版本:" + serviceVersions + "使用,不能删除,请先删除服务版本。");
}

String token = gitService.checkoutToken();


+ 29
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/NewDatasetServiceImpl.java View File

@@ -4,9 +4,7 @@ import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.*;
import com.ruoyi.platform.mapper.AssetWorkflowDao;
import com.ruoyi.platform.mapper.AutoMlDao;
import com.ruoyi.platform.mapper.RayDao;
import com.ruoyi.platform.mapper.*;
import com.ruoyi.platform.service.DatasetTempStorageService;
import com.ruoyi.platform.service.GitService;
import com.ruoyi.platform.service.NewDatasetService;
@@ -56,6 +54,10 @@ public class NewDatasetServiceImpl implements NewDatasetService {
private AutoMlDao autoMlDao;
@Resource
private RayDao rayDao;
@Resource
private ActiveLearnDao activeLearnDao;
@Resource
private DevEnvironmentDao devEnvironmentDao;

@Value("${spring.redis.host}")
private String redisHost;
@@ -430,6 +432,18 @@ public class NewDatasetServiceImpl implements NewDatasetService {
throw new Exception("该数据集被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByDatasetId(JSON.toJSONString(map));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
throw new Exception("该数据集被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByDatasetId(JSON.toJSONString(map));
if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
throw new Exception("该数据集被开发环境:" + devEnvironments + "使用,不能删除,请先删除开发环境。");
}

String token = gitService.checkoutToken();
gitService.deleteProject(token, owner, repo);

@@ -461,6 +475,18 @@ public class NewDatasetServiceImpl implements NewDatasetService {
throw new Exception("该数据集版本被超参数自动寻优:" + rays + "使用,不能删除,请先删除超参数自动寻优。");
}

List<ActiveLearn> activeLearnList = activeLearnDao.queryByDatasetId(JSON.toJSONString(map));
if (activeLearnList != null && !activeLearnList.isEmpty()) {
String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
throw new Exception("该数据集版本被主动学习:" + activeLearns + "使用,不能删除,请先删除主动学习。");
}

List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByDatasetId(JSON.toJSONString(map));
if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
throw new Exception("该数据集版本被开发环境:" + devEnvironments + "使用,不能删除,请先删除开发环境。");
}

String token = gitService.checkoutToken();
String rootPath = Paths.get(localPathlocal + "/" + relativePath).getParent().toString();
gitService.deleteBranch(token, owner, repo, version, rootPath);


+ 5
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayInsServiceImpl.java View File

@@ -1,6 +1,5 @@
package com.ruoyi.platform.service.impl;

import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.platform.domain.Ray;
import com.ruoyi.platform.domain.RayIns;
import com.ruoyi.platform.mapper.RayDao;
@@ -8,6 +7,7 @@ import com.ruoyi.platform.mapper.RayInsDao;
import com.ruoyi.platform.service.RayInsService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.*;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -16,6 +16,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
@@ -69,6 +70,7 @@ public class RayInsServiceImpl implements RayInsService {
}

@Override
@Transactional
public String deleteById(Long id) {
RayIns rayIns = rayInsDao.queryById(id);
if (rayIns == null) {
@@ -84,6 +86,7 @@ public class RayInsServiceImpl implements RayInsService {
rayIns.setState(Constant.State_invalid);
int update = rayInsDao.update(rayIns);
if (update > 0) {
resourceOccupyService.deleteTaskState(Constant.TaskType_Ray, rayIns.getRayId(), id);
updateRayStatus(rayIns.getRayId());
return "删除成功";
} else {
@@ -264,7 +267,7 @@ public class RayInsServiceImpl implements RayInsService {
if (!StringUtils.equals(ins.getStatus(), Constant.Terminated)) {
ins.setStatus(StringUtils.isNotEmpty((String) status.get("phase")) ? (String) status.get("phase") : Constant.Pending);
}
if (StringUtils.equals(ins.getStatus(), "Error")) {
if (StringUtils.equals(ins.getStatus(), Constant.Error)) {
ins.setStatus(Constant.Failed);
}
return ins;


+ 6
- 4
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayServiceImpl.java View File

@@ -3,7 +3,6 @@ package com.ruoyi.platform.service.impl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.platform.domain.Ray;
import com.ruoyi.platform.domain.RayIns;
import com.ruoyi.platform.mapper.RayDao;
@@ -16,6 +15,7 @@ import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.RayParamVo;
import com.ruoyi.platform.vo.RayVo;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -24,6 +24,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
@@ -131,6 +132,7 @@ public class RayServiceImpl implements RayService {
}

@Override
@Transactional
public String delete(Long id) {
Ray ray = rayDao.getRayById(id);
if (ray == null) {
@@ -142,6 +144,7 @@ public class RayServiceImpl implements RayService {
throw new RuntimeException("无权限删除该实验");
}
ray.setState(Constant.State_invalid);
resourceOccupyService.deleteTaskState(Constant.TaskType_Ray, id, null);
return rayDao.edit(ray) > 0 ? "删除成功" : "删除失败";
}

@@ -156,12 +159,11 @@ public class RayServiceImpl implements RayService {
if (resourceOccupyService.haveResource(ray.getComputingResourceId(), 1)) {
RayParamVo rayParamVo = new RayParamVo();
BeanUtils.copyProperties(ray, rayParamVo);
rayParamVo.setComputingResourceId(ray.getComputingResourceId());
rayParamVo.setCodeConfig(JsonUtils.jsonToMap(ray.getCodeConfig()));
rayParamVo.setDataset(JsonUtils.jsonToMap(ray.getDataset()));
rayParamVo.setModel(JsonUtils.jsonToMap(ray.getModel()));
rayParamVo.setImage(JsonUtils.jsonToMap(ray.getImage()));
String param = JsonUtils.objectToJson(rayParamVo);
String param = JsonUtils.getConvertParam(rayParamVo);

// 调argo转换接口
try {
@@ -189,7 +191,7 @@ public class RayServiceImpl implements RayService {

// 插入记录到实验实例表
RayIns rayIns = new RayIns();
rayIns.setRayId(ray.getId());
rayIns.setRayId(id);
rayIns.setArgoInsNs((String) metadata.get("namespace"));
rayIns.setArgoInsName((String) metadata.get("name"));
rayIns.setParam(param);


+ 5
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ResourceOccupyServiceImpl.java View File

@@ -168,4 +168,9 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService {
resourceOccupy.setComputingResourceId(computingResourceId);
resourceOccupyDao.edit(resourceOccupy);
}

@Override
public void deleteTaskState(String taskType, Long taskId, Long taskInsId) {
resourceOccupyDao.deleteTaskState(taskType,taskId,taskInsId);
}
}

+ 15
- 6
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ServiceServiceImpl.java View File

@@ -14,6 +14,7 @@ import com.ruoyi.platform.service.ServiceService;
import com.ruoyi.platform.utils.ConvertUtil;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.serviceVos.ServiceCodeConfigVo;
import com.ruoyi.platform.vo.serviceVos.ServiceModelVo;
import com.ruoyi.platform.vo.serviceVos.ServiceVersionVo;
@@ -26,8 +27,10 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -64,7 +67,7 @@ public class ServiceServiceImpl implements ServiceService {
}

@Override
public Page<ServiceVersionVo> queryByPageServiceVersion(ServiceVersion serviceVersion, int page, int size) {
public Page<ServiceVersionVo> queryByPageServiceVersion(ServiceVersion serviceVersion, int page, int size) throws IOException {
PageRequest pageRequest;
if (StringUtils.isNotEmpty(serviceVersion.getRunState())) {
pageRequest = PageRequest.of(page, Integer.MAX_VALUE);
@@ -169,7 +172,7 @@ public class ServiceServiceImpl implements ServiceService {
}

@Override
public ServiceVersionVo getServiceVersion(Long id) {
public ServiceVersionVo getServiceVersion(Long id) throws IOException {
ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
ServiceVersionVo serviceVersionVo = getServiceVersionVo(serviceVersion);
com.ruoyi.platform.domain.Service service = serviceDao.getServiceById(serviceVersion.getServiceId());
@@ -195,7 +198,7 @@ public class ServiceServiceImpl implements ServiceService {
}

@Override
public Map<String, Object> serviceVersionCompare(Long id1, Long id2) throws IllegalAccessException {
public Map<String, Object> serviceVersionCompare(Long id1, Long id2) throws IllegalAccessException, IOException {
HashMap<String, Object> result = new HashMap<>();

ServiceVersion serviceVersion1 = serviceDao.getServiceVersionById(id1);
@@ -243,11 +246,13 @@ public class ServiceServiceImpl implements ServiceService {
throw new RuntimeException("该服务下还有版本,不能删除");
}

resourceOccupyService.deleteTaskState(Constant.TaskType_Service, id, null);
service.setState(Constant.State_invalid);
return serviceDao.updateService(service) > 0 ? "删除成功" : "删除失败";
}

@Override
@Transactional
public String deleteServiceVersion(Long id) {
ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
serviceVersion.setState(Constant.State_invalid);
@@ -260,6 +265,7 @@ public class ServiceServiceImpl implements ServiceService {
// if ((Integer) reqMap.get("code") == 200) {
// 结束扣积分
resourceOccupyService.endDeduce(Constant.TaskType_Service, null, id, null, null);
resourceOccupyService.deleteTaskState(Constant.TaskType_Service, serviceVersion.getServiceId(), id);
return serviceDao.updateServiceVersion(serviceVersion) > 0 ? "删除成功" : "删除失败";
// }
}
@@ -278,7 +284,8 @@ public class ServiceServiceImpl implements ServiceService {
paramMap.put("replicas", serviceVersion.getReplicas());
paramMap.put("env", JSONObject.parseObject(serviceVersion.getEnvVariables()));
paramMap.put("code_config", JSONObject.parseObject(serviceVersion.getCodeConfig()));
paramMap.put("image", serviceVersion.getImage());
String image = (String)JsonUtils.jsonToMap(serviceVersion.getImage()).get("value");
paramMap.put("image", image);
paramMap.put("model", JSONObject.parseObject(serviceVersion.getModel()));
paramMap.put("service_type", service.getServiceType());
paramMap.put("deploy_type", serviceVersion.getDeployType());
@@ -401,11 +408,12 @@ public class ServiceServiceImpl implements ServiceService {
serviceVersion.setModel(JSON.toJSONString(serviceVersionVo.getModel()));
serviceVersion.setCodeConfig(JSON.toJSONString(serviceVersionVo.getCodeConfig()));
serviceVersion.setEnvVariables(JSON.toJSONString(serviceVersionVo.getEnvVariables()));
serviceVersion.setImage(JacksonUtil.toJSONString(serviceVersionVo.getImage()));

return serviceVersion;
}

List<ServiceVersionVo> getServiceVersionVoList(List<ServiceVersion> serviceVersionList) {
List<ServiceVersionVo> getServiceVersionVoList(List<ServiceVersion> serviceVersionList) throws IOException {
List<ServiceVersionVo> result = new ArrayList<>();

for (ServiceVersion sv : serviceVersionList) {
@@ -415,7 +423,7 @@ public class ServiceServiceImpl implements ServiceService {
return result;
}

ServiceVersionVo getServiceVersionVo(ServiceVersion sv) {
ServiceVersionVo getServiceVersionVo(ServiceVersion sv) throws IOException {
ServiceVersionVo serviceVersionVo = new ServiceVersionVo();
BeanUtils.copyProperties(sv, serviceVersionVo);
ServiceModelVo serviceModelVo = JSON.parseObject(sv.getModel(), ServiceModelVo.class);
@@ -425,6 +433,7 @@ public class ServiceServiceImpl implements ServiceService {
serviceVersionVo.setCodeConfig(serviceCodeConfigVo);

serviceVersionVo.setEnvVariables(JacksonUtil.parseJSONStr2Map(sv.getEnvVariables()));
serviceVersionVo.setImage(JsonUtils.jsonToMap(sv.getImage()));
return serviceVersionVo;
}



+ 17
- 4
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/JsonUtils.java View File

@@ -1,11 +1,12 @@
package com.ruoyi.platform.utils;

import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONObject;
import com.ruoyi.common.security.utils.SecurityUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class JsonUtils {
@@ -33,9 +34,8 @@ public class JsonUtils {
}



// 将JSON字符串转换为扁平化的Map
public static Map<String, Object> flattenJson(String prefix, Map<String, Object> map) {
public static Map<String, Object> flattenJson(String prefix, Map<String, Object> map) {
Map<String, Object> flatMap = new HashMap<>();

for (Map.Entry<String, Object> entry : map.entrySet()) {
@@ -56,4 +56,17 @@ public class JsonUtils {
public static Map<String, Object> objectToMap(Object object) throws IOException {
return objectMapper.convertValue(object, Map.class);
}

public static String getConvertParam(Object object) throws JsonProcessingException {
HashMap<Object, Object> paramMap = new HashMap<>();
paramMap.put("data", JSON.parseObject(objectToJson(object), Map.class));

HashMap<Object, Object> userInfoMap = new HashMap<>();
userInfoMap.put("name", SecurityUtils.getLoginUser().getUsername());
userInfoMap.put("token", SecurityUtils.getLoginUser().getSysUser().getOriginPassword());
HashMap<Object, Object> extraInfoMap = new HashMap<>();
extraInfoMap.put("user_info", userInfoMap);
paramMap.put("extra_info", extraInfoMap);
return objectToJson(paramMap);
}
}

+ 124
- 65
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java View File

@@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

@@ -40,6 +41,8 @@ public class K8sClientUtil {
private String hostPath;
@Value("${dockerpush.proxyUrl}")
private String proxyUrl;
@Value("${proxy.useProxy:false}")
private boolean useProxy;

@Value("${git.localPath}")
String localPathlocal;
@@ -321,7 +324,6 @@ public class K8sClientUtil {
* @param port port
* @param mountPath 映射路径
* @param subPath pvc子路径
* @param pvcName 存储名
* @param image 镜像
* @return 创建成功的pod,的nodePort端口
*/
@@ -352,40 +354,68 @@ public class K8sClientUtil {

int lastIndex = hostPath.lastIndexOf('/');
String newPath = hostPath.substring(0, lastIndex);

V1Pod pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
.withLabels(selector)
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(image)
.withPorts(new V1ContainerPort().containerPort(port).protocol("TCP"))
.withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath).subPath(subPath))
.withNewSecurityContext().withNewPrivileged(true).endSecurityContext()
.addNewEnv()
.withName("HTTP_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("HTTPS_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("NO_PROXY")
.withValue("localhost,kubernetes.default.svc")
.endEnv()
.endContainer()
.addNewVolume()
.withName("workspace")
.withHostPath(new V1HostPathVolumeSource().path(newPath).type("DirectoryOrCreate"))
V1Pod pod;
if (useProxy) {
pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
.withLabels(selector)
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(image)
.withPorts(new V1ContainerPort().containerPort(port).protocol("TCP"))
.withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath).subPath(subPath))
.withNewSecurityContext().withNewPrivileged(true).endSecurityContext()
.addNewEnv()
.withName("HTTP_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("HTTPS_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("NO_PROXY")
.withValue("localhost,kubernetes.default.svc")
.endEnv()
.endContainer()
.addNewVolume()
.withName("workspace")
.withHostPath(new V1HostPathVolumeSource().path(newPath).type("DirectoryOrCreate"))
// .withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName))
.endVolume()
.withTerminationGracePeriodSeconds(14400L)
.endSpec()
.build();
.endVolume()
.withTerminationGracePeriodSeconds(14400L)
.endSpec()
.build();
} else {
pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
.withLabels(selector)
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(image)
.withPorts(new V1ContainerPort().containerPort(port).protocol("TCP"))
.withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath).subPath(subPath))
.withNewSecurityContext().withNewPrivileged(true).endSecurityContext()
.addNewEnv()
.withName("NO_PROXY")
.withValue("localhost,kubernetes.default.svc")
.endEnv()
.endContainer()
.addNewVolume()
.withName("workspace")
.withHostPath(new V1HostPathVolumeSource().path(newPath).type("DirectoryOrCreate"))
// .withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName))
.endVolume()
.withTerminationGracePeriodSeconds(14400L)
.endSpec()
.build();
}

try {
pod = api.createNamespacedPod(namespace, pod, null, null, null);
@@ -400,7 +430,7 @@ public class K8sClientUtil {
}

// 创建配置好的Pod
public Integer createConfiguredPod(String podName, String namespace, Integer port, String mountPath, V1PersistentVolumeClaim pvc, DevEnvironment devEnvironment, String dataPvcName, String datasetPath, String modelPath) {
public Integer createConfiguredPod(String podName, String namespace, Integer port, String mountPath, V1PersistentVolumeClaim pvc, DevEnvironment devEnvironment, String dataPvcName, String datasetPath, String modelPath) throws IOException {

//设置选择节点,pod反亲和性
Map<String, String> selector = new LinkedHashMap<>();
@@ -494,6 +524,8 @@ public class K8sClientUtil {
//配置资源
V1ResourceRequirements v1ResourceRequirements = setPodResource(devEnvironment.getComputingResourceId());

String image = (String) JsonUtils.jsonToMap(devEnvironment.getImage()).get("value");

V1Pod pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
@@ -502,7 +534,7 @@ public class K8sClientUtil {
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(devEnvironment.getImage())
.withImage(image)
.withPorts(new V1ContainerPort().containerPort(port).protocol("TCP"))
.withVolumeMounts(volumeMounts)
.withResources(v1ResourceRequirements)
@@ -641,7 +673,7 @@ public class K8sClientUtil {
}


public V1Pod createPodWithEnv(String podName, String namespace, String proxyUrl, String mountPath, String image) {
public V1Pod createPodWithEnv(String podName, String namespace, String mountPath, String image) {
CoreV1Api api = new CoreV1Api(apiClient);

V1SecurityContext v1SecurityContext = new V1SecurityContext();
@@ -653,38 +685,65 @@ public class K8sClientUtil {

List<V1Volume> volumes = new ArrayList<>();
volumes.add(new V1Volume().name("workspace").hostPath(new V1HostPathVolumeSource().path(hostPath + "/images").type("DirectoryOrCreate")));

V1Pod pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(image) // 替换为您实际要使用的镜像名称
.withSecurityContext(v1SecurityContext)
V1Pod pod;
if (useProxy) {
pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(image) // 替换为您实际要使用的镜像名称
.withSecurityContext(v1SecurityContext)
// .withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath))
.withVolumeMounts(volumeMounts)
.withNewSecurityContext().withNewPrivileged(true).endSecurityContext()
.addNewEnv()
.withName("HTTP_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("HTTPS_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("NO_PROXY")
.withValue("localhost,kubernetes.default.svc")
.endEnv()
.endContainer()
.withVolumeMounts(volumeMounts)
.withNewSecurityContext().withNewPrivileged(true).endSecurityContext()
.addNewEnv()
.withName("HTTP_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("HTTPS_PROXY")
.withValue(proxyUrl)
.endEnv()
.addNewEnv()
.withName("NO_PROXY")
.withValue("localhost,kubernetes.default.svc")
.endEnv()
.endContainer()
// .addNewVolume()
// .withName("workspace").withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName))
// .endVolume()
.withVolumes(volumes)
.endSpec()
.build();
.withVolumes(volumes)
.endSpec()
.build();
} else {
pod = new V1PodBuilder()
.withNewMetadata()
.withName(podName)
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(podName)
.withImage(image) // 替换为您实际要使用的镜像名称
.withSecurityContext(v1SecurityContext)
// .withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath))
.withVolumeMounts(volumeMounts)
.withNewSecurityContext().withNewPrivileged(true).endSecurityContext()
.addNewEnv()
.withName("NO_PROXY")
.withValue("localhost,kubernetes.default.svc")
.endEnv()
.endContainer()
// .addNewVolume()
// .withName("workspace").withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName))
// .endVolume()
.withVolumes(volumes)
.endSpec()
.build();
}

try {
pod = api.createNamespacedPod(namespace, pod, null, null, null);
} catch (ApiException e) {


+ 67
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ActiveLearnParamVo.java View File

@@ -0,0 +1,67 @@
package com.ruoyi.platform.vo;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.annotations.ApiModel;
import lombok.Data;

import java.util.Map;

@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(description = "主动学习参数")
public class ActiveLearnParamVo {
private Map<String,Object> codeConfig;

private Map<String,Object> dataset;

private Map<String,Object> image;

private Map<String,Object> model;

private String taskType;

private String frameworkType;

private String modelPy;

private String modelClassName;

private String classifierAlg;

private String regressorAlg;

private String datasetPy;

private String datasetClassName;

private Integer dataSize;

private Integer computingResourceId;

private Boolean shuffle;

private Integer trainSize;

private Integer initialNum;

private Integer queriesNum;

private Integer instancesNum;

private String queryStrategy;

private String lossPy;

private String lossClassName;

private Integer checkpointNum;

private Integer batchSize;

private Integer epochs;

private Float lr;
}

+ 114
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ActiveLearnVo.java View File

@@ -0,0 +1,114 @@
package com.ruoyi.platform.vo;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.Date;
import java.util.Map;

@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@ApiModel(description = "主动学习")
public class ActiveLearnVo {
private Long id;

@ApiModelProperty(value = "实验名称")
private String name;

@ApiModelProperty(value = "实验描述")
private String description;

@ApiModelProperty(value = "任务类型:classification或regression")
private String taskType;

@ApiModelProperty(value = "框架类型: sklearn, keras, pytorch")
private String frameworkType;

@ApiModelProperty(value = "代码")
private Map<String, Object> codeConfig;

@ApiModelProperty(value = "预训练的模型")
private Map<String, Object> model;

@ApiModelProperty(value = "模型文件路径")
private String modelPy;

@ApiModelProperty(value = "类名称")
private String modelClassName;

@ApiModelProperty(value = "分类算法")
private String classifierAlg;

@ApiModelProperty(value = "回归算法")
private String regressorAlg;

@ApiModelProperty(value = "dataset文件路径")
private String datasetPy;

@ApiModelProperty(value = "dataset类名")
private String datasetClassName;

@ApiModelProperty(value = "数据集文件路径")
private Map<String, Object> dataset;

@ApiModelProperty(value = "数据量")
private Integer dataSize;

@ApiModelProperty(value = "镜像")
private Map<String, Object> image;

@ApiModelProperty(value = "计算资源id")
private Integer computingResourceId;

@ApiModelProperty(value = "是否随机打乱")
private Boolean shuffle;

@ApiModelProperty(value = "训练集数据量")
private Integer trainSize;

@ApiModelProperty(value = "初始训练数据量")
private Integer initialNum;

@ApiModelProperty(value = "查询次数")
private Integer queriesNum;

@ApiModelProperty(value = "每次查询数据量")
private Integer instancesNum;

@ApiModelProperty(value = "查询策略:uncertainty_sampling, uncertainty_batch_sampling, max_std_sampling, expected_improvement, upper_confidence_bound")
private String queryStrategy;

@ApiModelProperty(value = "loss文件路径")
private String lossPy;

@ApiModelProperty(value = "loss类名")
private String lossClassName;

@ApiModelProperty(value = "多少轮查询保存一次模型参数")
private Integer checkpointNum;

@ApiModelProperty(value = "batch_size")
private Integer batchSize;

@ApiModelProperty(value = "epochs")
private Integer epochs;

@ApiModelProperty(value = "学习率")
private Float lr;

private String createBy;

private String updateBy;

private Date createTime;

private Date updateTime;

private Integer state;

@ApiModelProperty(value = "状态列表")
private String statusList;
}

+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/DevEnvironmentVo.java View File

@@ -34,7 +34,7 @@ public class DevEnvironmentVo implements Serializable {
/**
* 所用镜像
*/
private String image;
private Map<String,Object> image;
/**
* 对应数据集
*/


+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java View File

@@ -39,8 +39,8 @@ public class ImageVo implements Serializable {
/**
* 镜像推送地址
*/
// @ApiModelProperty(name = "url")
// private String url;
@ApiModelProperty(name = "value")
private String value;

/**
* 镜像tag名称


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/serviceVos/ServiceVersionVo.java View File

@@ -24,7 +24,7 @@ public class ServiceVersionVo {

private ServiceModelVo model;

private String image;
private Map<String, Object> image;

private String resource;



+ 187
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ActiveLearnDaoMapper.xml View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.platform.mapper.ActiveLearnDao">
<select id="count" resultType="java.lang.Long">
select count(1) from active_learn
<include refid="common_condition"></include>
</select>

<select id="queryByPage" resultType="com.ruoyi.platform.domain.ActiveLearn">
select * from active_learn
<include refid="common_condition"></include>
order by create_time desc limit #{pageable.offset}, #{pageable.pageSize}
</select>

<select id="getActiveLearnByName" resultType="com.ruoyi.platform.domain.ActiveLearn">
select *
from active_learn
where name = #{name}
and state = 1
</select>

<insert id="save" keyProperty="id" useGeneratedKeys="true">
insert into active_learn(name, description, task_type, framework_type,
code_config,
model, model_py, model_class_name,
classifier_alg, regressor_alg, dataset_py, dataset_class_name,
dataset, data_size, image, computing_resource_id, shuffle,
train_size, initial_num, queries_num, instances_num, query_strategy,
loss_py, loss_class_name, checkpoint_num, batch_size, epochs,
lr, create_by, update_by)
values (#{activeLearn.name}, #{activeLearn.description}, #{activeLearn.taskType}, #{activeLearn.frameworkType}, #{activeLearn.codeConfig},
#{activeLearn.model},
#{activeLearn.modelPy}, #{activeLearn.modelClassName}, #{activeLearn.classifierAlg},
#{activeLearn.regressorAlg},
#{activeLearn.datasetPy}, #{activeLearn.datasetClassName},
#{activeLearn.dataset}, #{activeLearn.dataSize},
#{activeLearn.image},
#{activeLearn.computingResourceId}, #{activeLearn.shuffle},
#{activeLearn.trainSize}, #{activeLearn.initialNum}, #{activeLearn.queriesNum}, #{activeLearn.instancesNum},
#{activeLearn.queryStrategy}, #{activeLearn.lossPy},
#{activeLearn.lossClassName}, #{activeLearn.checkpointNum}, #{activeLearn.batchSize},
#{activeLearn.epochs}, #{activeLearn.lr}, #{activeLearn.createBy}, #{activeLearn.updateBy})
</insert>

<update id="edit">
update active_learn
<set>
<if test="activeLearn.name != null and activeLearn.name !=''">
name = #{activeLearn.name},
</if>
<if test="activeLearn.description != null and activeLearn.description !=''">
description = #{activeLearn.description},
</if>
<if test="activeLearn.taskType != null and activeLearn.taskType !=''">
task_type = #{activeLearn.taskType},
</if>
<if test="activeLearn.frameworkType != null and activeLearn.frameworkType !=''">
framework_type = #{activeLearn.frameworkType},
</if>
<if test="activeLearn.codeConfig != null and activeLearn.codeConfig !=''">
code_config = #{activeLearn.codeConfig},
</if>
<if test="activeLearn.model != null and activeLearn.model !=''">
model = #{activeLearn.model},
</if>
<if test="activeLearn.modelPy != null and activeLearn.modelPy !=''">
model_py = #{activeLearn.modelPy},
</if>
<if test="activeLearn.modelClassName != null and activeLearn.modelClassName !=''">
model_class_name = #{activeLearn.modelClassName},
</if>
<if test="activeLearn.classifierAlg != null and activeLearn.classifierAlg !=''">
classifier_alg = #{activeLearn.classifierAlg},
</if>
<if test="activeLearn.regressorAlg != null and activeLearn.regressorAlg !=''">
regressor_alg = #{activeLearn.regressorAlg},
</if>
<if test="activeLearn.dataset != null and activeLearn.dataset !=''">
dataset = #{activeLearn.dataset},
</if>
<if test="activeLearn.datasetPy != null and activeLearn.datasetPy !=''">
dataset_py = #{activeLearn.datasetPy},
</if>
<if test="activeLearn.datasetClassName != null and activeLearn.datasetClassName !=''">
dataset_class_name = #{activeLearn.datasetClassName},
</if>
<if test="activeLearn.dataSize != null">
data_size = #{activeLearn.dataSize},
</if>
<if test="activeLearn.image != null and activeLearn.image !=''">
image = #{activeLearn.image},
</if>
<if test="activeLearn.computingResourceId != null">
computing_resource_id = #{activeLearn.computingResourceId},
</if>
<if test="activeLearn.shuffle != null">
shuffle = #{activeLearn.shuffle},
</if>
<if test="activeLearn.trainSize != null">
train_size = #{activeLearn.trainSize},
</if>
<if test="activeLearn.initialNum != null">
initial_num = #{activeLearn.initialNum},
</if>
<if test="activeLearn.queriesNum != null">
queries_num = #{activeLearn.queriesNum},
</if>
<if test="activeLearn.instancesNum != null">
instances_num = #{activeLearn.instancesNum},
</if>
<if test="activeLearn.queryStrategy != null and activeLearn.queryStrategy !=''">
query_strategy = #{activeLearn.queryStrategy},
</if>
<if test="activeLearn.lossPy != null and activeLearn.lossPy !=''">
loss_py = #{activeLearn.lossPy},
</if>
<if test="activeLearn.lossClassName != null and activeLearn.lossClassName !=''">
loss_class_name = #{activeLearn.lossClassName},
</if>
<if test="activeLearn.checkpointNum != null">
checkpoint_num = #{activeLearn.checkpointNum},
</if>
<if test="activeLearn.batchSize != null">
batch_size = #{activeLearn.batchSize},
</if>
<if test="activeLearn.epochs != null">
epochs = #{activeLearn.epochs},
</if>
<if test="activeLearn.lr != null">
lr = #{activeLearn.lr},
</if>
<if test="activeLearn.updateBy != null and activeLearn.updateBy !=''">
update_by = #{activeLearn.updateBy},
</if>
<if test="activeLearn.statusList != null and activeLearn.statusList !=''">
status_list = #{activeLearn.statusList},
</if>
<if test="activeLearn.state != null">
state = #{activeLearn.state},
</if>
</set>
where id = #{activeLearn.id}
</update>

<select id="getActiveLearnById" resultType="com.ruoyi.platform.domain.ActiveLearn">
select *
from active_learn
where id = #{id}
</select>

<select id="queryByDatasetId" resultType="com.ruoyi.platform.domain.ActiveLearn">
select *
from active_learn
where JSON_CONTAINS(dataset, #{datasetId})
and state = 1
</select>

<select id="queryByModelId" resultType="com.ruoyi.platform.domain.ActiveLearn">
select *
from active_learn
where JSON_CONTAINS(model, #{modelId})
and state = 1
</select>

<select id="queryByImageId" resultType="com.ruoyi.platform.domain.ActiveLearn">
select *
from active_learn
where JSON_CONTAINS(image, #{imageId})
and state = 1
</select>

<select id="queryByCodeConfig" resultType="com.ruoyi.platform.domain.ActiveLearn">
select *
from active_learn
where JSON_CONTAINS(code_config, #{codeConfig})
and state = 1
</select>

<sql id="common_condition">
<where>
state = 1
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
</where>
</sql>
</mapper>

+ 79
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ActiveLearnInsDaoMapper.xml View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.platform.mapper.ActiveLearnInsDao">
<select id="count" resultType="java.lang.Long">
select count(1)
from active_learn_ins
<where>
state = 1
and active_learn_id = #{activeLearnId}
</where>
</select>

<select id="queryAllByLimit" resultType="com.ruoyi.platform.domain.ActiveLearnIns">
select * from active_learn_ins
<where>
state = 1
and active_learn_id = #{activeLearnId}
</where>
order by update_time DESC
limit #{pageable.offset}, #{pageable.pageSize}
</select>

<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into active_learn_ins(active_learn_id, param, argo_ins_name, argo_ins_ns, node_status, node_result,
result_path, status)
values (#{activeLearnIns.activeLearnId}, #{activeLearnIns.param}, #{activeLearnIns.argoInsName},
#{activeLearnIns.argoInsNs},
#{activeLearnIns.nodeStatus}, #{activeLearnIns.nodeResult}, #{activeLearnIns.resultPath},
#{activeLearnIns.status})
</insert>

<update id="update">
update active_learn_ins
<set>
<if test="activeLearnIns.resultPath != null and activeLearnIns.resultPath != ''">
result_path = #{activeLearnIns.resultPath},
</if>
<if test="activeLearnIns.status != null and activeLearnIns.status != ''">
status = #{activeLearnIns.status},
</if>
<if test="activeLearnIns.nodeStatus != null and activeLearnIns.nodeStatus != ''">
node_status = #{activeLearnIns.nodeStatus},
</if>
<if test="activeLearnIns.nodeResult != null and activeLearnIns.nodeResult != ''">
node_result = #{activeLearnIns.nodeResult},
</if>
<if test="activeLearnIns.state != null">
state = #{activeLearnIns.state},
</if>
<if test="activeLearnIns.finishTime != null">
finish_time = #{activeLearnIns.finishTime},
</if>
</set>
where id = #{activeLearnIns.id}
</update>

<select id="queryById" resultType="com.ruoyi.platform.domain.ActiveLearnIns">
select * from active_learn_ins
<where>
state = 1 and id = #{id}
</where>
</select>

<select id="getByActiveLearnId" resultType="com.ruoyi.platform.domain.ActiveLearnIns">
select *
from active_learn_ins
where active_learn_id = #{activeLearnId}
and state = 1
order by update_time DESC limit 5
</select>

<select id="queryActiveLearnInsIsNotTerminated" resultType="com.ruoyi.platform.domain.ActiveLearnIns">
select *
from active_learn_ins
where (status NOT IN ('Terminated', 'Succeeded', 'Failed')
OR status IS NULL)
and state = 1
</select>
</mapper>

+ 1
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/AutoMlDao.xml View File

@@ -120,6 +120,7 @@
<select id="queryByPage" resultType="com.ruoyi.platform.domain.AutoMl">
select * from auto_ml
<include refid="common_condition"></include>
order by create_time desc limit #{pageable.offset}, #{pageable.pageSize}
</select>

<select id="getAutoMlById" resultType="com.ruoyi.platform.domain.AutoMl">


+ 27
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/DevEnvironmentDaoMapper.xml View File

@@ -208,11 +208,38 @@
where id = #{devEnvironment.id}
</update>

<select id="getByName" resultType="com.ruoyi.platform.domain.DevEnvironment">
select *
from dev_environment
where name = #{name}
and state = 1
</select>

<!--通过主键删除-->
<delete id="deleteById">
delete from dev_environment where id = #{id}
</delete>

<select id="queryByDatasetId" resultType="com.ruoyi.platform.domain.DevEnvironment">
select *
from dev_environment
where JSON_CONTAINS(dataset, #{datasetId})
and state = 1
</select>

<select id="queryByModelId" resultType="com.ruoyi.platform.domain.DevEnvironment">
select *
from dev_environment
where JSON_CONTAINS(model, #{modelId})
and state = 1
</select>

<select id="queryByImageId" resultType="com.ruoyi.platform.domain.DevEnvironment">
select *
from dev_environment
where JSON_CONTAINS(image, #{imageId})
and state = 1
</select>
</mapper>


+ 3
- 2
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/RayDaoMapper.xml View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.platform.mapper.RayDao">
<insert id="save">
<insert id="save" keyProperty="id" useGeneratedKeys="true">
insert into ray(name, description, dataset, model, code_config, main_py, num_samples, parameters,
points_to_evaluate, storage_path,
search_alg, scheduler, metric, mode, max_t,
@@ -94,11 +94,12 @@
<select id="queryByPage" resultType="com.ruoyi.platform.domain.Ray">
select * from ray
<include refid="common_condition"></include>
order by create_time desc limit #{pageable.offset}, #{pageable.pageSize}
</select>

<select id="getRayByName" resultType="com.ruoyi.platform.domain.Ray">
select *
from ray _
from ray
where name = #{name}
and state = 1
</select>


+ 11
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ResourceOccupy.xml View File

@@ -60,6 +60,17 @@
where id = #{id}
</update>

<update id="deleteTaskState">
update resource_occupy set task_state = 0
where task_type = #{taskType}
<if test="taskId != null and taskId !=''">
and task_id = #{taskId}
</if>
<if test="taskInsId != null and taskInsId !=''">
and task_ins_id = #{taskInsId}
</if>
</update>

<select id="haveResource" resultType="java.lang.Boolean">
select case when used + #{need} &lt;= total then TRUE else FALSE end
from resource


+ 30
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ServiceDaoMapper.xml View File

@@ -190,4 +190,34 @@
set run_state = #{runState}
where deployment_name = #{deploymentName}
</update>

<select id="queryByModelId" resultType="java.lang.String">
select concat(b.service_name, ':', a.version)
from service_version a,
service b
where JSON_CONTAINS(a.model, #{modelId})
and a.state = 1
and b.state = 1
and a.service_id = b.id
</select>

<select id="queryByImageId" resultType="java.lang.String">
select concat(b.service_name, ':', a.version)
from service_version a,
service b
where JSON_CONTAINS(a.image, #{imageId})
and a.state = 1
and b.state = 1
and a.service_id = b.id
</select>

<select id="queryByCodeConfig" resultType="java.lang.String">
select concat(b.service_name, ':', a.version)
from service_version a,
service b
where JSON_CONTAINS(a.code_config, #{codeConfig})
and a.state = 1
and b.state = 1
and a.service_id = b.id
</select>
</mapper>

Loading…
Cancel
Save