#4592 合入 #4531 ,模型License功能。

Merged
ychao_1983 merged 27 commits from zouap_static into V20230808 9 months ago
  1. +4
    -2
      models/ai_model_manage.go
  2. +1
    -1
      modules/modelappservice/modelsevice.go
  3. +1
    -1
      modules/storage/minio.go
  4. +5
    -1
      modules/storage/storage.go
  5. +2
    -0
      options/locale/locale_en-US.ini
  6. +2
    -0
      options/locale/locale_zh-CN.ini
  7. +23
    -4
      routers/repo/ai_model_manage.go
  8. +101
    -22
      routers/repo/ai_model_square.go
  9. +6
    -4
      templates/repo/datasets/index.tmpl
  10. +41
    -0
      templates/repo/modelmanage/create_online.tmpl
  11. +9
    -1
      web_src/vuepages/apis/modules/modelmanage.js
  12. +53
    -0
      web_src/vuepages/components/cloudbrain/details/ExportModel.vue
  13. +2
    -0
      web_src/vuepages/langs/config/en-US.js
  14. +2
    -0
      web_src/vuepages/langs/config/zh-CN.js
  15. +78
    -47
      web_src/vuepages/pages/modelmanage/intro/index.vue
  16. +55
    -5
      web_src/vuepages/pages/modelmanage/local/index.vue
  17. +54
    -5
      web_src/vuepages/pages/modelmanage/settings/index.vue

+ 4
- 2
models/ai_model_manage.go View File

@@ -56,6 +56,7 @@ type AiModelManage struct {
OnlineInfo []map[string]interface{} `xorm:"-" json:"onlineInfo"`
UsedCloudbrain []map[string]interface{} `xorm:"-" json:"usedCloudbrain"`
HasOnlineUrl int `xorm:"NOT NULL DEFAULT 0" json:"hasOnlineUrl"`
License string `xorm:"NULL" json:"license"`
}

type AiModelFile struct {
@@ -395,16 +396,17 @@ func ModifyModelCollectedNum(id string, collectedNum int) error {
return nil
}

func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool) error {
func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool, license string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("name", "label", "description", "engine", "is_private").Update(&AiModelManage{
re, err := sess.Cols("name", "label", "description", "engine", "is_private", "license").Update(&AiModelManage{
Description: description,
Name: name,
Label: label,
Engine: int64(engine),
IsPrivate: isPrivate,
License: license,
})
if err != nil {
return err


+ 1
- 1
modules/modelappservice/modelsevice.go View File

@@ -31,7 +31,7 @@ func ProducerOrder(modelApp *models.ModelApp) {

func GetWaitTime() int {
dvid := setting.BaiduWenXin.MODEL_SERVERS
return ((len(wenxinChannel) / dvid) + 1) * 15
return ((len(wenxinChannel) / dvid) + 1) * 30
}

func consumerOrder(in <-chan *models.ModelApp, url string) {


+ 1
- 1
modules/storage/minio.go View File

@@ -22,7 +22,7 @@ var (
)

const (
PresignedGetUrlExpireTime = time.Hour * 24 * 7
PresignedGetUrlExpireTime = time.Hour * 24 * 1
PresignedPutUrlExpireTime = time.Hour * 24 * 7
)



+ 5
- 1
modules/storage/storage.go View File

@@ -70,7 +70,11 @@ func Init() error {
m.BasePath,
m.UseSSL,
)
log.Info("minio storage inited.")
if err != nil {
log.Info("minio storage inited failed." + err.Error())
} else {
log.Info("minio storage inited succeed.")
}
MinioCore, err = minio.NewCore(m.Endpoint, m.AccessKeyID, m.SecretAccessKey, m.UseSSL)
if err != nil {
log.Error("init ScheduleMinioCore err.%v", err)


+ 2
- 0
options/locale/locale_en-US.ini View File

@@ -1345,6 +1345,8 @@ model.manage.engine=Model engine
model.manage.select.engine=Select model engine
model.manage.modelfile=Model file
model.manage.modellabel=Model label
model.manage.modellicense=License
model.manage.select.modellicense=Select license
model.manage.modeldesc=Model brief introduction
model.manage.modelaccess=Model Access
model.manage.modelaccess.public=Public


+ 2
- 0
options/locale/locale_zh-CN.ini View File

@@ -1358,6 +1358,8 @@ model.manage.engine=模型框架
model.manage.select.engine=选择模型框架
model.manage.modelfile=模型文件
model.manage.modellabel=模型标签
model.manage.modellicense=许可证
model.manage.select.modellicense=选择许可证
model.manage.modeldesc=模型简介
model.manage.modelaccess=模型权限
model.manage.modelaccess.public=公开


+ 23
- 4
routers/repo/ai_model_manage.go View File

@@ -87,6 +87,7 @@ func saveModelByParameters(aiTask *models.Cloudbrain, name string, version strin
aiTask.ContainerIp = ""
aiTaskJson, _ := json.Marshal(aiTask)
isPrivate := ctx.QueryBool("isPrivate")
license := ctx.Query("license")
model := &models.AiModelManage{
ID: id,
Version: version,
@@ -109,6 +110,7 @@ func saveModelByParameters(aiTask *models.Cloudbrain, name string, version strin
Status: STATUS_COPY_MODEL,
IsPrivate: isPrivate,
ComputeResource: aiTask.ComputeResource,
License: license,
}

err = models.SaveModelToDb(model)
@@ -278,6 +280,7 @@ func SaveLocalModel(ctx *context.Context) {
}
}
}
license := ctx.Query("license")
model := &models.AiModelManage{
ID: id,
Version: version,
@@ -299,6 +302,7 @@ func SaveLocalModel(ctx *context.Context) {
Status: STATUS_FINISHED,
IsPrivate: isPrivate,
ComputeResource: computeResource,
License: license,
}

err := models.SaveModelToDb(model)
@@ -540,7 +544,11 @@ func DeleteModelFile(ctx *context.Context) {
return
} else {
log.Info("delete obs file size is:" + fmt.Sprint(totalSize))
models.ModifyModelSize(id, model.Size-totalSize)
newSize := model.Size - totalSize
if newSize < 0 {
newSize = 0
}
models.ModifyModelSize(id, newSize)
modelFile := &models.AiModelFile{
Name: fileName,
ModelID: id,
@@ -1285,6 +1293,7 @@ func ModifyModelInfo(ctx *context.Context) {
description := ctx.Query("description")
engine := ctx.QueryInt("engine")
isPrivate := ctx.QueryBool("isPrivate")
license := ctx.Query("license")
aimodels := models.QueryModelByName(name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
if len(aimodels) == 1 {
@@ -1299,16 +1308,26 @@ func ModifyModelInfo(ctx *context.Context) {
return
}
}
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate)
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate, license)
if task.Name != name {
aimodels = models.QueryModelByName(task.Name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
for _, model := range aimodels {
models.ModifyLocalModel(model.ID, name, model.Label, model.Description, int(model.Engine), model.IsPrivate)
models.ModifyLocalModel(model.ID, name, model.Label, model.Description, int(model.Engine), model.IsPrivate, model.License)
}
}
}

if license != task.License {
mdContent := getMDContent(task)
if mdContent != "" {
if task.License != "" {
log.Info("start to remove license.")
mdContent = removeLicenseFromMD(mdContent, task.License)
}
mdContent = addLicenseMDToReadme(mdContent, license)
updateReadMeMd(task, mdContent)
}
}
if err != nil {
re["msg"] = err.Error()
ctx.JSON(200, re)


+ 101
- 22
routers/repo/ai_model_square.go View File

@@ -452,6 +452,10 @@ func ModifyModelReadMe(ctx *context.Context) {
if err == nil {
content := ctx.Query("content")
hasOnlineUrl := hasOnlineUrlInReadme(content)
log.Info(" model.License =")
if model.License != "" {
content = addLicenseMDToReadme(content, model.License)
}
path := Model_prefix + models.AttachmentRelativePath(id) + "/"
if model.Type == models.TypeCloudBrainTwo {
err = storage.PutStringToObs(setting.Bucket, path+README_FILE_NAME, content)
@@ -476,6 +480,79 @@ func ModifyModelReadMe(ctx *context.Context) {
}
}

func updateReadMeMd(model *models.AiModelManage, content string) {
path := Model_prefix + models.AttachmentRelativePath(model.ID) + "/"
if model.Type == models.TypeCloudBrainTwo {
log.Info("put model md file to obs.")
err := storage.PutStringToObs(setting.Bucket, path+README_FILE_NAME, content)
if err != nil {
log.Info("Failed to update readme file. as:" + err.Error())
}
}
}

func removeLicenseFromMD(content string, licenseId string) string {
lines := strings.Split(content, "\n")
licenseMap := getLicenseMap(licenseId)
if licenseMap == nil {
return content
}
newLines := make([]string, 0, len(lines))
for _, line := range lines {
if line == "## 许可证" || strings.HasPrefix(line, "["+licenseMap["name"]+"]") {
log.Info("line is equal. + line=" + line)
continue
}
newLines = append(newLines, line)
}
return strings.Join(newLines, "\n")
}

func getLicenseMap(licenseId string) map[string]string {
url := setting.RecommentRepoAddr + "model/license.json"
result, err := repository.RecommendContentFromPromote(url)
log.Info("license result=" + result)
remap := make([]map[string]string, 0)
if err == nil {
err = json.Unmarshal([]byte(result), &remap)
if err != nil {
log.Info("error=" + err.Error())
return nil
}
for _, licenseMap := range remap {
if licenseMap["id"] == licenseId {
return licenseMap
}
}
} else {
log.Info("error=" + err.Error())
}
return nil
}

func addLicenseMDToReadme(content string, licenseId string) string {
if hasLicense(content) {
return content
}
log.Info("start to add license to md file.")
licenseMap := getLicenseMap(licenseId)
if licenseMap == nil {
return content
}
content += "\n## 许可证\n"
content += "[" + licenseMap["name"] + "](" + licenseMap["linkUrl"] + ")\n"
return content
}

func hasLicense(content string) bool {
re := regexp.MustCompile(`## 许可证\s+(.+)`)
match := re.FindStringSubmatch(content)
if len(match) > 1 {
return true
}
return false
}

func hasOnlineUrlInReadme(content string) bool {
re := regexp.MustCompile(`在线体验地址\s+(.+)`)
match := re.FindStringSubmatch(content)
@@ -491,6 +568,26 @@ func hasOnlineUrlInReadme(content string) bool {
return false
}

func getMDContent(model *models.AiModelManage) string {
files := queryOneLevelModelFile(model, "")
var content []byte
for _, file := range files {
if strings.ToLower(file.FileName) == strings.ToLower(README_FILE_NAME) {
path := Model_prefix + models.AttachmentRelativePath(model.ID) + "/"
body, err := storage.ObsDownloadAFile(setting.Bucket, path+file.FileName)
if err != nil {
log.Info("download file failed: %s\n", err.Error())
break
} else {
defer body.Close()
content, err = ioutil.ReadAll(body)
return string(content)
}
}
}
return ""
}

func QueryModelReadMe(ctx *context.Context) {
id := ctx.Query("id")
model, err := models.QueryModelById(id)
@@ -498,31 +595,13 @@ func QueryModelReadMe(ctx *context.Context) {
"code": "-1",
}
if err == nil {
files := queryOneLevelModelFile(model, "")
find := false
var content []byte
for _, file := range files {
if strings.ToLower(file.FileName) == strings.ToLower(README_FILE_NAME) {
find = true
path := Model_prefix + models.AttachmentRelativePath(id) + "/"
body, err := storage.ObsDownloadAFile(setting.Bucket, path+file.FileName)
if err != nil {
log.Info("download file failed: %s\n", err.Error())
break
} else {
defer body.Close()
content, err = ioutil.ReadAll(body)
}
}
}
mdContent := getMDContent(model)
metas := map[string]string{"include_toc": "true"}
if find {
if mdContent != "" {
re["isExistMDFile"] = "true"
re["fileName"] = README_FILE_NAME
strc := string(content)
re["content"] = strc

re["htmlcontent"] = string(markdown.RenderRaw([]byte(strc), "", false, metas))
re["content"] = mdContent
re["htmlcontent"] = string(markdown.RenderRaw([]byte(mdContent), "", false, metas))
} else {
re["isExistMDFile"] = "false"
re["fileName"] = README_FILE_NAME


+ 6
- 4
templates/repo/datasets/index.tmpl View File

@@ -353,14 +353,15 @@
<span class="ui basic blue button" style="color: #fa8c16 !important;"
@click="setPrivate('{{.UUID}}',true,{{$k}})"
v-else="privates[{{$k}}]">{{$.i18n.Tr "dataset.set_private"}}</span>
{{end}}
{{end}}
{{ if $.IsSigned }}
<a class="ui basic blue button">
<el-dropdown size="medium">
<span class="el-dropdown-link">
{{$.i18n.Tr "repo.more"}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="copyUrl('{{.DownloadURL}}')">{{$.i18n.Tr "dataset.copy_url"}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="copyUrl('{{.S3DownloadURL}}')">{{$.i18n.Tr "dataset.copy_url"}}
</el-dropdown-item>
{{if and ($.CanWrite) (eq .DecompressState 1) }}
<el-dropdown-item @click.native="gotoAnnotate('{{$.RepoLink}}','{{.UUID}}',{{.Type}})">
@@ -376,7 +377,8 @@
{{end}}
</el-dropdown-menu>
</el-dropdown>
</a>
</a>
{{ end }}
</div>
</div>
</div>


+ 41
- 0
templates/repo/modelmanage/create_online.tmpl View File

@@ -126,6 +126,19 @@
<ul id="treeDemo" class="ztree"></ul>
</div>
</div>
</div>
<div class="inline fields">
<div class="two wide field right aligned">
<label for="license">{{.i18n.Tr "repo.model.manage.modellicense"}} &nbsp</label>
</div>
<div class="ui ten wide field dropdown selection" id="choice_License">
<input class="ays-ignore" type="hidden" id="license" name="license" required>
<div class="default text newtext">{{.i18n.Tr "repo.model.manage.select.modellicense"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-license">

</div>
</div>
</div>
<div class="inline fields">
<div class="two wide field right aligned">
@@ -298,6 +311,7 @@
$("#job-name").empty()
createModelName()
loadTrainList()
initLicense()

$(function () {
$('#choice_model').dropdown({
@@ -522,6 +536,33 @@
}
}

function initLicense() {
$('#choice_License').dropdown('clear')
$('#choice_License').dropdown({ clearable: true })
$("#job-license").empty()
$.get(`/dashboard/invitation?filename=model/license.json`, (data) => {
try {
const license = JSON.parse(data) || [];
let itemHtml = '';
for (let i = 0, iLen = license.length; i < iLen; i++) {
const item = license[i];
if (i == 0) {
itemHtml = itemHtml + '<option class="active item" data-value="' + item.id + '">' + item.name + '</option>';
} else {
itemHtml = itemHtml + '<option class="item" data-value="' + item.id + '">' + item.name + '</option>';
}
}
if (license.length) {
$('#choice_License .default.text').text(license[0].name);
$('#choice_License input[name="license"]').val(license[0].id)
$("#job-license").append(itemHtml);
}
} catch(err) {
console.log(err);
}
});
}

function check() {
let jobid = document.getElementById("jobId").value;
let versionname = document.getElementById("versionName").value;


+ 9
- 1
web_src/vuepages/apis/modules/modelmanage.js View File

@@ -1,6 +1,5 @@
import service from "../service";
import Qs from 'qs';
import { param } from "jquery";

// 保存本地模型
export const saveLocalModel = (data) => {
@@ -163,3 +162,12 @@ export const getModelEvolutionMap = (params) => {
params: { id: params.id },
});
}

// 获取平台模型许可证列表
export const getModelLicenseList = () => {
return service({
url: `/dashboard/invitation`,
method: 'get',
params: { filename: 'model/license.json' },
});
}

+ 53
- 0
web_src/vuepages/components/cloudbrain/details/ExportModel.vue View File

@@ -66,6 +66,20 @@
</el-popover>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.license') }}</label></div>
<div class="r-content">
<el-select style="width:100%;" size="medium" v-model="state.license" class="license-sel"
:placeholder="$t('modelManage.selectLicense')">
<template slot="prefix">
<i v-if="state.license" class="el-select__caret el-input__icon el-icon-close"
@click.stop.prevent="handleClearLicenseClick"></i>
</template>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.modelLabel') }}</label></div>
<div class="r-content">
@@ -106,6 +120,7 @@ import BaseDialog from '~/components/BaseDialog.vue';
import { MODEL_ENGINES } from '~/const';
import { getListValueWithKey } from '~/utils';
import { getAiTaskOutputResultAll, setAiTaskResultToModel } from '~/apis/modules/cloudbrain';
import { getModelLicenseList } from '~/apis/modules/modelmanage';

const MAX_LABEL_COUNT = 5;
const REPOISPRIVATE = window.REPO_IS_PRIVATE;
@@ -130,9 +145,11 @@ export default {
engine: '0',
filesStr: '',
label: '',
license: '',
description: '',
isPrivate: REPOISPRIVATE ? '1' : '0',
},
licenseList: [],
nameErr: false,
modelFileErr: false,
modelFilesData: [],
@@ -159,6 +176,9 @@ export default {
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
handleClearLicenseClick() {
this.state.license = '';
},
submit() {
this.state.name = this.state.name.trim();
if (!this.checkName()) {
@@ -186,6 +206,7 @@ export default {
engine_name: getListValueWithKey(MODEL_ENGINES, this.state.engine.toString()),
modelSelectedFile: this.state.filesStr,
label: this.state.label.split(/\s+/).join(' ').trim(),
license: this.state.license,
isPrivate: (this.repoIsPrivate || this.state.isPrivate == 1) ? true : false,
description: this.state.description,
}
@@ -293,6 +314,20 @@ export default {
console.log(err);
this.loading = false;
});
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
this.licenseList = license;
if (license.length) {
this.state.license = license[0].id;
}
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
},
closed() { },
},
@@ -391,6 +426,24 @@ export default {
}
}

.license-sel {
/deep/.el-input--prefix .el-input__inner {
padding-left: 15px;
}

/deep/.el-input__prefix {
position: absolute;
right: 0;

.el-icon-close {
position: absolute;
right: 24px;
color: rgba(0, 0, 0, .87);
font-weight: bold;
}
}
}

.input-disabled {
/deep/ .el-input__inner {
background-color: #f5f5f6 !important;


+ 2
- 0
web_src/vuepages/langs/config/en-US.js View File

@@ -257,6 +257,8 @@ const en = {
modelManage: 'Model management',
modelName: 'Model name',
useCluster: 'Available clusters',
license: 'License',
selectLicense: 'Select license',
local: 'Local',
online: 'Online',
createModel: 'Create Model',


+ 2
- 0
web_src/vuepages/langs/config/zh-CN.js View File

@@ -273,6 +273,8 @@ const zh = {
modelManage: '模型管理',
modelName: '模型名称',
useCluster: '可用集群',
license: '许可证',
selectLicense: '选择许可证',
local: '本地',
online: '线上',
createModel: '创建模型',


+ 78
- 47
web_src/vuepages/pages/modelmanage/intro/index.vue View File

@@ -4,8 +4,8 @@
<NotFound></NotFound>
</div>
<div v-else>
<ModelHeader :tab="'intro'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName" v-if="JSON.stringify(modelData)!=='{}'"
:model="modelData">
<ModelHeader :tab="'intro'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName"
v-if="JSON.stringify(modelData) !== '{}'" :model="modelData">
</ModelHeader>
<div class="ui container content">
<div class="content-l" v-loading="loading">
@@ -85,6 +85,13 @@
<span class="label add-btn" v-if="canEdit" @click="addLabel">{{ $t('modelManage.addLabels') }}</span>
</div>
<div class="summary">
<div class="row">
<div class="label">{{ $t('modelManage.license') }}:</div>
<div class="value">
<a v-if="licenseInfo.name" target="_blank" :href="licenseInfo.linkUrl">{{ licenseInfo.name }}</a>
<span v-else>--</span>
</div>
</div>
<div class="row">
<div class="label">{{ $t('modelManage.modelEngine') }}:</div>
<div class="value">{{ modelData.engineName }}</div>
@@ -116,34 +123,35 @@
<template v-if="onlineUrl">
<!-- <div class="divider-column-vertical"></div> -->
<div class="online-address">
<a :href="onlineUrl" target="_blank" style="width: 50%;background: rgb(255, 255, 255);">{{ $t('modelManage.onlineInference') }}</a>
<a :href="onlineUrl" target="_blank" style="width: 50%;background: rgb(255, 255, 255);">{{
$t('modelManage.onlineInference') }}</a>
</div>
</template>
<template v-if="onlineAddressList.length">
<!-- <div class="divider-column-vertical"></div> -->
<div class="summary" style="margin-left: 1rem;margin-top:1.5rem">
<div class="title">{{ $t('modelManage.otherOnline') }}:</div>
<div class="detail-address" v-for="item in onlineAddressList">
<div class="detail-address" v-for="(item, index) in onlineAddressList" :key="index">
<li class="nowrap">
<a :href="item.onlineInferUrl">{{item.ownerName}}/{{item.repoDisplayName}}</a>
<a :href="item.onlineInferUrl">{{ item.ownerName }}/{{ item.repoDisplayName }}</a>
</li>
</div>
</div>
</template>
</div>
<template v-if="trainUsedDataList.length">
<div class="divider-column-vertical"></div>
<div class="summary" style="margin-left: 1rem;">
<div class="title">{{ $t('modelManage.trainUsedDataList') }}:</div>
<div class="detail-address" v-for="item in trainUsedDataList">
<div class="detail-address" v-for="item in trainUsedDataList" :key="item.dataset_name">
<span style="display: flex;" v-if="!item.is_delete">
<i style="margin-right: 0.5rem;" class="ri-stack-line"></i>
<a class="nowrap" :href="item.repository_link" :title="item.dataset_name">{{item.dataset_name}}</a>
<a class="nowrap" :href="item.repository_link" :title="item.dataset_name">{{ item.dataset_name }}</a>
</span>
<span v-else style="display: flex;color:#888888" class="nowrap">
<span v-else style="display: flex;color:#888888" class="nowrap">
<i style="margin-right: 0.5rem;" class="ri-stack-line"></i>
<span style="width: 70%;" class="nowrap">{{item.dataset_name}}</span>
<span style="width: 70%;" class="nowrap">{{ item.dataset_name }}</span>
<span>{{ $t('modelManage.deleted') }}</span>
</span>
</div>
@@ -153,9 +161,9 @@
<div class="divider-column-vertical"></div>
<div class="summary" style="margin-left: 1rem;">
<div class="title">{{ $t('modelManage.modelUseTaskList') }}:</div>
<div class="detail-address" v-for="item in modelUseTaskList">
<div class="detail-address" v-for="item in modelUseTaskList" :key="item.jobName">
<li class="nowrap">
<a :href="item.url" :title="item.jobName">{{item.jobName}}</a>
<a :href="item.url" :title="item.jobName">{{ item.jobName }}</a>
</li>
</div>
</div>
@@ -170,7 +178,7 @@
import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import { getUrlSearchParams, setWebpackPublicPath, getListValueWithKey, transFileSize } from '~/utils';
import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce } from '~/apis/modules/modelmanage';
import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce, getModelLicenseList } from '~/apis/modules/modelmanage';
import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util';

@@ -184,6 +192,7 @@ export default {
repoName: location.pathname.split('/')[2],
loading: false,
modelData: {},
licenseInfo: {},
labels: [],
introFileName: '',

@@ -209,7 +218,7 @@ export default {
onlineAddressList: [],
trainUsedDataList: [],
modelUseTaskList: [],
onlineUrl:'',
onlineUrl: '',


};
@@ -217,7 +226,7 @@ export default {
components: { ModelHeader, NotFound },
methods: {
createIntro() {
this.content = this.emptyDefaultContent;
this.content = this.emptyDefaultContent;
this.toggleEdit(true);
},
changeEditTab(tab) {
@@ -309,7 +318,7 @@ export default {
this.content = data.content;
this.htmlContent = data.htmlcontent;
const match = data.content.match(/(#+)(.*)在线体验地址\s+(http[s]?:\/\/\S+)/g);
if(match){
if (match) {
this.onlineUrl = 'http' + match[0].split('http')[1]
}
}
@@ -360,52 +369,52 @@ export default {
this.submitLoading = false;
});
},
getFullUrlForType(data){
getFullUrlForType(data) {
const result = []
data.forEach(element => {
const detailObj = {jobName:element.displayJobName}
const detailObj = { jobName: element.displayJobName }
const prefixRepoLink = `${element.ownerName}/${element.repoName}`
if(element.type === 0){
if(element.jobType==='DEBUG'){
if (element.type === 0) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/${element.id}`
}else if(element.jobType==='TRAIN'){
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/train-job/${element.jobId}`
}else if(element.jobType==='INFERENCE'){
} else if (element.jobType === 'INFERENCE') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/inference-job/${element.jobId}`
}else if(element.jobType==='BENCHMARK' || element.jobType==='MODELSAFETY'){
} else if (element.jobType === 'BENCHMARK' || element.jobType === 'MODELSAFETY') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/benchmark/${element.id}`
}else{
} else {
}
}
if(element.type === 1){
if(element.jobType==='DEBUG'){
if (element.type === 1) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/modelarts/notebook/${element.id}`
}else if(element.jobType==='TRAIN'){
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/modelarts/train-job/${element.id}`
}else if(element.jobType==='INFERENCE'){
} else if (element.jobType === 'INFERENCE') {
detailObj.url = `/${prefixRepoLink}/modelarts/inference-job/${element.id}`
}else if(element.jobType==='BENCHMARK' || element.jobType==='MODELSAFETY'){
} else if (element.jobType === 'BENCHMARK' || element.jobType === 'MODELSAFETY') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/benchmark/${element.id}`
}else{
} else {
}

}
if(element.type === 2){
if(element.jobType==='DEBUG'){
if (element.type === 2) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/grampus/notebook/${element.id}`
}else if(element.jobType==='TRAIN'){
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/grampus/train-job/${element.jobId}`
}else if(element.jobType==='INFERENCE'){
} else if (element.jobType === 'INFERENCE') {

}else if(element.jobType==='BENCHMARK'){
} else if (element.jobType === 'BENCHMARK') {

}else{
} else {
detailObj.url = `/${prefixRepoLink}/grampus/onlineinfer/${element.id}`
}
}
result.push(detailObj)
});
return result
}
},
@@ -447,9 +456,23 @@ export default {
this.canEdit = model.isCanOper;
this.onlineAddressList = model.onlineInfo
this.trainUsedDataList = model.datasetInfo === null ? [] : model.datasetInfo
const modelUseTaskList = model.usedCloudbrain.length > 5 ? model.usedCloudbrain.slice(0,5) : model.usedCloudbrain
const modelUseTaskList = model.usedCloudbrain.length > 5 ? model.usedCloudbrain.slice(0, 5) : model.usedCloudbrain
this.modelUseTaskList = this.getFullUrlForType(modelUseTaskList)
this.getIntroInfo();
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
const matchLicense = license.filter(item => item.id == this.modelData.license);
if (matchLicense.length) {
this.licenseInfo = matchLicense[0];
}
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
} else {
this.emptyPage = true;
}
@@ -638,18 +661,21 @@ export default {
margin-bottom: 8px;
line-height: 20px;
}
.online-container{
background: linear-gradient(180deg, rgba(203,212,251,0.3) 0%,rgba(255,255,255,0) 100%);

.online-container {
background: linear-gradient(180deg, rgba(203, 212, 251, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
border-color: rgb(225, 227, 230);
border-width: 1px 0px 0px;
border-style: solid;
margin-top: 1.5rem;

.online-address {
width: 100%;
display: flex;
justify-content: center;
margin-top: 1.5rem;
a{

a {
width: 50%;
background: rgb(255, 255, 255);
color: rgb(22, 132, 252);
@@ -662,7 +688,7 @@ export default {
}
}
}
.labels {
margin-top: 12px;
margin-bottom: 12px;
@@ -711,24 +737,27 @@ export default {
border-radius: 2px;
font-size: 12px;
}
.divider-column-vertical {
background-color: rgb(243 244 246);
height: 1px;
margin-bottom: 1.25rem;
margin-top: 1.25rem;
}
.detail-address{

.detail-address {
margin: 0.5rem 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: rgba(5, 127, 255, 1);

>a {
color: rgba(5, 127, 255, 1);
}
}

.summary {
.row {
display: flex;
@@ -737,19 +766,21 @@ export default {
.label {
color: rgb(136, 136, 136)
}

.value {
color: rgb(16, 16, 16);
flex: 1;
}
}
.title{

.title {
height: 20px;
color: rgba(136, 136, 136, 1);
font-size: 14px;
margin-bottom: 1.2rem;
}
}
}
}
</style>

+ 55
- 5
web_src/vuepages/pages/modelmanage/local/index.vue View File

@@ -20,7 +20,7 @@
<div class="row" :class="nameErr ? 'error' : ''">
<div class="r-title"><label class="required">{{ $t('modelManage.modelName') }}</label></div>
<div class="r-content">
<el-input size="medium" :maxLength="25" v-model="state.name" @blur="checkName"
<el-input size="medium" style="width:100%;" :maxLength="64" v-model="state.name" @blur="checkName"
:placeholder="$t('modelManage.pleaseInputModelName')">
</el-input>
</div>
@@ -28,19 +28,33 @@
<div class="row" v-show="isShowVersion">
<div class="r-title"><label class="required">{{ $t('modelManage.version') }}</label></div>
<div class="r-content">
<el-input class="input-disabled" style="width:288px;" size="medium" v-model="state.version" readonly>
<el-input class="input-disabled" style="width:100%;" size="medium" v-model="state.version" readonly>
</el-input>
</div>
</div>
<div class="row">
<div class="r-title"><label class="required">{{ $t('modelManage.modelEngine') }}</label></div>
<div class="r-content">
<el-select style="width:288px;" size="medium" v-model="state.engine" placeholder="">
<el-select style="width:100%;" size="medium" v-model="state.engine" placeholder="">
<el-option v-for="item in engineList" :key="item.k" :label="item.v" :value="item.k">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.license') }}</label></div>
<div class="r-content">
<el-select style="width:100%;" size="medium" v-model="state.license" class="license-sel"
:placeholder="$t('modelManage.selectLicense')">
<template slot="prefix">
<i v-if="state.license" class="el-select__caret el-input__icon el-icon-close"
@click.stop.prevent="handleClearLicenseClick"></i>
</template>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.modelLabel') }}</label></div>
<div class="r-content">
@@ -48,7 +62,6 @@
:placeholder="$t('modelManage.modelLabelInputTips')" @input="labelInput"></el-input>
</div>
</div>

<div class="row" v-if="repoIsPrivate == false">
<div class="r-title"><label>{{ $t('modelManage.modelAccess') }}</label></div>
<div class="field">
@@ -92,7 +105,7 @@

<script>

import { saveLocalModel, getModelInfoByName, modifyModel } from '~/apis/modules/modelmanage';
import { saveLocalModel, getModelInfoByName, modifyModel, getModelLicenseList } from '~/apis/modules/modelmanage';
import { getUrlSearchParams } from '~/utils';
import { MODEL_ENGINES } from '~/const'

@@ -109,10 +122,12 @@ export default {
name: REPO_NAME + '_model_' + Math.random().toString(36).substr(2, 4),
version: '0.0.1',
engine: '0',
license: '',
label: '',
description: '',
isPrivate: false,
},
licenseList: [],
nameErr: false,
isShowVersion: false,
engineList: MODEL_ENGINES,
@@ -130,6 +145,9 @@ export default {
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
handleClearLicenseClick() {
this.state.license = '';
},
submit() {
this.state.name = this.state.name.trim();
if (!this.checkName()) {
@@ -231,6 +249,20 @@ export default {
this.cancel();
});
}
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
this.licenseList = license;
if (license.length) {
this.state.license = license[0].id;
}
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
},
beforeDestroy() { },
};
@@ -393,6 +425,24 @@ export default {
}
}

.license-sel {
/deep/.el-input--prefix .el-input__inner {
padding-left: 15px;
}

/deep/.el-input__prefix {
position: absolute;
right: 0;

.el-icon-close {
position: absolute;
right: 24px;
color: rgba(0, 0, 0, .87);
font-weight: bold;
}
}
}

.input-disabled {
/deep/ .el-input__inner {
background-color: #f5f5f6 !important;


+ 54
- 5
web_src/vuepages/pages/modelmanage/settings/index.vue View File

@@ -4,8 +4,8 @@
<NotFound></NotFound>
</div>
<div v-else>
<ModelHeader :tab="'settings'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName" v-if="JSON.stringify(modelData)!=='{}'"
:model="modelData">
<ModelHeader :tab="'settings'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName"
v-if="JSON.stringify(modelData) !== '{}'" :model="modelData">
</ModelHeader>
<div class="ui container content-wrap">
<div class="header">
@@ -24,19 +24,33 @@
<div class="row" v-show="isShowVersion">
<div class="r-title"><label class="required">{{ $t('modelManage.version') }}</label></div>
<div class="r-content">
<el-input class="input-disabled" style="width:288px;" size="medium" v-model="state.version" readonly>
<el-input class="input-disabled" style="width:100%;" size="medium" v-model="state.version" readonly>
</el-input>
</div>
</div>
<div class="row">
<div class="r-title"><label class="required">{{ $t('modelManage.modelEngine') }}</label></div>
<div class="r-content">
<el-select style="width:288px;" size="medium" v-model="state.engine" placeholder="">
<el-select style="width:100%;" size="medium" v-model="state.engine" placeholder="">
<el-option v-for="item in engineList" :key="item.k" :label="item.v" :value="item.k">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.license') }}</label></div>
<div class="r-content">
<el-select style="width:100%;" size="medium" v-model="state.license" class="license-sel"
:placeholder="$t('modelManage.selectLicense')">
<template slot="prefix">
<i v-if="state.license" class="el-select__caret el-input__icon el-icon-close"
@click.stop.prevent="handleClearLicenseClick"></i>
</template>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.modelLabel') }}</label></div>
<div class="r-content">
@@ -77,7 +91,7 @@
import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import { getUrlSearchParams } from '~/utils';
import { saveLocalModel, getModelInfoByName, modifyModel } from '~/apis/modules/modelmanage';
import { getModelInfoByName, modifyModel, getModelLicenseList } from '~/apis/modules/modelmanage';
import { MODEL_ENGINES } from '~/const';

const REPO_NAME = location.pathname.split('/')[2];
@@ -101,9 +115,11 @@ export default {
version: '0.0.1',
engine: '0',
label: '',
license: '',
description: '',
isPrivate: '1',
},
licenseList: [],
nameErr: false,
isShowVersion: false,
engineList: MODEL_ENGINES,
@@ -121,6 +137,9 @@ export default {
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
handleClearLicenseClick() {
this.state.license = '';
},
cancel() {
if (this.backUrl) {
window.location.href = this.backUrl;
@@ -193,8 +212,20 @@ export default {
this.state.version = data.version;
this.state.engine = data.engine.toString();
this.state.label = data.label;
this.state.license = data.license;
this.state.description = data.description;
this.state.isPrivate = data.isPrivate ? '1' : '0';
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
this.licenseList = license;
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
} else {
this.emptyPage = true;
}
@@ -330,6 +361,24 @@ export default {
}
}

.license-sel {
/deep/.el-input--prefix .el-input__inner {
padding-left: 15px;
}

/deep/.el-input__prefix {
position: absolute;
right: 0;

.el-icon-close {
position: absolute;
right: 24px;
color: rgba(0, 0, 0, .87);
font-weight: bold;
}
}
}

.input-disabled {
/deep/ .el-input__inner {
background-color: #f5f5f6 !important;


Loading…
Cancel
Save